一尘不染

为什么接口投影比带有Hibernate的Spring Data JPA中的构造函数投影和实体投影慢得多?

hibernate

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

1.实体投影

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

服务:

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以防您自己运行它。设置非常容易。自述文件包含您需要了解的所有内容。


阅读 254

收藏
2020-06-20

共1个答案

一尘不染

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

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

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

2020-06-20