1. 引言
在Hibernate框架中,load()
和get()
是两种常用的数据检索方法。本文将深入探讨这两种方法的核心差异,帮助开发者在实际场景中做出正确选择。
2. 加载策略对比
load()
方法采用延迟加载(Lazy Loading)策略。调用时返回一个实体代理对象,直到访问对象属性或方法时才触发数据库查询。工作原理如下:
Person person = new Person("John Doe", 30);
Session session = sessionFactory.getCurrentSession();
session.saveOrUpdate(person);
Person entity = session.load(Person.class, person.getId());
assertNotNull(entity);
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
首先创建并保存Person
对象,然后通过load()
获取实体。虽然entity
看起来是Person
对象,实际是Hibernate提供的代理对象。当访问代理对象的属性(如name
和age
)时,Hibernate会拦截调用并动态加载数据库数据。
相反,get()
方法采用立即加载(Eager Loading)策略,直接查询数据库并返回实际实体对象:
Person entity = session.get(Person.class, person.getId());
assertNotNull(entity);
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
3. 数据存在时的返回值
调用load()
时,Hibernate会创建一个仅填充主键id
的实体代理对象。其他属性保持未初始化状态,首次访问时才从数据库加载。如果在未初始化时访问属性,会抛出LazyInitializationException
:
Session session = sessionFactory.openSession();
session = sessionFactory.openSession();
Person entity = session.load(Person.class, person.getId());
// 关闭session
session.close();
assertThrows(LazyInitializationException.class, () -> {
entity.getName();
});
而get()
方法直接从数据库获取实际实体数据。返回的对象包含所有已初始化属性,即使关闭Hibernate Session后仍可正常访问:
Session session = sessionFactory.openSession();
session = sessionFactory.openSession();
Person entity = session.get(Person.class, person.getId());
// 关闭session
session.close();
// session关闭后仍可访问属性
assertEquals(person.getName(), entity.getName());
assertEquals(person.getAge(), entity.getAge());
4. 数据不存在时的行为
使用load()
时,即使数据不存在也会返回代理对象。只有当访问对象属性时才会触发数据库查询,若实体不存在则抛出ObjectNotFoundException
:
Session session = sessionFactory.getCurrentSession();
Person entity = session.load(Person.class, 100L);
// 访问属性触发数据库查询
assertThrows(ObjectNotFoundException.class, () -> {
entity.getName();
});
而get()
方法在数据存在时立即返回实际对象,**若数据不存在则直接返回null
**:
Session session = sessionFactory.getCurrentSession();
Person entity = session.get(Person.class, 100L);
assertNull(entity);
5. 缓存机制
两种方法都利用一级缓存(Session级别缓存)存储当前会话中访问过的实体。缓存保存最近访问或操作过的实体。
关键差异在于:
get()
方法:若对象存在于缓存,直接返回缓存对象load()
方法:即使数据已缓存,仍返回代理对象
示例说明:
Person person = new Person("John Doe", 30);
Session session = sessionFactory.openSession();
session.saveOrUpdate(person);
Person entity = session.get(Person.class, person.getId());
// 从session缓存中移除实体,模拟新会话
session.evict(entity);
Person cacheEntity = session.get(Person.class, person.getId());
当创建Person
实体后,Hibernate会缓存该实体。首次调用get()
时,因实体在缓存中不会查询数据库。随后从缓存中移除实体模拟新会话。
再次使用get()
获取实体时,因缓存中不存在,Hibernate会查询数据库。控制台输出仅显示一条SQL select语句:
Hibernate: select p1_0.id,p1_0.age,p1_0.name from Person p1_0 where p1_0.id=?
6. 核心差异总结
特性 | get() 方法 |
load() 方法 |
---|---|---|
加载策略 | 立即加载(直接查询数据库) | 延迟加载(返回代理对象) |
数据存在时的返回值 | 实际对象 | 代理对象 |
数据库查询时机 | 立即执行 | 首次访问属性时执行 |
数据不存在时的返回值 | null |
访问属性时抛出ObjectNotFoundException |
缓存行为 | 优先返回缓存对象 | 即使缓存存在仍返回代理对象 |
7. 结论
本文深入分析了Hibernate中get()
和load()
方法的本质区别:
- ✅ **
get()
**:适合需要立即访问对象并确保数据最新的场景 - ✅ **
load()
**:适合仅需引用对象且需优化数据库调用的场景
⚠️ 踩坑提醒:使用load()
时务必注意Session生命周期,避免LazyInitializationException
。在不确定数据是否存在时,优先使用get()
方法。
示例源码可在GitHub仓库获取。