文章目录
- 示例代码放到最后,使用的是Springboot 项目
- 1. 简介
- 2. Hibernate抓取策略分类
- 2.1 即时加载(Eager Loading)
- 2.2 延迟加载(Lazy Loading)
- 2.3 子查询加载(Subselect Loading)
- 2.4 基于批处理的加载(Batch Loading)
- 3. 即时加载(Eager Loading)
- 3.1 概述
- 3.2 实体类映射配置
- 3.3 使用场景和注意事项
- 4. 延迟加载(Lazy Loading)
- 4.1 概述
- 4.2 实体类映射配置
- 4.3 使用场景和注意事项
- 5. 子查询加载(Subselect Loading)
- 5.1 概述
- 5.2 实体类映射配置
- 5.3 使用场景和注意事项
- 6. 基于批处理的加载(Batch Loading)
- 6.1 概述
- 6.2 实体类映射配置
- 6.3 使用场景和注意事项
- 7. 总结
- Demo
示例代码放到最后,使用的是Springboot 项目
1. 简介
本文将深入讨论Hibernate中的抓取策略,介绍不同类型的抓取策略以及它们的使用场景和注意事项。
2. Hibernate抓取策略分类
2.1 即时加载(Eager Loading)
即时加载是指在查询主实体时立即加载相关联的实体对象。这种策略会在查询时一次性加载所有关联的实体对象,可以减少数据库查询次数。
2.2 延迟加载(Lazy Loading)
延迟加载是指在访问关联属性时才会真正加载相关的实体对象。这种策略在查询主实体时只会加载主实体对象,而关联的实体对象只有在被访问时才会被加载。
2.3 子查询加载(Subselect Loading)
子查询加载是指通过执行子查询来加载关联的实体集合对象,而不是通过单独的SQL语句执行关联查询。这种策略适用于一对多或多对多关系,并可以提高性能。
2.4 基于批处理的加载(Batch Loading)
基于批处理的加载是指通过执行批量SQL语句一次性加载多个实体对象。这种策略可以减少数据库交互次数,提高性能。
3. 即时加载(Eager Loading)
3.1 概述
即时加载策略可以通过设置@ManyToOne
、@OneToOne
等注解来实现,默认情况下Hibernate会使用即时加载进行关联对象的加载。
3.2 实体类映射配置
在关联属性上使用fetch = FetchType.EAGER
注解来指定即时加载策略。
@Entity
@Data
public class Classes {@Idprivate int id;private String name;@OneToMany(mappedBy = "classes", fetch = FetchType.EAGER)private List<Student> students;
}
这里可以看到查询的班级实体种student属性集合有二十个对象
3.3 使用场景和注意事项
- 适用于关联实体对象数据量较小且经常被使用的情况。
- 频繁加载大量关联对象可能导致性能问题,需要谨慎使用。
4. 延迟加载(Lazy Loading)
4.1 概述
延迟加载策略可以通过设置@ManyToOne
、@OneToOne
等注解来实现,默认情况下Hibernate会使用延迟加载进行关联对象的加载。
4.2 实体类映射配置
在关联属性上使用fetch = FetchType.LAZY
注解来指定延迟加载策略。
@Entity
public class Classes{// ...@OneToMany(mappedBy = "classes", fetch = FetchType.LAZY)private List<Student> students;// ...
}
这里开启懒加载后查询到的对象种数据是空的,只有当执行get方法时才会去获取数据
4.3 使用场景和注意事项
- 适用于关联实体对象数据量较大或者不经常被使用的情况,可以减少不必要的数据库查询。
- 注意检查在延迟加载策略下可能引发的懒加载异常,需要合理处理。
5. 子查询加载(Subselect Loading)
5.1 概述
子查询加载策略通过执行子查询来加载关联的实体集合对象,适用于一对多或多对多关系。
5.2 实体类映射配置
在关联集合属性上使用@Fetch
注解,并指定@Fetch(FetchMode.SUBSELECT)
来启用子查询加载策略。
@Entity
public class Order {// ...@OneToMany(mappedBy = "order")@Fetch(FetchMode.SUBSELECT)private List<OrderItem> orderItems;// ...
}
5.3 使用场景和注意事项
- 适用于一对多或多对多的关联关系,减少关联集合对象的查询次数,提高性能。
- 子查询加载策略会执行多条SQL语句,需要考虑数据库性能和查询效率的平衡。
6. 基于批处理的加载(Batch Loading)
6.1 概述
基于批处理的加载策略通过执行批量SQL语句一次性加载多个实体对象,减少数据库交互次数,提高性能。
6.2 实体类映射配置
在关联集合属性上使用@BatchSize
注解,并指定要批量加载的大小。
@Entity
public class Order {// ...@OneToMany(mappedBy = "order")@BatchSize(size = 10)private List<OrderItem> orderItems;// ...
}
@BatchSize
注解的作用是为了优化 Hibernate 对关联集合属性的查询性能。它可以应用于实体类的集合属性上,告诉 Hibernate 在加载该集合属性时一次性加载指定个数的数据,从而减少数据库查询次数。
具体来说,@BatchSize
注解会影响到使用延迟加载(FetchType.LAZY)的关联集合属性的时候。当访问该集合属性时,如果该属性还没有被初始化,Hibernate 会触发查询加载数据。通过设置 @BatchSize
注解,可以控制同时加载的数据量,从而减少数据库查询的次数。
例如,假设我们有一个 EntityA
实体类,其中包含一个 List<EntityB>
类型的集合属性,我们可以在该属性上添加 @BatchSize
注解来优化查询性能:
@Entity
public class EntityA {// ...@OneToMany(mappedBy = "entityA", fetch = FetchType.LAZY)@BatchSize(size = 10) // 在这里添加注解private List<EntityB> entityBList;// getters and setters
}
在这个例子中,将 @BatchSize(size = 10)
注解应用于 entityBList
属性上。当访问 entityBList
属性时,Hibernate 会一次性加载 10 条 EntityB
数据,从而减少查询次数,提高性能。
需要注意的是,@BatchSize
注解只对延迟加载的集合属性有效,对于即时加载(FetchType.EAGER)的集合属性无效。此外,它只影响到关联集合属性本身的加载,不会影响到关联实体对象的加载。
总结来说,@BatchSize
注解可以优化延迟加载的关联集合属性的查询性能,通过一次加载多个数据项减少数据库查询次数。但需要根据具体情况选择合适的批处理大小,并综合考虑内存消耗和查询性能。
6.3 使用场景和注意事项
- 适用于一对多或多对多的关联关系,减少数据库交互次数,提高性能。
- 需要根据实际情况调整批量加载的大小,避免一次性加载过多数据导致内存占用过大。
7. 总结
Hibernate提供了多种抓取策略用于加载关联实体对象,包括即时加载、延迟加载、手动加载、子查询加载和基于批处理的加载。选择合适的抓取策略可以提高查询性能和减少数据库交互次数,但需要根据实际情况和性能要求进行综合考虑。
Demo
import lombok.Data;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import java.util.List;
@Entity
@Data
public class Classes {@Idprivate int id;private String name;
// @OneToMany(mappedBy = "classes")
// private List<Student> students;//子查询加载
// @OneToMany(mappedBy = "classes")
// @Fetch(FetchMode.SUBSELECT)
// private List<Student> students;//基于批处理的加载@OneToMany(mappedBy = "classes")@BatchSize(size = 6)private List<Student> students;public void loadCustomer() {// 手动加载关联的Customer对象Hibernate.initialize(students);}}import lombok.Data;
import javax.persistence.*;@Entity
@Data
public class Student {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int id;private String name;@ManyToOne(fetch = FetchType.EAGER)private Classes classes;}
public interface JPAClassesDao extends JpaRepository<Classes, Integer>, Serializable {
}
public interface JPAStudentDao extends JpaRepository<Student, Integer> , Serializable {
}
@AutowiredJPAStudentDao jpastudentDao;@AutowiredJPAClassesDao jpaclassesDao;
// @Resource
// private SessionFactory factory;@RequestMapping("/Insert")public void InsertClassesTest(){List<Classes> a=new ArrayList<>();List<Student> s=new ArrayList<>();for (int i = 0; i < 10; i++) {Classes b=new Classes();b.setId(i);b.setName("班级"+i);a.add(b);for (int j = 0; j < 20; j++) {Student c=new Student();c.setClasses(b);c.setName("学生"+i+j);s.add(c);}}jpaclassesDao.saveAll(a);jpastudentDao.saveAll(s);}@RequestMapping("/QueryEAGER")public void queryEAGERTest(){Classes byId = jpaclassesDao.findById(0).get();for (Student student : byId.getStudents()) {System.out.println("班级"+byId.getName()+"的:"+student.getName());}}@RequestMapping("/QueryLAZY")public void queryLAZYTest(){Classes byId = jpaclassesDao.getOne(1);System.out.println("--------------------------------------");for (Student student : byId.getStudents()) {System.out.println("班级"+byId.getName()+"的:"+student.getName());}}@RequestMapping("/QueryTriggerLAZY")public void queryTriggerTest(){Classes byId = jpaclassesDao.getOne(1);// byId.loadCustomer();for (Student student : byId.getStudents()) {System.out.println("班级"+byId.getName()+"的:"+student.getName());}}@RequestMapping("/QuerySubselect")public void querySubselectTest(){Classes byId = jpaclassesDao.getOne(1);for (Student student : byId.getStudents()) {System.out.println("班级"+byId.getName()+"的:"+student.getName());}}
<!-- hibernate依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>