为什么接口预测比使用Hibernate的Spring Data JPA中的构造函数预测和实体预测慢得多?

Sik*_*kor 12 performance spring hibernate projection spring-data-jpa

我一直在想我应该使用哪种投影,所以我做了一点测试,其中包括5种类型的投影(基于文档:https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#project):

1.实体投射

这只是findAll()Spring Data存储库提供的标准.没什么好看的.

服务:

List<SampleEntity> projections = sampleRepository.findAll();
Run Code Online (Sandbox Code Playgroud)

实体:

@Entity
@Table(name = "SAMPLE_ENTITIES")
public class SampleEntity {
    @Id
    private Long id;
    private String name;
    private String city;
    private Integer age;
}
Run Code Online (Sandbox Code Playgroud)

2.构造函数投影

服务:

List<NameOnlyDTO> projections = sampleRepository.findAllNameOnlyConstructorProjection();
Run Code Online (Sandbox Code Playgroud)

库:

@Query("select new path.to.dto.NameOnlyDTO(e.name) from SampleEntity e")
List<NameOnlyDTO> findAllNameOnlyConstructorProjection();
Run Code Online (Sandbox Code Playgroud)

数据传输对象:

@NoArgsConstructor
@AllArgsConstructor
public class NameOnlyDTO {
    private String name;
}
Run Code Online (Sandbox Code Playgroud)

3.界面投影

服务:

List<NameOnly> projections = sampleRepository.findAllNameOnlyBy();
Run Code Online (Sandbox Code Playgroud)

库:

List<NameOnly> findAllNameOnlyBy();
Run Code Online (Sandbox Code Playgroud)

接口:

public interface NameOnly {
    String getName();
}
Run Code Online (Sandbox Code Playgroud)

4.元组投影

服务:

List<Tuple> projections = sampleRepository.findAllNameOnlyTupleProjection();
Run Code Online (Sandbox Code Playgroud)

库:

@Query("select e.name as name from SampleEntity e")
List<Tuple> findAllNameOnlyTupleProjection();
Run Code Online (Sandbox Code Playgroud)

5.动态投影

服务:

List<DynamicProjectionDTO> projections = sampleRepository.findAllBy(DynamicProjectionDTO.class);
Run Code Online (Sandbox Code Playgroud)

库:

<T> List<T> findAllBy(Class<T> type);
Run Code Online (Sandbox Code Playgroud)

数据传输对象:

public class DynamicProjectionDTO {

    private String name;

    public DynamicProjectionDTO(String name) {
        this.name = name;
    }
}
Run Code Online (Sandbox Code Playgroud)


一些额外的信息:

该项目是使用gradle spring boot插件(版本2.0.4)构建的,它使用了引擎盖下的Spring 5.0.8.数据库:内存中的H2.

结果:

Entity projections took 161.61 ms on average out of 100 iterations.
Constructor projections took 24.84 ms on average out of 100 iterations.
Interface projections took 252.26 ms on average out of 100 iterations.
Tuple projections took 21.41 ms on average out of 100 iterations.
Dynamic projections took 23.62 ms on average out of 100 iterations.
-----------------------------------------------------------------------
One iteration retrieved (from DB) and projected 100 000 objects.
-----------------------------------------------------------------------
Run Code Online (Sandbox Code Playgroud)

笔记:

检索实体需要一些时间是可以理解的.Hibernate跟踪这些对象的更改,延迟加载等.

构造函数投影非常快,并且对DTO方面没有限制,但需要在@Query注释中创建手动对象.

接口预测结果非常缓慢.看问题.

元组预测是最快的,但不是最方便的.他们需要JPQL中的别名,并且必须通过调用.get("name")来检索数据而不是.getName().

动态投影看起来很酷很快,但必须只有一个构造函数.不多也不少.否则Spring Data会抛出异常,因为它不知道使用哪一个(它需要构造函数参数来确定从DB检索哪些数据).

题:

为什么界面预测比检索实体需要更长的时间?返回的每个界面投影实际上都是代理.创建该代理是否如此昂贵?如果是这样,它是否会破坏预测的主要目的(因为它们意味着比实体更快)?其他投影看起来很棒.我真的很喜欢这方面的一些见解.谢谢.

编辑: 这是测试存储库:https://github.com/aurora-software-ks/spring-boot-projections-test,以防您想自己运行它.它很容易设置.自述文件包含您需要知道的所有内容.

gal*_*ics 5

我在较旧版本的Spring Data中遇到了类似的行为,这就是我的看法:https : //blog.arnoldgalovics.com/how-much-projections-can-help/

我与Spring Data负责人Oliver Gierke进行了一次交谈,他做了一些改进(这就是为什么您获得如此“好”结果的原因:-)),但是从根本上来说,抽象与手动编码总是有代价的。

这是其他所有事物之间的权衡。一方面,您获得了灵活性,更轻松的开发,更少的维护(希望如此),另一方面,您获得了完全的控制权,即查询模型更加难看。