Hibernate的检索策略包括立即检索和延迟检索,可以在配置文件中通过对lazy、fetch、batch-size属性的设置来进行控制。一对多、多对多、多对一和一对一关系下的不同检索策略将影响对数据库访问的效率。
检索策略
- 立即检索,立即加载检索方法指定的对象
- 延迟检索,延迟加载检索方法指定的对象,在使用具体属性值时,才进行加载(这个时候会执行查询语句)
检索策略使用场景
- 如果加载对象是为了访问他的属性,则使用立即加载
- 如果加载对象目的是为了获得他的应用,则可以使用延迟加载
检索策略属性
以一个老师有多个学生,一对多的关系为例
- lazy: 主要决定students集合被初始化的时机. 即到底是在加载 Teacher对象时就被初始化, 还是在程序访问 students 集合时被初始化
- fetch: 取值为 “select” 或 “subselect” 时, 决定初始化 students的查询语句的形式; 若取值为”join”, 则决定 students 集合被初始化的时机,若把 fetch 设置为 “join”, lazy 属性将被忽略
- batch-size:批量检索能减少 SELECT 语句的数目, 提高延迟检索或立即检索的运行性能
一对多和多对多的检索策略,lazy和fetch取值对应策略
- lazy=true ——– fatch=默认 ——– 采用延迟检索
- lazy=false ——– fatch=默认 ——– 采用立即检索
- lazy=extra ——– fatch=默认 ——– 采用加强延迟检索(延迟对象集合初始化时机)
- lazy=true/false/extra ——– fatch=默认 ——– 根据lazy决定执行检索策略
- lazy=true/false/extra ——– fatch=subselect ——– 根据lazy决定执行检索策略
- lazy=默认 ——– fatch=join ——– 采用迫切左外连接策略
多对一和一对一的检索策略,lazy和fetch取值对应策略
- lazy=proxy ——– fetch=默认 ——– 采用延迟检索
- lazy=non-proxy ——– fetch=默认 ——– 采用无代理延迟检索(需要增强持久化类的字节码才能实现)
- lazy=false ——– fetch=默认 ——– 采用立即检索
- lazy=默认 ——– fetch=join ——– 采用迫切左外连接策略(比立即检索用更少select语句)
实例验证
以下以学生与老师,多对多的关系为例
- 实体类
学生package test.hibernate.spring.model; import java.util.HashSet; import java.util.Set; public class Student { private int id; private String name; private Set<Teacher> teachers=new HashSet<>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Teacher> getTeachers() { return teachers; } public void setTeachers(Set<Teacher> teachers) { this.teachers = teachers; } public Student() { super(); } @Override public String toString() { return "Student [id=" + id + ", name=" + name + "]"; } }
老师
package test.hibernate.spring.model; import java.util.HashSet; import java.util.Set; public class Teacher { private int id; private String name; private Set<Student> students=new HashSet<>(); public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } public Teacher() { super(); } @Override public String toString() { return "Teacher [id=" + id + ", name=" + name + "]"; } }
- 配置文件
Student.hbm.xml<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <!-- 类级加载配置 lazy默认为true--> <class name="test.hibernate.spring.model.Student" table="s_students" > <id name="id" type="int"> <column name="s_id" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="s_name" /> </property> <!-- 集合加载配置,当lazy="true"时不会检索,fetch="join"时,lazy属性无效,将会使用left outer join进行检索,减少对数据库的访问次数--> <!-- batch-size是设置每次访问数据库时检索的数量,默认是1,当值设大则会减少对数据库的访问次数,但设得过大也会加大内存的负担,需根据服务器的硬件配置来设置 --> <set name="teachers" table="t_students_teachers" inverse="true" lazy="false" fetch="join" batch-size="3"> <key> <column name="s_id" /> </key> <many-to-many class="test.hibernate.spring.model.Teacher" column="t_id"></many-to-many> </set> </class> </hibernate-mapping>
Teacher.hbm.xml<?xml version="1.0"?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping> <class name="test.hibernate.spring.model.Teacher" table="t_teachers"> <id name="id" type="int"> <column name="t_id" /> <generator class="native" /> </id> <property name="name" type="java.lang.String"> <column name="t_name" /> </property> <set name="students" table="t_students_teachers" inverse="false" fetch="subselect" batch-size="3"> <key> <column name="t_id" /> </key> <many-to-many class="test.hibernate.spring.model.Student" column="s_id"></many-to-many> </set> </class> </hibernate-mapping>
- 测试
/**
*Description:
*author: ljd
*@date 2024年7月30日
*@version 1.0
*/
package test.hibernate.spring;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.cfg.Configuration;
import org.hibernate.query.Query;
import org.hibernate.service.ServiceRegistry;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import test.hibernate.spring.model.Student;
public class TestSession {
SessionFactory sessionFactory = null;
Session session = null;
Transaction ts = null;
@Before
public void beforP() {
System.out.println("begin....");
/* hibernate规定,所有的配置或服务,必须配置或注册到一个服务注册类中 */
Configuration configuration = new Configuration().configure();
ServiceRegistry sr = configuration.getStandardServiceRegistryBuilder().build();
/* 从注册类中获得工厂类 */
sessionFactory = new MetadataSources(sr).buildMetadata().buildSessionFactory();
/* 通过工厂类开启Session */
session = sessionFactory.openSession();
/* 开启事务 */
ts = session.beginTransaction();
}
@After
public void endP() {
System.out.println("end....");
/* 提交事务 */
ts.commit();
/* 关闭Session */
session.close();
/* 关闭工厂 */
sessionFactory.close();
}
// @Test
public void testGet() {
// 使用get时, class标签中lazy值不管是true还是false都会直接加载,当set标签
// 中fetch="join"时,会在get时使用左join将其集体一起检索出来,访问数据库只需一次
// fetch值是其它时则需要访两次
Student s = session.get(Student.class, 1);
System.out.println(s.getTeachers().size());
}
// @Test
public void testLoad() {
// 使用load时, class标签中lazy是true时不会直接加载,是false时会直接加载
Student s = session.load(Student.class, 1);
// 类中的集合是否直接检索,取决于类是否是直接加载,如果类是懒加载,那么集合将是在访问时才会检索
System.out.println(s.getTeachers().size());
}
@Test
public void testBatchSize() {
String sql = "from Student";
@SuppressWarnings("unchecked")
Query<Student> query = session.createQuery(sql);
List<Student> students = query.list();
for (Student s : students) {
System.out.println(s.getTeachers().size());
System.out.println("-----------------------------");
}
}
}