Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/375.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 为什么在JPA中使用“select new”在POJO类中放置实体会导致N+1问题?_Java_Spring_Hibernate_Jpa - Fatal编程技术网

Java 为什么在JPA中使用“select new”在POJO类中放置实体会导致N+1问题?

Java 为什么在JPA中使用“select new”在POJO类中放置实体会导致N+1问题?,java,spring,hibernate,jpa,Java,Spring,Hibernate,Jpa,我拥有以下实体: @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "simple_entity") public class SimpleEntity { @Id private Long id; @Column(name = "text") private String text; } 我想从数据库中获取一些具有附加列的实体。 为此,我创建了一个简单

我拥有以下实体:

@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();

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

因此,我有一些问题:

为什么JPA是这样工作的? 有没有办法在JPQL中的一个查询中获取这些数据? 我应该如何获得实体以及一些额外的数据?我已经看到了返回Object[]的解决方案,但它看起来不太好?
这不是一个完整的答案,但我相信它会为讨论添加一些信息:

结果转换器

当您将问题标记为hibernate时,您可以使用5.2中不推荐使用的hibernate结果转换器,但6.0仍然使用Alpha:

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();
适当的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();
一般评论

我认为,在预测中对实体使用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
}

您使用什么hibernate版本?我相信默认策略是延迟加载,这会导致N+1。您可以将其设置为“急切”,并检查它是否未触发N个查询以获取其他数据。因为您应该选择所需的列,而不是完整的实体。显然,您在正在执行alookup或更高版本的构造函数中执行this.name=m.name。相反,您应该真正查询所需的列。另外,我强烈建议您不要编写自己的pojo,而是使用一个投影,Spring Data JPA随后将为该投影创建一个具有适当列的查询。@SternK Hibernate 5.4。12@Amit在我的情况下,如何将策略更改为懒惰?谢谢你的回答。事实上,如果使用了结果转换器,或者DTO中没有完整的实体,则不会发生此问题。因此,我还有一个问题。您将如何解决JPA中的以下问题:您有消息实体和类似的实体。您的任务是选择所有消息实体和喜欢的总数。您喜欢什么解决方案?您通常如何处理此类问题?在不了解您的应用程序和模型的情况下很难说,但一般来说:这似乎是某种报告,因此使用dto可能是合适的-在这种情况下,您确实不需要脏检查等。此外,如果需要提取所有消息,请思考分页是否合适?你能使用键集分页吗?它甚至不是一个报告,只是在帖子旁边显示喜欢的数量的附加信息。就我个人而言,我通常创建一个聚合此类数据的数据库视图,即:message_id+count like.id,但我想知道是否只使用JPQL和projection或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
}