Reputation: 431
I have the following database model:
A
aId
AB
aId
bId
B
bId
status
In a Spring data Specification, I want to return the instances of A when B.status is 'X'. The JPQL code is the following:
select a from A a where a in
(select ab.id.a from AB ab where ab.id.b.status= :status)
These are the model classes:
@Entity
public class A {
private Long aId;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.a")
private Set<AB> ab;
}
@Entity
public class B {
private Long bId;
private String Status;
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "id.b")
private Set<AB> ab;
}
@Entity
public class AB {
private ABPK id;
}
public class ABPK {
@ManyToOne
@JoinColumn(name="aId")
private A a;
@ManyToOne
@JoinColumn(name="bId")
private B b;
}
How would be the JPA Criteria in the Spring Specification?
public class ASpecifications {
public static Specification<A> test(final String status) {
return new Specification<Party>() {
@Override
public Predicate toPredicate(Root<A> a, CriteriaQuery<?> query, CriteriaBuilder cb) {
return null;
}
};
}
}
Upvotes: 2
Views: 9509
Reputation: 101
Select In - is an option but you could achieve same result with double join. And its easier to make such a jpa specification:
SELECT A.ID FROM A LEFT JOIN AB ON A.ID = AB.A_ID LEFT JOIN B ON AB.B_ID = B.ID WHERE B.STATUS = 'STATUS'
Method would look like this:
public static Specification<A> findB(String input) {
return new Specification<A>() {
@Override
public Predicate toPredicate(Root<A> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
Join<A,AB> AjoinAB = root.joinList(A_.AB_LIST,JoinType.LEFT);
Join<AB,B> ABjoinB = AjoinAB.join(AB_.B,JoinType.LEFT);
return cb.equal(ABjoinB.get(B_.NAME),input);
}
};
}
Or shorter
public static Specification<A> findB(String input) {
return (Specification<A>) (root, cq, cb) -> {
Join<A,AB> AjoinAB = root.joinList(A_.AB_LIST,JoinType.LEFT);
Join<AB,B> ABjoinB = AjoinAB.join(AB_.B,JoinType.LEFT);
return cb.equal(ABjoinB.get(B_.NAME),input);
};
}
I know it's been a long time since this question arose, but I came across it when I tried to do the same. This is my solution that works and I hope it helps someone.
Entities below
@Entity
public class A {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "a")
private List<AB> abList;
}
@Entity
public class B {
@Id
private Long id;
private String status;
@OneToMany(mappedBy = "b")
private List<AB> abList;
}
@Entity
public class AB {
@Id
private Long id;
private String name;
@ManyToOne
@JoinColumn(name = "a_id")
private A a;
@ManyToOne
@JoinColumn(name = "b_id")
private B b;
}
Upvotes: 0
Reputation: 431
The Specification that returns instances of A using Criteria API is the following:
public class ASpecifications {
public static Specification<A> test(final String status) {
return new Specification<Party>() {
@Override
public Predicate toPredicate(Root<A> a, CriteriaQuery<?> query, CriteriaBuilder cb) {
Subquery<A> sq = query.subquery(A.class);
Root<AB> ab = sq.from(AB.class);
sq.select(ab.get(AB_.id).get(ABPK_.a));
sq.where(cb.equal(ab.get(AB_.id).get(ABPK_.b).get(B_.status), status));
Predicate p = cb.in(a).value(sq);
return cb.and(p);
}
};
}
}
Upvotes: 3
Reputation: 6452
I suppose you wanted to select "A entities from AB entities where B is of provided status":
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<A> cq = cb.createQuery(A.class);
Root<AB> ab = cq.from(AB.class);
cq.select(ab.get("id").get("a"));
cq.where(cb.equal(ab.get("id").get("b.status"), status));
Upvotes: 0
Reputation: 1053
There are some good examples included on a previous post that address exactly what you're trying to accomplish here: jpa-2-0-criteria-api-subqueries-in-expressions.
Upvotes: 0