I have two entities, A
and B
. There is many-to-many relationship between them, where A
hold a list of B
. How to write a Specification to retrieve all A
entites that contains B
with specific name? Example:
@Service
public class YourService {
@Resource
private ARepository repository;
// I know how to do this type of queries with specifications
public List<A> getByB(B b) {
return repository.findAll(Specifications.containsB(b));
}
//Question: how to write Specification for this type of query?
public List<A> getByNameOfB(String name) {
return repository.findAll(Specifications.containsBWithName(name));
}
}
Entities:
@Entity
public class B {
@Id
@SequenceGenerator(sequenceName = "B_SEQ", name = "BSeq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "BSeq")
private Long id;
@Column(unique = true, updatable = false)
private String name;
}
@Entity
public class A {
@Id
@SequenceGenerator(sequenceName = "A_SEQ", name = "ASeq", allocationSize = 1)
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ASeq")
private Long id;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "A_B",
joinColumns = {@JoinColumn(name = "A_ID", nullable = false, updatable = false)},
inverseJoinColumns = {@JoinColumn(name = "B_ID", nullable = false, updatable = false)})
@Fetch(value = FetchMode.SUBSELECT)
private List<B> bList;
}
Metamodels:
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(A.class)
public class A_ {
public static volatile ListAttribute<A, B> bList;
}
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(B.class)
public class B_ {
public static volatile SingularAttribute<B, String> name;
}
Retrieval:
public interface ARepository extends JpaRepository<A, Long>, JpaSpecificationExecutor<A> {
}
public class Specifications {
public static Specification<A> containsB(B b) {
return (root, query, cb) -> {
Expression<List<B>> bList = root.get(A_.bList);
return cb.isMember(b, bList);
};
}
// HERE IS A QUESTION:
public static Specification<A> containsBWithName(String name) {
return (root, query, cb) -> {
ListJoin<List<B>> bList = root.join(A_.bList);
Expression<String> exp = bList.get(B_.name)
//TODO how to check that name is one of the retrieved names?
//PROBLEM, method below expects Expression<List<String>> instead of Expression<String>
cb.isMember(name, exp);
};
}
}
Try to do next:
public static Specification<A> containsBWithName(String name) {
return (root, query, cb) -> {
root.join("bList", JoinType.INNER);
return cb.equal(root.get("bList").get("name"), name);
};
}
Hope it will do the trick.
Spring Data version 1.11.4
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