hql and #EmbeddedId - jpa

Is it possible to query on Embeddable object from query? Here are my Entities:
#Entity
#Table(name = "A")
public class UnitParam implements Serializable {
...
#EmbeddedId
private UnitParamId unitParamId;
....
}
#Embeddable
public class UnitParamId implements Serializable {
#Column(name = "PcID", nullable = false)
private short pcId;
#Column(name = "UnitID", nullable = false)
private short unitId;
#Column(name = "ParamID", nullable = false)
private int paramId;
...
}
#Entity
#Table(name = "B")
public class ParameterMapping extends BasicEntity {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns(value = {
#JoinColumn(name = "PcID", referencedColumnName = "PcID"),
#JoinColumn(name = "UnitID", referencedColumnName = "UnitID"),
#JoinColumn(name = "ParamID", referencedColumnName = "ParamID") })
private UnitParam unitParam;
...
}
Here is the query that fails:
select p.id, p.name as name,
p.unitParam.unitParamId.pcId as processCell,
p.unitParam.unitParamId.unitId as unit,
p.unitParam.unitParamId.paramId as paramId
from ParameterMapping p
With the Exception:Caused by: org.hibernate.QueryException: could not resolve property: unitParamId of: ParameterMapping
[SELECT p.id, p.name as name, p.unitParam.unitParamId.pcId as processCell, p.unitParam.unitParamId.unitParam.unitId as unit,
p.unitParam.unitParamId.paramId as paramId FROM de.koehl.mes.model.ParameterMapping p]
Thank you in advance.
I found the problem: First Problem was mixing field/property access. After fixing that, the ManyToOne generates the columns, but no foreign key! But I don't know why!!!!

There is no unitParam field in UnitParamId, so the path p.unitParam.unitParamId.unitParam.unitId is invalid. Change your query to
select p.id, p.name as name,
p.unitParam.unitParamId.pcId as processCell,
p.unitParam.unitParamId.unitId as unit,
p.unitParam.unitParamId.paramId as paramId
from ParameterMapping p
or even better:
select p.id, p.name as name,
unitParam.unitParamId.pcId as processCell,
unitParam.unitParamId.unitId as unit,
unitParam.unitParamId.paramId as paramId
from ParameterMapping p
inner join p.unitParam unitParam

Related

JPQL: How to join via #ElementCollection with #MapKeyJoinColumn

I have problems creating the correct JPQL query for joining through the following tables:
While between GROUPS and USERS there is a conventional #ManyToMany mapping table, DOCUMENTS_GROUPS is what causes the trouble. As you can see in the following entity, I want the relationship between DOCUMENTS and GROUPS to be mapped as a Map containing the access_mode (which works just fine except for the query):
#Entity
#Table(name = "DOCUMENTS")
#NamedQueries({
#NamedQuery(
name = "Documents.findAccessibleByUser",
query = "SELECT d FROM Document d INNER JOIN d.groups g INNER JOIN KEY(g).members m WHERE m.id = :userId"
)
})
public class Document {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ElementCollection
#CollectionTable(name = "DOCUMENTS_GROUPS", joinColumns = {#JoinColumn(name = "document_id")})
#MapKeyJoinColumn(name = "group_id")
#Column(name = "access_mode")
#Enumerated(EnumType.STRING)
private Map<Group, AccessMode> groups = new HashMap<>();
/* ... */
}
With Group being rather normal:
#Entity
#Table(name = "GROUPS")
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(length = 255)
private String name;
#ManyToMany
#JoinTable(name = "USERS_GROUPS", //
joinColumns = {#JoinColumn(name = "group_id")}, //
inverseJoinColumns = {#JoinColumn(name = "user_id")} //
)
private Set<User> members = new HashSet<>();
/* ... */
}
My question is now: How do I need to modify the second JOIN in my JPQL query?
SELECT d FROM Document d
INNER JOIN d.groups g
INNER JOIN KEY(g).members m
WHERE m.id = :userId
is syntactically wrong (unexpected KEY after INNER JOIN).
Of course, I have already tried a plain INNER JOIN g.members m, but since we're dealing with a Map<Group, AccessMode>, this fails with cannot dereference scalar collection element: members.
I was facing the same problem with a simple key-value Map<String, String> like:
#Entity Item.java
#ElementCollection
#MapKeyColumn(name = "name")
#Column(name = "value")
#CollectionTable(indexes = #Index(columnList = "value"))
private Map<String, String> attributes = new HashMap<>();
Joining the attributes was possible:
Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr");
but not querying fields:
Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr WHERE attr.value = 'something'");
I debugged the Hibernate internals and found out that the alias attr is already resolved to the value (e.attributes.value), so the only thing you can do here is:
Query query = em.createQuery("SELECT i FROM Item i INNER JOIN i.attributes attr WHERE attr = 'something'");
But I did not find any documentation or JPQL examples pointing that out. The behaviour is is useless in my case, because I want to have conditions for both key and value. Thats why I migrated to a foreign entity collection with key mapping and composite primary key. Its way more complicated but works as expected.
The composite key entity to prevent single primary keys
#Embeddable
public class ItemAttributeName implements Serializable {
private String name;
#ManyToOne
#JoinColumn(nullable = false)
private Item item;
// Empty default constructor is important
public ItemAttributeName() {
}
public ItemAttributeName(Item item, String name) {
this.item = article;
this.name = name;
}
}
The real attribute entity
#Entity
public class ItemAttribute {
#EmbeddedId
private ItemAttributeName id;
private String value;
// Empty default constructor is important
public ItemAttribute() {
}
public ItemAttribute(Item item, String name) {
this.id = new ItemAttributeName (item, name);
}
public String getValue() {
return value;
}
}
#Entity Item.java
#OneToMany(mappedBy = "id.item",cascade = CascadeType.PERSIST)
#MapKeyColumn(name = "name")
public Map<String, ItemAttribute> attributes = new HashMap<>();
Creating entities:
Item item = new Item ();
ItemAttribute fooAttribute = new ItemAttribute(item, "foo");
fooAttribute.setValue("356");
item.attributes.put("foo", fooAttribute);
Querying entities:
Query query = em.createQuery("SELECT i FROM Item i JOIN i.attributes attr WHERE attr.id.name = 'foo' AND attr.value='bar'");
List<Item> resultList = query.getResultList();
System.out.println(resultList.get(0).attributes.get("foo").getValue());
Prints out: bar

