本文将深入讨论Hibernate中的抓取策略,介绍不同类型的抓取策略以及它们的使用场景和注意事项。
即时加载是指在查询主实体时立即加载相关联的实体对象。这种策略会在查询时一次性加载所有关联的实体对象,可以减少数据库查询次数。
延迟加载是指在访问关联属性时才会真正加载相关的实体对象。这种策略在查询主实体时只会加载主实体对象,而关联的实体对象只有在被访问时才会被加载。
子查询加载是指通过执行子查询来加载关联的实体集合对象,而不是通过单独的SQL语句执行关联查询。这种策略适用于一对多或多对多关系,并可以提高性能。
基于批处理的加载是指通过执行批量SQL语句一次性加载多个实体对象。这种策略可以减少数据库交互次数,提高性能。
即时加载策略可以通过设置@ManyToOne
、@OneToOne
等注解来实现,默认情况下Hibernate会使用即时加载进行关联对象的加载。
在关联属性上使用fetch = FetchType.EAGER
注解来指定即时加载策略。
@Entity
@Data
public class Classes {
@Id
private int id;
private String name;
@OneToMany(mappedBy = "classes", fetch = FetchType.EAGER)
private List<Student> students;
}
这里可以看到查询的班级实体种student属性集合有二十个对象
延迟加载策略可以通过设置@ManyToOne
、@OneToOne
等注解来实现,默认情况下Hibernate会使用延迟加载进行关联对象的加载。
在关联属性上使用fetch = FetchType.LAZY
注解来指定延迟加载策略。
@Entity
public class Classes{
// ...
@OneToMany(mappedBy = "classes", fetch = FetchType.LAZY)
private List<Student> students;
// ...
}
这里开启懒加载后查询到的对象种数据是空的,只有当执行get方法时才会去获取数据
子查询加载策略通过执行子查询来加载关联的实体集合对象,适用于一对多或多对多关系。
在关联集合属性上使用@Fetch
注解,并指定@Fetch(FetchMode.SUBSELECT)
来启用子查询加载策略。
@Entity
public class Order {
// ...
@OneToMany(mappedBy = "order")
@Fetch(FetchMode.SUBSELECT)
private List<OrderItem> orderItems;
// ...
}
基于批处理的加载策略通过执行批量SQL语句一次性加载多个实体对象,减少数据库交互次数,提高性能。
在关联集合属性上使用@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
类型的集合属性,我们可以在该属性上添加 @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
注解可以优化延迟加载的关联集合属性的查询性能,通过一次加载多个数据项减少数据库查询次数。但需要根据具体情况选择合适的批处理大小,并综合考虑内存消耗和查询性能。
Hibernate提供了多种抓取策略用于加载关联实体对象,包括即时加载、延迟加载、手动加载、子查询加载和基于批处理的加载。选择合适的抓取策略可以提高查询性能和减少数据库交互次数,但需要根据实际情况和性能要求进行综合考虑。
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 {
@Id
private int id;
private String name;
// @OneToMany(mappedBy = "classes")
// private List students;
//子查询加载
// @OneToMany(mappedBy = "classes")
// @Fetch(FetchMode.SUBSELECT)
// private List 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 {
}
@Autowired
JPAStudentDao jpastudentDao;
@Autowired
JPAClassesDao 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());
}
}
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-jpaartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
dependency>