我陷入了查询,我正在使用Spring Data JPA,JpaRepository和JpaSpecificationExecutor。所以我必须使用Predicate
中的CriteriaBuilder
。
目前我只是这样做:
Specifications<MyEntity> spec = Specifications.where(MySpec.isTrue());
List<MyEntity> myEntities = myRepository.findAll(spec);
MySpec.isTrue()是:
public static Specification<MyEntity> isTrue() {
return new Specification<MyEntity>() {
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
SetJoin<MyEntity, JoinEntity> j = root.join(MyEntity.joinEntities, JoinType.LEFT);
return cb.isTrue(j.get(JoinEntity_.attr));
}
};
}
所以这当然会导致这个SQL:
SELECT e.* FROM MyEntity e
LEFT OUTER JOIN JoinEntity j ON j.myEntityId = e.id
WHERE j.attr = true
但我只对MyEntity
的独特集合感兴趣。所以在JPQL中我会写:
SELECT DISTINCT(e) FROM MyEntity e
LEFT JOIN e.joinEntities j
WHERE j.attr = true
现在我的解决方案是:
List<MyEntity> myEntities = myRepository.findAll(spec);
Set<MyEntity> entitiesSet = new HashSet<MyEntity>(myEntities);
必须有更好的方式;)
如何使用CriteriaBuilder
(和JpaSpecificationExecutor
)?
第一个想法很简单:
public static Specification<MyEntity> isTrue() {
return new Specification<MyEntity>() {
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
SetJoin<MyEntity, JoinEntity> j = root.join(MyEntity.joinEntities, JoinType.LEFT);
query.distinct(true); // <<-- HERE
return cb.isTrue(j.get(JoinEntity_.attr));
}
};
}
这有效但有点破坏了那些小规格件的感觉。所以我想出了一个使用子查询的解决方案。这可能需要一些额外的时间,但对我而言,目前并不重要:
public static Specification<MyEntity> isTrue() {
return new Specification<MyEntity>() {
@Override
public Predicate toPredicate(Root<MyEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
Subquery<JoinEntity> subquery = query.subquery(JoinEntity.class);
Root<JoinEntity> subRoot = subquery.from(JoinEntity.class);
subquery.select(subRoot);
subquery.where(cb.isTrue(subRoot.get(JoinEntity_.attr)));
subquery.groupBy(subRoot.get(JoinEntity_.myEntity));
return cb.exists(subquery);
}
};
}