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提供的代理对象。当访问代理对象的属性(如nameage)时,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仓库获取。


原始标题:load() vs. get() in Hibernate | Baeldung