本文介绍了如何使用谓词过滤子实体集合?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have an entity service on which I need to filter a collection of child entity, based on a list of id's. My service have a public method which receive the id of the parent entity and a list of id's of some of his children entities.

默认情况下,我知道 JPA 将获取所有相关实体,这是他的实际行为.但是我们需要提高服务的性能.因此,我不想获取所有相关实体并使用许多循环过滤它们(过滤 id 以及日期属性等其他属性),我只想获取我的请求所涉及的实体.

By default, I know that JPA will fetch all related entities and this his the actual behavior. But we need to work on the performance of the service. So instead of getting all related entities and filter them with many loop (filter on id's and also on other properties like date property), I want to get only entities concerned by my request.

我的父实体

@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.

I'm using Spring-data CrudRepository to get data from my DB and I also extends JpaSpecificationExecutor to use Predicate.

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

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

This let me use CrudRepository findOne() method but with a Specification object instead of the regular Long parameter.

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

Also, I combine multiples Specification's object with the following call:

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

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

I created a simple junit test with one Parent linked to two children entities. In my request, I'm able to get the parent entity with the provided Id. But even if I provide the child id, I always get both children entities in the list inside the parent.

在我的方法中返回一个新的 Specification 对象,其中 toPredicate 方法被覆盖,我无法创建一个 Predicate 来过滤我的 children 集合并且只获取我感兴趣的那些.我知道 Hibernate Criteria 可以添加限制",但这在 toPredicate 方法提供的 CriteriaBuilder 中不可用.

In my method which return a new Specification object, in which the toPredicate method is override, I'm unable to create a Predicate that will filter my children collection and only get those one I'm interested. I know that the Hibernate Criteria has the possibility to add "Restrictions" but this is not available in the CriteriaBuilder that is provided with the toPredicate method.

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 参数.

In the secondSpecification() method, I also tried to use ListJoin instead of Root directly in the Entity. I searched in other questions here but it seems that this concern is solved with the Hibernate Criteria restrictions or with a LeftJoin, which I tried in my ListJoin in specifing JoinType.LEFT parameter.

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

Here are links to already tested solutions whitout success :

JPA CriteriaBuilder - 如何使用IN"比较运算符

JPA2 Criteria-API: select... in (select from哪里)

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

I want to mention that I'm relatively new with Criteria API and Predicate. Maybe I'm missing something that is simple but that is obvious to experienced JPA developpers!

非常感谢您的帮助!

推荐答案

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

Finally, I found a way to resolved my issue. Requesting only partial collections of sub-entities is something that we found dangerous in terms of data integrity. If a remote service calls to request my parent entity with a partial collection of children's entities within a get, this parent entity object may be return for a modify operation which will result in many "delete" calls on the removed instances of children entities. The persistence API will consider these missing children as relations that were removed, which is something we don't want.

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

I created a dummy transfert object which contains the partial collections of children's entities requested so this dummy transfert object can't not be use in a future modify operation call. The full version of the parent entity will be used for the "modify" purpose.

这篇关于如何使用谓词过滤子实体集合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 00:07