Criteria API query with non-direct relation

Good day!
There is SQL query that finely works:
select oi.nameshort from creditrequest c
join users u on u.id=c.user_id
join peoplemanagers pm on pm.people_id=u.people_id
-- join organiztion o on pm.organization_id=o.id
join organizationinfo oi on oi.organization_id=pm.organization_id
where oi.nameshort like 'Hydro%'
The problem arises when I try to translate SQL to JPA Criteria API. I need like restriction on table organizationinfo, but there is no direct reference from organization to it.
I try
pb.like(root.get(CreditRequestEntity_.userId)
.get(UsersEntity_.peopleId)
.get(PeopleEntity_.id)
.get(PeopleManagerEntity_.organizationId)
.get(OrganizationInfoEntity_.organizationId),
filter.getOrganization());
but it fails on .get(PeopleEntity_.id).
How to solve problem on Criteria API?
Classes:
#Entity
#Table(name = "creditrequest")
public class CreditRequestEntity {
...
#ManyToOne
#JoinColumn(name = "borrower_id")
private BorrowerEntity borrower;
...
}
#Entity
#Table(name = "borrower")
public class BorrowerEntity {
...
#ManyToOne
#JoinColumn(name = "organization_id")
private OrganizationEntity organizationId;
...
}
#Entity
#Table(name = "organization")
public class OrganizationEntity { ... }
#Entity
#Table(name = "organizationinfo")
public class OrganizationInfoEntity { ...
#Column(name = "nameshort")
private String nameShort;
#ManyToOne
#JoinColumn(name = "organization_id")
private OrganizationEntity organizationId;
...
}
Any class has field id as primary key.
if (filter.getOrganization() != null) {
List<Expression<String>> expressions = new ArrayList<>();
Subquery<OrganizationInfoEntity> psq = query.subquery(OrganizationInfoEntity.class);
Root<OrganizationInfoEntity> orgInfoRoot = psq.from(OrganizationInfoEntity.class);
psq.select(orgInfoRoot);
Join<OrganizationEntity, OrganizationInfoEntity> borrowerOrganizationInfoJoin = root.join(CreditRequestEntity_.borrower)
.join(BorrowerEntity_.organizationId).join(OrganizationEntity_.organizationInfo);
PredicateBuilder pipb = new PredicateBuilder(builder);
pb.add(builder.equal(borrowerOrganizationInfoJoin.get(OrganizationInfoEntity_.isActive), ActiveStatus.ACTIVE));
expressions.add(borrowerOrganizationInfoJoin.get(OrganizationInfoEntity_.nameShort));
pipb.like(filter.getOrganization(), expressions.toArray(new Expression[]{}));
psq.where(pipb.getWherePredicates());
pb.add(builder.exists(psq));
}

