【问题标题】:Hibernate Generating Unnecessary Queries When Using Nested Closed JPA Projections使用嵌套封闭 JPA 投影时 Hibernate 生成不必要的查询
【发布时间】:2020-08-27 08:28:16
【问题描述】:

我在使用 JPA/Hibernate 时遇到了一个奇怪的问题,希望能得到一些帮助。 我的环境:

  • OpenJDK 11.0.2 Spring Boot 2.2.2
  • spring-boot-starter-JPA(以上Spring Boot版本的默认版本)
  • 休眠 5.4.9
  • 玛丽亚 DB 2.3.0
  • Windows 10

我正在使用两个实体:

@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

}

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

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

并用这个Service方法调用上面的Repository方法:

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

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

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

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

我期望看到一个 Hibernate 生成的查询。

为什么 Hibernate 会分别运行两个查询来选择每个实体中的每个字段?

提前致谢!

【问题讨论】:

  • 注解Projection不需要了,可以试试去掉吗?
  • 谢谢,Lucsbelt。我已经尝试过使用和不使用 Projection 注释并得到相同的结果。
  • (换句话说,@Projection 注释似乎不会以某种方式影响问题)。
  • 当您单独查询 EmployeeFirstNameProjection 时也会发生这种情况?我在这里读到stackoverflow.com/a/53783270/3449039,当投影中有一个非原始字段时,所有列都包含在查询中。作为解决方法,您可以使用自定义 DTO 创建 JPQL 查询。
  • 很遗憾我没有意识到这一点

标签: hibernate jpa projection


【解决方案1】:

这是 Spring-Data Projections 的一个限制,是 Blaze-Persistence Entity Views 的完美用例。

Blaze-Persitence 是基于 JPA 的查询构建器,它支持 JPA 模型之上的许多高级 DBMS 功能。我在它之上创建了实体视图,以允许在 JPA 模型和自定义接口定义模型之间轻松映射,例如 Spring Data Projections on steroids。这个想法是您以您喜欢的方式定义您的目标结构,并通过 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 Data 集成允许您几乎像 Spring Data Projections 一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

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

【讨论】:

  • 谢谢,克里斯蒂安。我去看看!
【解决方案2】:

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

这里已经在https://stackoverflow.com/a/53783270/3449039 进行了解释,当时打开了一个 jira https://jira.spring.io/browse/DATAJPA-1218 并以“目前,我们不通过仅选择所需属性来优化引用实体的查询”的答案关闭。

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

【讨论】:

    猜你喜欢
    • 2018-04-19
    • 1970-01-01
    • 2022-12-02
    • 2018-12-16
    • 2021-01-08
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多