首页 文章

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

提问于
浏览
6

我有一个实体服务,我需要根据id的列表过滤子实体的集合 . 我的服务有一个公共方法,它接收父实体的id和他的一些子实体的id列表 .

默认情况下,我知道JPA将获取所有相关实体,这是他的实际行为 . 但我们需要研究服务的性能 . 因此,我没有获得所有相关实体并使用多个循环过滤它们(过滤id和其他属性,如date属性),我想只得到我的请求所关注的实体 .

我的父实体

@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方法被覆盖,我无法创建一个谓词,它将过滤我的子集合,只得到那些我感兴趣的 . 我知道Hibernate Criteria有可能添加“Restrictions”,但这在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开发人员来说是显而易见的!

非常感谢你的帮助!

2 回答

  • 2

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

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

  • 2

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

相关问题