Hibernate 在使用嵌套封闭 JPA 投影时生成不必要的查询

Hibernate Generating Unnecessary Queries When Using Nested Closed JPA Projections

我 运行 遇到了 JPA/Hibernate 的奇怪问题,希望能得到一些帮助。 我的环境:

我正在使用两个实体:

@Entity
@Table(name = "\"accUser\"") 
public class User implements Serializable {

    private static final long serialVersionUID = -5750077342980986498L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "UserID")
    private Long id;

    // NOTE: Below is an embedded object!
    @OneToOne
    @JoinColumn(name = "EmpNum")
    private Employee employee;

    //...other fields are wrapped primitives omitted for brevity
}

@Entity
@Table(name = "\"hrmEmployee\"")
public class Employee implements Serializable {
    private static final long serialVersionUID = 5471137977607643256L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "EmpNum")
    private Long employeeNumber;

    @Column(name = "FirstName")
    private String firstName;

    //...other fields are wrapped primitives omitted for brevity

}

我定义了以下(已关闭)投影接口,每个都在它们自己的 class 文件中:

@Projection(types={ User.class })
interface UserProjection {
    Long getId();
    EmployeeFirstNameProjection getEmployee();
}

@Projection(types={ Employee.class })
interface EmployeeFirstNameProjection {
    String getFirstName();
}

我调用的是这个Repository接口查询方法:

<T> T findUserById(Long id, Class <T> type);

并使用此服务方法调用上述 Repository 方法:

public UserProjection getUser(Long id) {
    return userRepository.findUserById(id, UserProjection.class);
} 

所以在运行时,这里是单个返回的 UserProjection 的 JSON:

{"employee":{"firstName":"Matt"},"id":1796}

这正是我想要返回的。但是,当代码执行时,Hibernate 在两个查询中 selecting 两个实体中的所有字段,这是我不希望的。我使用 Projections 机制的全部原因是限制 JSON 的线路流量,并且出于性能原因希望希望保持较低的查询数量。

我希望看到单个 Hibernate 生成的查询。

为什么 Hibernate 运行 两个各自的查询 select 每个实体中的每个字段?

提前致谢!

这是 Spring-Data Projections 的限制,是 Blaze-Persistence Entity Views.

的完美用例

Blaze-Persitence 是基于 JPA 的查询构建器,它支持基于 JPA 模型的许多高级 DBMS 功能。我在它之上创建了实体视图,以允许在 JPA 模型和自定义接口定义的模型之间轻松映射,类似于 Spring 类固醇数据投影。这个想法是您按照自己喜欢的方式定义目标结构,并通过 JPQL 表达式将属性(getter)映射到实体模型。由于属性名称用作默认映射,因此您大多不需要显式映射,因为 80% 的用例都具有作为实体模型子集的 DTO。

您的模型的映射可能看起来像下面这样简单

@EntityView(User.class)
interface UserProjection {
    Long getId();
    EmployeeFirstNameProjection getEmployee();
}

@EntityView(Employee.class)
interface EmployeeFirstNameProjection {
    String getFirstName();
}

查询就是将实体视图应用于查询,最简单的就是通过 id 进行查询。

UserProjection dto = entityViewManager.find(entityManager, UserProjection.class, id);

但是 Spring 数据集成让您几乎可以像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

它只会获取您告诉它获取的映射

Hibernate 和 Spring-data 正常工作,当投影中存在非原始字段时,所有列都包含在查询中。

这里已经解释过 and a jira was opened at that moment https://jira.spring.io/browse/DATAJPA-1218 并以这个答案结束 "Currently, we don't optimize the query for referenced entities by only selecting the required properties"。

作为解决方法,您可以使用自定义 DTO 创建 JPQL 查询,或者尝试使用 @christian-beikov 提到的类似 Blaze-Persitence 的方法