为什么在JPA中使用“选择新”将实体放置在POJO类中会导致N + 1问题? [关闭]

问题描述 投票:5回答:1
我有以下实体:

@Getter @Setter @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "simple_entity") public class SimpleEntity { @Id private Long id; @Column(name = "text") private String text; }

我想从数据库中获取一些

带有附加列的实体。为此,我创建了一个简单的Pair类。

@Getter @Setter @AllArgsConstructor public class Pair<First, Second> { private First first; private Second second; }
然后我在JPQL中准备了一个查询,该查询创建了预期的结果。

@Repository public interface SimpleEntityRepository extends JpaRepository<SimpleEntity, Long> { @Query("SELECT new com.example.demo.Pair(m, false) FROM SimpleEntity m") List<Pair<SimpleEntity, Boolean>> getRecords(); }

查询返回正确的结果,但是其他查询存在问题。

wtf

因此,我有一些问题:

    为什么JPA这样工作?
  1. 有没有办法在JPQL的一个查询中获取此数据?
  2. 我应该如何获得实体以及一些其他数据(我已经看到了返回Object []的解决方案,但是看起来不太好)?
java spring hibernate jpa
1个回答
3
投票
不是完整的答案,但我相信它将为讨论添加一些信息:

结果转换器

当您将问题标记为休眠时,可以使用休眠结果转换器(在5.2中已弃用,但Alpha中仍为6.0:]

List<Pair<SimpleEntity, Boolean>> resultList = entityManager.createQuery( "select m as first, false as second from SimpleEntity m") .unwrap(org.hibernate.query.Query.class) .setResultTransformer(Transformers.aliasToBean(Pair.class)) .getResultList();

这假设Pair具有无参数构造函数并产生:

select simpleenti0_.id as col_0_0_, 0 as col_1_0_, simpleenti0_.id as id1_18_, simpleenti0_.text as text2_18_ from simple_entity simpleenti0_

正确的DTO

使用可以通过字段列表而非整个实体构造的类。当您指定选定字段的列表而不是整个实体时,查询将表现良好。

public class SimpleEntityDTO { private Long id; private String text; private Boolean second; public SimpleEntityDTO() { } public SimpleEntityDTO(Long id, String text, Boolean second) { this.id = id; this.text = text; this.second = second; } // getters and setters }

并修改您的查询:

@Query("SELECT new com.example.demo.SimpleEntityDTO(m.id, m.text, false) FROM SimpleEntity m") List<SimpleEntityDTO> getRecordsExtended();

这产生了:

select simpleenti0_.id as col_0_0_, simpleenti0_.text as col_1_0_, 0 as col_2_0_ from simple_entity simpleenti0_

一般性备注

我相信,对投影中的实体使用DTO的原因之一是,它们不受管理。在您的情况下,如果实体是DTO的一部分,则对实体进行管理:

@Transactional public void changeValueInFirstRecord() { List<Pair<SimpleEntity, Boolean>> all = simpleEntityRepository.getRecords(); SimpleEntity firstEntity = all.get(0).getFirst(); boolean managed = entityManager.contains(firstEntity); System.out.println("managed: " + managed); // true firstEntity.setText("new value"); // firstEntity is updated }

对于结果转换器也是如此:

@Transactional public void changeValueInFirstEntityViaTransformer() { List<Pair<SimpleEntity, Boolean>> all = entityManager.createQuery( "select m as first, false as second from SimpleEntity m") .unwrap(org.hibernate.query.Query.class) .setResultTransformer(Transformers.aliasToBean(Pair.class)) .getResultList(); SimpleEntity firstEntity = all.get(0).getFirst(); boolean managed = entityManager.contains(firstEntity); System.out.println("managed: " + managed); // true firstEntity.setText("new_value"); // firstEntity is updated }

© www.soinside.com 2019 - 2024. All rights reserved.