【问题标题】:Why are interface projections much slower than constructor projections and entity projections in Spring Data JPA with Hibernate?为什么接口投影比 Spring Data JPA with Hibernate 中的构造函数投影和实体投影慢得多?
【发布时间】:2019-04-20 04:27:09
【问题描述】:

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

1.实体投影

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

服务:

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。

结果:

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 跟踪这些对象的更改、延迟加载等。

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

界面预测结果非常缓慢。见问题。

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

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

问题:

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

编辑: 这是测试存储库:https://github.com/aurora-software-ks/spring-boot-projections-test,如果您想自己运行它。设置非常简单。自述文件包含您需要了解的所有内容。

【问题讨论】:

  • 所以你担心在一个可能需要大约 300 毫秒的方法调用中检索 100000 个对象需要 2.5 毫秒。还要确保您正在运行正确的测试(带有热身迭代等)。并单独运行每个测试(不是一个带有多个测试用例的测试,而是一个单独的测试,包括加载 JVM 等)。但由于它们是代理并且包裹在实体周围,我怀疑它们确实比实体慢。但总而言之,感觉就像是未成熟的优化。
  • 嘿,感谢您的评论。该测试是在热身迭代之后完成的。这不是一个单元测试,它是在完全启动项目后进行的,通过执行此调用几次来预热它,然后再次调用它来测试预测。结果几乎完全相同。它也不是大约 2.5 毫秒。在 100 次尝试中,一次迭代(投影 100 000 个对象)平均需要 252 毫秒。使用具有关系、多个调用和其他内容的真实业务逻辑可能会使它变得更慢。这个测试只是为了找出哪些更好。
  • 是否有机会提供这些测试?甚至可以作为拉取请求?
  • @JensSchauder 当然。我会在几个小时内完成,因为我现在不在家。我将创建一个公共回购,并在下一条评论中让您知道。感谢您对此感兴趣。
  • 别着急,我需要一些时间才能调查清楚。

标签: spring performance hibernate spring-data-jpa projection


【解决方案1】:

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

我与 Oliver Gierke(Spring Data 负责人)进行了交谈,他做了一些改进(这就是为什么你会得到如此“好”的结果 :-))但基本上,抽象与手动编码总是会有成本。

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

【讨论】:

  • 您好,感谢您的回答。我读过你的文章,是的,它看起来像同一个“问题”。无论如何,我宁愿更喜欢完全控制,或者只使用带有 1 个构造函数的动态投影,而不是这样的时间。所以这种行为或多或少是有意的,对吗?如果是这样,我认为应该在文档中提到这类预测是最慢的,甚至比检索实体还要慢,因为人们主要使用它来提高性能。
  • 我的意思是,使用更高级别的抽象并非没有成本,所以这是完全可以预期的。出于一个原因,我个人不会将其放入文档中,使用 Spring Data 的主要目的是快速入门并涵盖一般用例。只要您想获得最佳性能,就必须摆脱抽象。
猜你喜欢
  • 2021-06-01
  • 1970-01-01
  • 2021-06-19
  • 2018-08-06
  • 2020-01-10
  • 2018-11-25
  • 2018-03-05
  • 2021-10-29
  • 1970-01-01
相关资源
最近更新 更多