如何用谓词过滤子实体集合?
我有我需要过滤的孩子实体的集合,基于身份的名单上的实体服务。我的服务有一个公共方法,它接收父实体的ID和他的一些子实体的ID列表。如何用谓词过滤子实体集合?
默认情况下,我知道,JPA将获取所有相关实体,这他的实际行为。但是我们需要努力研究服务的性能。因此,我不想获取所有相关的实体,并用许多循环过滤它们(过滤id和日期属性等其他属性),我只想获得我的请求所关注的实体。
我父实体
@Entity
@Table(name = "MyParent")
public class MyParentEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyParent")
@SequenceGenerator(allocationSize = 1, name = "SEQ_MyParent",
sequenceName = "SEQ_MyParent")
@Column(name = "ID_PARENT")
private Long id;
@OneToMany(mappedBy = "myParent", cascade = CascadeType.ALL,
fetch = FetchType.EAGER, orphanRemoval = true)
private final List<MyChildEntity> myChild = new ArrayList<MyChildEntity>();
}
我的孩子实体
@Entity
@Table(name = "MyChild")
public class MyChildEntity {
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "SEQ_MyChild")
@SequenceGenerator(allocationSize = 1, name = "SEQ_MyChild",
sequenceName = "SEQ_MyChild")
@Column(name = "ID_CHILD")
private Long id;
@ManyToOne
@JoinColumn(name = "ID_PARENT")
private MyParentEntity myParent;
}
我使用Spring的数据CrudRepository摆脱我的数据库的数据,我也延伸JpaSpecificationExecutor使用谓语。
public interface MyParentRepository extends CrudRepository<MyParentEntity, Long>,
JpaSpecificationExecutor<MyParentEntity> {
}
这让我使用CrudRepository findOne()方法,但是使用Specification对象而不是常规的Long参数。
而且,我结合倍数规范的对象与下面的调用:
this.myParentRepository.findOne(Specifications
.where(firstSpecification(parentId))
.and(secondSpecification(childrenIdsList)));
我创建了一个父一个简单的JUnit测试与两个孩子的实体。在我的请求中,我能够使用提供的Id获取父实体。但即使我提供了孩子ID,我总是在父母的列表中找到两个孩子实体。
在我的方法,该方法返回一个新的规范对象,其中toPredicate方法是重写,我无法创建一个谓词,将筛选我的孩子们收集和只得到那些一个我很感兴趣。我知道Hibernate Criteria有可能添加“限制”,但这在toPredicate方法提供的CriteriaBuilder中不可用。
public static Specification<MyParentEntite> firstSpecification(final Long id) {
return new Specification<MyParentEntite>() {
@Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Predicate predicate = cb.equal(root.get(MyParentEntity_.id), id);
return cb.and(predicate);
}
};
}
public static Specification<MyParentEntite> secondSpecification(final List<Long> ids) {
return new Specification<MyParentEntite>() {
@Override
public Predicate toPredicate(Root<MyParentEntite> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
Root<MyChildEntity> child = query.from(MyChildEntity.class);
Expression<Long> exp = child.get(MyChildEntity_.id);
Predicate p = exp.in(ids);
return cb.and(p);
}
};
}
在secondSpecification()方法中,我也尝试在实体中直接使用ListJoin而不是Root。我在这里搜索了其他问题,但似乎这个问题是通过Hibernate Criteria限制或LeftJoin解决的,我在ListJoin中试图指定JoinType.LEFT参数。
这里是链接已经测试成功whitout解决方案:
JPA CriteriaBuilder - How to use "IN" comparison operator
JPA2 Criteria-API: select... in (select from where)
我想提一提,我与标准API和谓语相对较新。也许我错过了一些简单的东西,但这对于有经验的JPA开发者来说显而易见!
非常感谢您的帮助!
最后,我找到了一种方法来解决我的问题。只请求部分子实体集合是我们在数据完整性方面发现的危险。如果远程服务调用请求我的父实体在get中包含子实体的部分集合,则可能会返回此父实体对象以进行修改操作,这将导致在已删除的子实体实例上发生许多“删除”调用。持久性API会将这些失踪的孩子视为被删除的关系,这是我们不想要的。
我创建了一个虚拟转移对象,其中包含所请求的儿童实体的部分集合,因此此虚拟转移对象不能用于将来的修改操作调用。父实体的完整版本将用于“修改”目的。
您的JPA提供程序是否休眠?您是否考虑过可以过滤子实体而不是删除它们的hibernate中的过滤器。但过滤器的用法在某种程度上很难理解!