Using the Criteria API and Metamodel API to Join two Middle table always cross join

This is a RBAC module,There is three basic table user,role and permission and middle mapping table user_role and role_permission.
#Entity
#Table(name = "USER")
public class User implements Serializable {
#Id
private String userId;
...
}
#Entity
#Table(name = "ROLE")
public class Role implements Serializable {
#Id
private String roleId;
...
}
#Entity
#Table(name = "PERMISSION")
public class Permission implements Serializable {
#Id
private String permissionId;
...
}
#Entity
#Table(name = "USER_ROLE")
public class UserRole implements Serializable {
#Id
#GenericGenerator(name = "uuidGenerator", strategy = "uuid")
#GeneratedValue(generator = "uuidGenerator")
#Column(name = "ID")
private String id;
#ManyToOne
#JoinColumn(name = "USERID")
private User user;
#ManyToOne
#JoinColumn(name = "ROLEID")
private Role role;
...
}
#Entity
#Table(name = "ROLE_PERMISSION")
public class RolePermission implements Serializable {
#Id
private String id;
#ManyToOne
#JoinColumn(name = "PERMISSIONID")
private Permission permission;
#ManyToOne
#JoinColumn(name = "ROLEID")
private Role role;
...
}
and now i want to find all permission by user.id, SQL express like this:
select rp.* from Role_Permission rp,User_Role ur where ur.roleId = rp.roleId and ur.userId = :id
but by Criteria API:
public Predicate toPredicate(Root<RolePermission> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
List<Predicate> predicates = new ArrayList<Predicate>();
if (StringUtil.isNotEmpty(userId)) {
final Root<UserRole> userRoleRoot = criteriaQuery.from(UserRole.class);
Join<RolePermission,UserRole> join = root.join("role", JoinType.INNER);
Predicate predicate = criteriaBuilder.equal(join.get("roleId"), root.get("role").get("roleId"));
predicate = criteriaBuilder.and(predicate,criteriaBuilder.equal(userRoleRoot.<UserRole>get("user").get("id"), userId));
predicates.add(predicate);
}
return criteriaBuilder.and(predicates.toArray(new Predicate[]{}));
}
and query build result is:
select count(rolepermis0_.id) as col_0_0_ from role_permission rolepermis0_ inner join role role2_ on rolepermis0_.roleid=role2_.roleid cross join user_role userrole1_ where role2_.roleid=rolepermis0_.roleid and userrole1_.userid=?
but why here role_permission cross join user_role, how role_permission join user_role by criteria API ?
thanks a lot.
final Subquery<UserRole> userRoleSubquery = criteriaQuery.subquery(UserRole.class);
final Root<UserRole> userRole = userRoleSubquery.from(UserRole.class);
userRoleSubquery.select(userRole.<UserRole>get("id"));
userRoleSubquery.where(criteriaBuilder.equal(root.get("role").get("roleId"), userRole.get("role").get("roleId")), criteriaBuilder.equal(userRole.get("user").get("id"), userId));
Predicate predicate = criteriaBuilder.exists(userRoleSubquery);
predicates.add(predicate);

