Hibernate中session.get()和session.load()的区别

本文详细对比了Hibernate框架中Session.get()与Session.load()两种方法的区别,包括它们的工作原理、性能影响及异常处理等方面,帮助开发者选择最适合应用程序需求的方法。

-- 翻译自https://www.mkyong.com/hibernate/different-between-session-get-and-session-load/

很多时候你会发现,使用Hibernate的开发人员会混淆session.get()和session.load()的用法,你是否理解这两个方法的区别并知道在什么时候使用正确的方法呢?

实际上,两个方法都是用来从数据库获取对象,只不过实现机制不一样而已。

1. session.load()

  • 这种方式总是会返回一个代理而不是真正得去查询数据库。 在Hibernate里面,代理是一个依据ID值获取到的对象,该对象的属性还没有初始化,它看起来就是一个临时的虚拟对象而已。
  • 如果load方法没有找到数据,就会抛出ObjectNotFoundException.

2. session.get()

  • 这种方式总是会去数据库查询数据并返回一个真实的对象,该对象就代表数据库中的一行而非代理。
  • 如果没有找到数据就会返回null.

性能方面的区别

由于某些原因,Hibernate会创建一些对象,当你做关联查询的时候,为了维护对象之间的关系,一般来说你会从数据库中拿到一个对象(持久化的实例)并把该对象作为一个引用指向另一个对象。我们来看一个例子来让大家理解什么情况下我们应该使用session.load().

1. session.get()

拿一个股票的应用来说,股票跟股票交易之间应该是一对多的关系,当你保存一次股票交易的时候,一般来说我们会写如下的代码:

Stock stock = (Stock)session.get(Stock.class, new Integer(2));
           StockTransaction stockTransactions = new StockTransaction();
           //set stockTransactions detail
           stockTransactions.setStock(stock);
           session.save(stockTransactions);

输出:

Hibernate:
    select ... from mkyong.stock stock0_
    where stock0_.STOCK_ID=?
Hibernate:
    insert into mkyong.stock_transaction (...)
    values (?, ?, ?, ?, ?, ?)

用session.get(), Hibernate会查询数据库以获取Stock对象,并把该对象作为一个引用指向StockTransaction对象。然而,保存流程数据的操作是很耗费资源的,如果一个小时内有成千上万次股票交易,你认为有必要每次都去查询数据库,去首先拿到Stock的真实对象然后把StockTransaction保存到数据库中吗?毕竟来说,你只是需要一个Stock的对象来让StockTransaction引用而已。

2. session.load()

在上面的应用场景中,session.load()会是一个好的解决方案,让我们来看一个例子:

Stock stock = (Stock)session.load(Stock.class, new Integer(2));
           StockTransaction stockTransactions = new StockTransaction();
           //set stockTransactions detail
           stockTransactions.setStock(stock);
           session.save(stockTransactions);

输出:

Hibernate:
    insert into mkyong.stock_transaction (...)
    values (?, ?, ?, ?, ?, ?)

使用session.load()方式,Hibernate不会查询数据库(输出的SQL里面没有select语句)来获取Stock对象,这种方式会返回一个Stock的代理对象 - 一个依据给定的ID值得虚假对象。这这种场景中,一个代理对象就足够用来保存股票交易的记录了。

异常方面的区别

session.load()

Stock stock = (Stock)session.load(Stock.class, new Integer(100)); //proxy

 //initialize proxy, no row for id 100, throw ObjectNotFoundException
System.out.println(stock.getStockCode());

load方法总是会依据给定的ID值来返回一个代理对象,哪怕这个ID值甚至都不存在于数据库。然而,当Hibernate试图从数据库中获取属性来初始化该代理对象的时候,它就会使用select语句来从数据库查询数据,如果没有找到任何数据行,就会抛出ObjectNotFoundException

org.hibernate.ObjectNotFoundException: No row with the given identifier exists:
[com.mkyong.common.Stock#100]

session.get()

//return null if not found
Stock stock = (Stock)session.get(Stock.class, new Integer(100));
System.out.println(stock.getStockCode()); //java.lang.NullPointerException

与load方法不一样,如果在数据库中没有找到数据,get方法会返回null.

总结:

关于这两个方法的选择没有一成不变的解决方案,你必须要首先理解这两种方法的区别和联系,然后才能决定哪种方式更适合你的应用场景。

 

PS:第一次翻译国外网站的文章,感觉真是费力,本人英语水平有限,还请各位多多谅解。其实关于session.get()和session.load()的区别,只要记住两点就好了:

1. load方法支持延迟加载而get方法则不会。

2. load方法在没找到数据的时候会抛出ObjectNotFoundException而get方法则会返回空

Hibernate Session接口的`get()``load()`方法在功能使用场景上存在一些区别: ### 检索策略 - **`get()`方法**:采用立即检索策略,调用该方法时,Hibernate会立即执行`select`语句从数据库中查询对象。 ```java Session session = sessionFactory.openSession(); Student student = (Student) session.get(Student.class, 1L); session.close(); ``` - **`load()`方法**:默认采用延迟检索策略,调用时Hibernate不会立即执行`select`语句,仅返回实体类的代理类实例,占用内存很少。只有在真正使用该对象的属性时,才会执行`select`语句从数据库中加载数据。 ```java Session session = sessionFactory.openSession(); Student student = (Student) session.load(Student.class, 1L); // 当使用student对象的属性时,才会执行select语句 System.out.println(student.getName()); session.close(); ``` ### 处理不存在记录的情况 - **`get()`方法**:如果数据库中没有该记录,并且`session`没有被关闭,返回`null`。 ```java Session session = sessionFactory.openSession(); Student student = (Student) session.get(Student.class, 999L); if (student == null) { System.out.println("记录不存在"); } session.close(); ``` - **`load()`方法**:如果数据库中没有该记录,会抛出`ObjectNotFoundException`或`EntityNotFoundException`异常。 ```java try { Session session = sessionFactory.openSession(); Student student = (Student) session.load(Student.class, 999L); System.out.println(student.getName()); session.close(); } catch (Exception e) { e.printStackTrace(); } ``` ### 处理`session`关闭的情况 - **`get()`方法**:在`session`关闭后仍然可以获得对象(前提是在`session`关闭前已经将对象加载到内存中)。 ```java Session session = sessionFactory.openSession(); Student student = (Student) session.get(Student.class, 1L); session.close(); // 可以正常使用student对象 System.out.println(student.getName()); ``` - **`load()`方法**:如果在`session`关闭后尝试访问延迟加载的对象,会抛出`LazyInitializationException`异常。 ```java Session session = sessionFactory.openSession(); Student student = (Student) session.load(Student.class, 1L); session.close(); try { // 会抛出LazyInitializationException异常 System.out.println(student.getName()); } catch (LazyInitializationException e) { e.printStackTrace(); } ``` ### 重载方法 - **`load()`方法**:有一个重载方法`public void load(Object object, Serializable id)`,可以使用“一个空的持久化类的实例 + 主键”来获取实体对象。 ```java @Test public void testLoad() throws Exception { Session session = sessionFactory.openSession(); Student student = new Student(); session.load(student, 1L); System.out.println(student); session.close(); } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值