如何用谓词过滤子实体集合?

问题描述:

我有我需要过滤的孩子实体的集合,基于身份的名单上的实体服务。我的服务有一个公共方法,它接收父实体的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中的过滤器。但过滤器的用法在某种程度上很难理解!