为什么在带有 Hibernate 的 Spring Data JPA 中,接口投影比构造函数投影和实体投影慢得多? [英] Why are interface projections much slower than constructor projections and entity projections in Spring Data JPA with Hibernate?

查看:19
本文介绍了为什么在带有 Hibernate 的 Spring Data JPA 中,接口投影比构造函数投影和实体投影慢得多?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've been wondering which kind of projections should I use, so I did a little test, which covered 5 types of projections (based on docs: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections):

1.实体投影

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

This is just a standard findAll() provided by Spring Data repository. Nothing fancy here.

服务:

List<SampleEntity> projections = sampleRepository.findAll();

实体:

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

2.构造函数投影

服务:

List<NameOnlyDTO> projections = sampleRepository.findAllNameOnlyConstructorProjection();

存储库:

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

数据传输对象:

@NoArgsConstructor
@AllArgsConstructor
public class NameOnlyDTO {
    private String name;
}

3.界面投影

服务:

List<NameOnly> projections = sampleRepository.findAllNameOnlyBy();

存储库:

List<NameOnly> findAllNameOnlyBy();

界面:

public interface NameOnly {
    String getName();
}

4.元组投影

服务:

List<Tuple> projections = sampleRepository.findAllNameOnlyTupleProjection();

存储库:

@Query("select e.name as name from SampleEntity e")
List<Tuple> findAllNameOnlyTupleProjection();

5.动态投影

服务:

List<DynamicProjectionDTO> projections = sampleRepository.findAllBy(DynamicProjectionDTO.class);

存储库:

<T> List<T> findAllBy(Class<T> type);

数据传输对象:

public class DynamicProjectionDTO {

    private String name;

    public DynamicProjectionDTO(String name) {
        this.name = name;
    }
}


一些附加信息:

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

The project was built using gradle spring boot plugin (version 2.0.4), which uses Spring 5.0.8 under the hood. Database: H2 in memory.

结果:

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.
-----------------------------------------------------------------------

注意事项:

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

It is understandable that retrieving entities takes some time. Hibernate tracks these objects for changes, lazy loading and so on.

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

Constructor projections are really fast and have no limitations on the DTO side, but require manual object creation in @Query annotation.

结果表明界面投影真的很慢.看问题.

Interface projections turned out to be really slow. See question.

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

Tuple projections were the fastest, but are not the most convinient to play with. They need an alias in JPQL and the data has to be retrieved by calling .get("name") instead of .getName().

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

Dynamic projections look pretty cool and fast, but must have exactly one constructor. No more, no less. Otherwise Spring Data throws an exception, because it doesn't know which one to use (it takes constructor parameters to determine which data to retrieve from DB).

问题:

为什么界面投影比检索实体花费的时间更长?返回的每个界面投影实际上是一个代理.创建该代理是否如此昂贵?如果是这样,它是否违背了投影的主要目的(因为它们意味着比实体更快)?其他投影看起来很棒.我真的很想对此有所了解.谢谢.

Why interface projections take longer than retrieving entities? Each interface projection returned is actually a proxy. Is it so expensive to create that proxy? If so, doesn't it defeat the main purpose of projections (since they are meant to be faster than entities)? Other projections look awesome tho. I would really love some insight on this. Thank you.

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

EDIT : Here is the test repository: https://github.com/aurora-software-ks/spring-boot-projections-test in case you want to run it yourself. It is very easy to set up. Readme contains everything you need to know.

推荐答案

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

I experienced similar behavior with an older version of Spring Data and this was my take on it: https://blog.arnoldgalovics.com/how-much-projections-can-help/

我与 Oliver Gierke(Spring Data 负责人)进行了交谈,他进行了一些改进(这就是您获得如此好"结果的原因:-))但基本上,抽象与手动编码总是会有成本的.

I had a talk with Oliver Gierke (Spring Data lead) and he made some improvements (that's why you get so "good" results :-) ) but basically there will be always a cost on having abstractions vs coding it manually.

这是一种权衡,就像其他一切一样.一方面,您获得了灵活性、更轻松的开发、更少的维护(希望如此),另一方面您获得了完全控制权,查询模型有点丑陋.

This is a trade-off as everything else is. On one hand you got flexibility, easier development, less maintenance (hopefully), on the other hand you get full control, a bit uglier query model.

这篇关于为什么在带有 Hibernate 的 Spring Data JPA 中,接口投影比构造函数投影和实体投影慢得多?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