仅选择实体集合的子集

Posted

技术标签:

【中文标题】仅选择实体集合的子集【英文标题】:Select only a subset of an Entities Collection 【发布时间】:2020-03-26 08:58:56 【问题描述】:

如果我有一个包含@OneToMany 的实体,使用 JPA 如何选择实体以及相关子项的子集?我不能使用@Where@Filter 注释。

更多详情 我正在将我们的商业模式翻译成更通用的东西,所以不要担心这个例子没有意义 IRL。但是查询有很多(比这个例子更多)left join fetch 案例。 Friend 没有关系,只有一个字符串名称。

@Entity
public class Parent 

    @Id
    @GeneratedValue
    private int parentId;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "friendId")
    private Friend friends;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "parent", cascade = CascadeType.ALL)
    private Set<Child> childrenSet = new HashSet<>();


@Entity
public class Child 

     @Id
     @GeneratedValue
     private int childId;

     private boolean blonde;

     @ManyToOne(fetchType.LAZY, cascade = CascadeType.ALL)
     private Parent parent;

@Query( "SELECT p " +
        "FROM Parent p " +
        "JOIN FETCH p.friends f " +
        "LEFT JOIN FETCH p.childrenSet c " + 
        "WHERE f.name IS NOT NULL " +
        "AND c.blonde = true")
List<Parent> getParentsWithListOfOnlyBlondeChildren();

测试类

@Transactional
@SpringBootTest
@RunWith(SpringRunner.class)
@DataJpaTest
public class TestParentRepo 
    @PersistenceContxt
    private EntityManager entityManager;

    @Autowired
    private ParentRepo parentRepo;

    @Before
    public void setup() 
        Child c1 = new Child();
        c1.setBlonde(true);
        Child c2 = new Child();
        c2.setBlonde(false);

        Friend friend1 = new Friend();
        friend1.setName("friend1");

        Set<Child> children = new HashSet<>();
        children.add(c1);
        children.add(c2);

        Parent parent1 = new Parent();
        parent1.setFriends(friend1);

        c1.setParent(parent1);
        c2.setParent(parent1);

        entityManager.persist(friend1);
        entityManager.persist(parent1);
        entityManager.persist(c1);
        entityManager.persist(c2);

        entityManager.flush();
        entityManager.clear();
    

    @Test
    public void runTest() 
        List<Parent> parent = parentRepo.getParentsWithListOfOnlyBlondeChildren();

        System.out.println(parent);
    

现在在调试时,我 GET 是集合中包含两个子对象的父对象。我想要是只有 c1 的父母(金发女郎 = true)。

查询必须是什么才能过滤掉不符合条件的相关子实体?

我试图避免这样做:查询父母,为每个父母查询孩子匹配标准。 编辑 经过更多测试后,我发现这仅在运行测试时不起作用,即问题在于在单元测试中使用 H2 DB 获得预期结果。使用实际 mysql 实例运行时,查询工作正常。

【问题讨论】:

【参考方案1】:

你不需要@Query(至少对于像这样的简单查询),如果你使用spring-data-jpa,你可以在你的存储库类中写一个这样的方法

List<Parent> findByChildrenSet_Blonde(boolean isBlonde)

spring-data 将通过查看方法名称为您制定和执行查询。 _是表示依赖类字段

现在你可以像这样调用这个函数了

findByChildrenSet_Blonde(true)

你也可以写

List<Parent> findByChildrenSet_BlondeTrue()

【讨论】:

这不起作用。它返回了一个包含 2 个父对象(同一个对象)的列表,它们都有两个孩子,blonde 和 !blonde。 那么数据有问题。检查您的数据。还可以在这里查看文档docs.spring.io/spring-data/jpa/docs/current/reference/html/…。我猜你在测试中漏掉了一行。试试这个parent1.setChildrenSet(children) 你是对的,有一些缺失,但它不在数据中,它在问题中。只有在 H2 DB 上运行测试时才会发生这种情况。当我使用实际的 MySQL 数据库完全运行它时,它会按预期工作。 不应该是这样的。上面的查询在 MySql 中对你有用吗? 是的,确实如此。当我设置一个测试端点来调用您建议的存储库方法时,插入完整的 MySQL 实例,它完全按照预期/想要的方式工作。所以我相信它是 H2(或者至少是我的配置)不能按照我们想要的方式工作。

以上是关于仅选择实体集合的子集的主要内容,如果未能解决你的问题,请参考以下文章

停止加载子集合,如果我更新父集合

JPA双向实体:在查询父实体时仅选择子实体的子集

Leetcode78. Subsets(求集合的子集问题)

Java递归遍历集合

核心数据 - 如何跟踪实体集的子集

等和的分隔子集(DP)