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

Posted

技术标签:

【中文标题】如何使用谓词过滤子实体集合?【英文标题】:How to filter child entities collections with predicate? 【发布时间】:2014-10-26 23:02:56 【问题描述】:

我有一个实体服务,我需要根据 id 列表过滤子实体的集合。我的服务有一个公共方法,它接收父实体的 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-data CrudRepository 从我的数据库中获取数据,并且我还扩展了 JpaSpecificationExecutor 以使用 Predicate。

public interface MyParentRepository extends CrudRepository<MyParentEntity, Long>,
    JpaSpecificationExecutor<MyParentEntity> 

这让我可以使用 CrudRepository findOne() 方法,但使用 Specification 对象而不是常规的 Long 参数。

另外,我将多个规范的对象与以下调用结合起来:

this.myParentRepository.findOne(Specifications
    .where(firstSpecification(parentId))
    .and(secondSpecification(childrenIdsList)));

我创建了一个简单的 junit 测试,其中一个 Parent 链接到两个子实体。在我的请求中,我能够使用提供的 ID 获取父实体。但即使我提供了子 ID,我总是在父级列表中获得两个子级实体。

在我的方法中返回一个新的 Specification 对象,其中 toPredicate 方法被覆盖,我无法创建一个 Predicate 来过滤我的 children 集合并且只获取我感兴趣的那些。我知道 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() 方法中,我也尝试在Entity中直接使用ListJoin而不是Root。我在这里搜索了其他问题,但似乎通过 Hibernate Criteria 限制或 LeftJoin 解决了这个问题,我在 ListJoin 中指定了 JoinType.LEFT 参数。

以下是已测试但未成功的解决方案的链接:

JPA CriteriaBuilder - How to use "IN" comparison operator

JPA2 Criteria-API: select... in (select from where)

我想提一下,我对 Criteria API 和 Predicate 还比较陌生。也许我错过了一些简单的东西,但对于经验丰富的 JPA 开发人员来说这是显而易见的!

非常感谢您的帮助!

【问题讨论】:

【参考方案1】:

最后,我找到了解决问题的方法。仅请求子实体的部分集合是我们发现在数据完整性方面危险的事情。如果远程服务调用请求我的父实体在 get 中包含子实体的部分集合,则此父实体对象可能会返回以进行修改操作,这将导致对子实体的已删除实例的许多“删除”调用。持久性 API 会将这些丢失的子节点视为已删除的关系,这是我们不希望的。

我创建了一个虚拟 transfert 对象,其中包含请求的子实体的部分集合,因此这个虚拟 transfert 对象不能在以后的修改操作调用中使用。父实体的完整版本将用于“修改”目的。

【讨论】:

【参考方案2】:

您的 JPA 提供程序是否处于休眠状态?您是否考虑过休眠中的过滤器,它可以过滤子实体而不是删除它们。但是过滤器的使用在某种程度上很难理解!

【讨论】:

以上是关于如何使用谓词过滤子实体集合?的主要内容,如果未能解决你的问题,请参考以下文章

如何对实体的自定义属性进行谓词

swift Core Data谓词过滤给定父级的所有子数据

如何使用谓词过滤列表[重复]

如何使用实体框架获取记录计数匹配谓词

如何过滤嵌套集合实体框架对象?

NSPredicate的使用