我一直在想应该使用哪种投影,所以我做了一点测试,涵盖了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,如果您想自己运行它。设置非常容易。自述文件包含您需要了解的所有内容。
答案 0 :(得分:4)
我在较旧版本的Spring Data中遇到了类似的行为,这就是我的看法:https://blog.arnoldgalovics.com/how-much-projections-can-help/
我与Spring Data负责人Oliver Gierke进行了一次交谈,他进行了一些改进(这就是为什么您获得如此“好”结果的原因:-)),但是从根本上来说,抽象与手动编码总是有代价的。
这是其他一切之间的权衡。一方面,您具有灵活性,更轻松的开发,更少的维护(希望),另一方面,您可以完全控制,查询模型也更难看。