JPQL NamedQuery: Access attribute of an #Embeddable class from an #ElementCollection reference

The follwing named query
<named-query name="fix.getByProblem">
<query>
SELECT f
FROM Fix f JOIN f.solved s
WHERE s.id IN :ids
</query>
</named-query>
is supposed to return all fixes that solve at least one of the given problems, but fails with the error message
Exception Description: Error compiling the query [fix.getByProblem]:
SELECT f FROM Fix f JOIN f.solved s WHERE s.id IN :ids
], unknown state or association field [id] of class [ProblemHandle].
The model is as follows: (simplified)
Fix.java
#ElementCollection
#CollectionTable(name = "FIX_SOLVED", schema = SCHEMA_NAME, joinColumns = {#JoinColumn(name = "SOURCE_VERSION", referencedColumnName = "version")})
#AttributeOverrides({ #AttributeOverride(column = #Column(name = "SOLVED_ID", nullable = true), name = "id") })
private Collection<ProblemHandle> solved;
ProblemHandle.java
#Embeddable
#Access(AccessType.PROPERTY)
public class ProblemHandle {
private Long id;
...
}
Problem.java
#Entity(name = Problem.ENTITY_NAME)
#Access(value = AccessType.FIELD)
#Table(name = Problem.TABLE_NAME, schema = Problem.SCHEMA_NAME)
#IdClass(ProblemHandle.class)
public class Problem {
public static final String ENTITY_NAME = "problem";
public static final String SCHEMA_NAME = "X";
public static final String TABLE_NAME = "PROBLEM";
#Id
#Column(name="id", nullable = false)
private Long id;
...
}
How can I achieve that without having to change the pattern, e.g. using handles?
You have #Access(AccessType.PROPERTY), so the name of your attribute comes from your get method, not the variable. What is the name of your get method?
Try removing #Access(AccessType.PROPERTY)
Also, what version are you using? Try using the 2.4 release.

JPA query many to one association

I want to build the following pseudo query
Select a From APDU a where a.group.id= :id
group is a field in APDU class of the type APDUGroup.class.
I just want to get a list of APDUs based on APDUGroup's id.
How do i do that using a standard JPA query?
UPDATE
Yes, I have tried the above query and tried other variations for hours before posting in S/O. Here is the generated SQL for the query above:
SELECT t1.ID, t1.status, t1.type, t1.modified, t1.response, t1.expectedSize, t1.created, t1.description, t1.sequence, t1.name, t1.command, t1.recurring, t1.auth, t1.createdBy, t1.APDUGroup, t1.modifiedBy FROM APDUGroup t0, APDU t1 WHERE ((t0.ID = ?) AND (t0.ID = t1.APDUGroup))
The query looks okay but nothing get selected from my table.
There are at least 100 APDUs with APDUGroup = 1 in my test database.
I'm using eclipselink as the JPA provider.
Given the following Entities:
#Entity
public class APDU implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private APDUGroup group;
//...
}
#Entity
public class APDUGroup implements Serializable {
#Id
#GeneratedValue
private Long id;
//...
}
The following query will return a list of APDUs for a given APDUGroup id:
select a from APDU a where a.group.id = :id
Oh, wait, that's your query :)
Entity 1:
#Entity
#Getter
#Setter
#Table(name = "invoices")
public class Invoice implements Serializable {
#Id
#GeneratedValue
#Column(name = "invoice_id", updatable = false, nullable = false)
private Long invoiceId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id", referencedColumnName = "person_id", insertable = false, updatable = false, nullable = false)
private Person person;
//...
}
Entity 2:
#Entity
#Getter
#Setter
#Table(name = "people")
public class Person implements Serializable {
#Id
#GeneratedValue
#Column(name = "person_id", updatable = false, nullable = false)
private Long personId;
//...
}
Finally, Your Data Access Object (JPA Repository)
#Repository
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
#Query(value="SELECT x FROM Invoice x WHERE x.person.personId = :myPersonId")
List<Invoice> findInvoiceByPersonId (long myPersonId);
}
I hope this example has been helpful :)