当前位置: 首页 > 面试题库 >

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

秦宏盛
2023-03-14
问题内容

我一直想知道应该使用哪种类型的投影,所以我做了一点测试,涵盖了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以防您自己运行它。设置非常容易。自述文件包含您需要了解的所有内容。


问题答案:

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

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

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



 类似资料:
  • 有很多关于3D的opengl投影矩阵的很棒的教程,但我不是在做3D。我真的很难根据自己的喜好进行正交投影设置。 int宽=320;int高=480; 我使用这些设置创建了一个视图投影矩阵。 创建此视图和投影矩阵并将它们传递给gpu之后。 然后我创建一个从的四边形,以便它的原点位于中心。 然后我为四边形制作一个比例矩阵,这样我就可以在屏幕上看到它了。它呈现为一个完美的正方形,但当然glViewpor

  • MongoDB 中的投影即查询指定的字段,而不是直接查询文档的全部字段。比如说某个文档中有 5 个字段,而我们只需要其中的 3 个字段,那么就可以使用 MongoDB 中的投影来指定需要查询的 3 个字段。 在《 MongoDB查询文档》一节中我们介绍的 find() 方法,在使用 find() 方法时,如果不设置其中的第二个参数,那么在查询时将返回文档中的所有字段,想要限制要查询的字段,您就需要

  • 举个简单的例子来说明正交投影与透视投影照相机的区别。使用透视投影照相机获得的结果是类似人眼在真实世界中看到的有“近大远小”的效果(如下图中的(a));而使用正交投影照相机获得的结果就像我们在数学几何学课上老师教我们画的效果,对于在三维空间内平行的线,投影到二维空间中也一定是平行的(如下图中的(b))。 (a)透视投影,(b)正交投影 那么,你的程序需要正交投影还是透视投影的照相机呢? 一般说来,对

  • 泛型方法 在控制器中获取列表 测试 测试 结果[java.lang.ClassCastException] 为什么该类强制执行异常,因为criteria.SetProjection(pl)返回条件,然后返回相同列表的条件。 如何对此进行动态控制? 更新我!

  • 我有一个奇怪的问题,我不知道为什么会发生。我肯定我做错了什么,因为这是我第一次使用数据投影,而且我在使用DTOS时从来没有遇到过这样的问题。 我有一个SELECT statemen,它返回各种数据类型的某些列。我有一个接口,我将它传递给JPA存储库,这样它就可以进行接口映射。但是它不是根据列名映射结果(例如'accountnum'->),而是按照字母顺序映射列。因此,如果'date_of_orde

  • 当我尝试这个函数时,我得到一个错误。 CRSError:无效的投影: epsg: 4326:(内部程序错误:proj_create: SQLite错误对SELECT名称,类型,coordinate_system_auth_name,coordinate_system_code,datum_auth_name,datum_code,area_of_use_auth_name,area_of_use_