So, I have the following entities:
@Entity
public class Supplier {
@Column(name = "SUPPLIERID")
private BigInteger supplierId;
@OneToMany
@JoinColumn(name = "ID_SUPP", foreignKey = @ForeignKey(name = "fk_POIS_SUPP"))
private List<POS> posList;
...
}
@Entity
public class POS {
@Column(name = "POSID")
private BigInteger posId
}
So, POS
does not have a reference to Supplier
, which means that we have a unidirectional one-to-many relationship. I need to look for a POS
by posId
and supplierId
. That is, find a supplier with the specified supplierId
and then find a pos in the supplier's list of pos's that has the specified posId. How do I write a criteria query for this?
I tried using subqueries. My idea was to create a subquery that would fetch all POS
's of a Supplier
with a given supplierId
. Then the main query would search within those POS
's for a POS
with the given posId
.
The problem was I couldn't write a query that would fetch a Supplier
s list of POS
s. Apparently you can't write a query of type List<POS>
:
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<POS> outerQuery = cb.createQuery(POS.class);
Root<POS> outerQueryRoot = outerQuery.from(POS.class);
Subquery<POS> subquery = outerQuery.subquery(POS.class);
Root<Supplier> subqueryRoot = subquery.from(Supplier.class);
subquery.where(cb.equal(subqueryRoot.get(Supplier_.supplierId), supplierId));
subquery.select(subqueryRoot.get(Supplier_.posList);
On this last line, I get a compilation error that Expression<POS> does not match Expression<List<POS>>
. And I can't change the type of the subquery because Java doesn't allow generic class literals (List<POS>.class
).
Any ideas?
I found very simple solution without subquery. Start from Suppler, join POS through posList and then 'select' POS.
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<POS> query = cb.createQuery(POS.class);
Root<Supplier> supplierRoot = query.from(Supplier.class);
ListJoin<Supplier, POS> posList = supplierRoot.joinList(Supplier_.posList);
query
.select(posList)
.where(
cb.equal(supplierRoot.get(Supplier_.suppliertId), supplierId),
cb.equal(posList.get(POS_.posId), posId)
);
With Hibernate 5.2.11 it generated nice query with two inner joins through N->M table very similar to manually written code ;-). Accepted answer is I guess wrong because it skips "posList" relation. It will select POS objects which are not in relation with specified Supplier.
You can do this with subqueries. SQL equivalent to the jpql "select p from POS p where p.id in (select sp.id from Supplier s join s.posList sp where s.id = :supplierId)"
See JPA2 Criteria-API: select... in (select from where)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With