休眠:session.get和session.load之间的区别


88

从API中,我可以看到它与代理有关。但是我找不到有关代理的大量信息,也不了解调用session.get和之间的区别session.load。有人可以解释一下或将我定向到参考页面吗?

谢谢!!

Answers:


117

Hibernate论坛中

摘自《休眠中的行动》一书。好人读过这个..


通过标识符检索对象以下Hibernate代码段从数据库检索User对象:

User user = (User) session.get(User.class, userID);

get()方法很特殊,因为标识符唯一地标识一个类的单个实例。因此,应用程序通常使用标识符作为持久对象的便捷句柄。按标识符检索可以在检索对象时使用高速缓存,从而避免在对象已被高速缓存的情况下数据库命中。Hibernate还提供了load()方法:

User user = (User) session.load(User.class, userID);

load()方法较旧;由于用户请求,get()已添加到Hibernate的API中。区别不大:

如果load()在缓存或数据库中找不到对象,则会引发异常。load()方法从不返回null。如果找不到对象,则get()方法将返回null。

load()方法可以返回代理而不是实际的持久实例。代理是一个占位符,它在首次访问实物时触发加载。另一方面,get()从不返回代理。在get()和load()之间进行选择很容易:如果确定持久对象存在,并且不存在将被认为是例外,则load()是一个不错的选择。如果不确定是否存在具有给定标识符的持久实例,请使用get()并测试返回值以查看其是否为null。使用load()有进一步的含义:应用程序可以检索对持久实例的有效引用(代理),而无需访问数据库来检索其持久状态。因此,当load()在缓存或数据库中找不到持久对象时,它可能不会引发异常。访问代理后,将在以后引发异常。当然,通过标识符检索对象并不像使用任意查询那样灵活。


1
我正在调试一个问题,其中session.Get <T>()返回代理!
肯特·布加亚特

7
非常感谢!对我来说,最重要的部分是:“如果load()在缓存或数据库中找不到该对象,则会引发异常。如果找不到该对象,则get()方法将返回null。”
克里斯,2010年

15
用于Session.get的JavaDoc说:返回具有给定标识符的给定实体类的持久性实例;如果不存在这样的持久性实例,则返回null。(如果该实例或该实例的代理已经与该会话相关联,则返回该实例或代理。)因此,书中的这一节说:“另一方面,get()永远不会返回代理。” 是不正确的。
Vicky

如果您对dao使用了事务管理策略,则可能更喜欢get()。否则,在load()返回代理的情况下,调用者也需要在打开的休眠会话的上下文中执行。例如,如果您正在执行MVC,则控制器可能会执行dao.load(),然后在以后如果没有有效会话的情况下尝试访问代理对象时抛出异常。执行dao.get()会将实际对象返回到控制器,而不管会话(假定存在)
开发

@Vicky描述的问题可能会引起头痛,我看不出有任何好处。在某些情况下,我还需要标识符来进行进一步的参数化查询。但是由于该对象的代理已经在会话中,因此标识符的getter返回null。如果会话中存在该代理,为什么他们要检索该代理而不是真实实例?
djmj 2012年

15

好吧,至少在nhibernate中,session.Get(id)将从数据库中加载对象,而session.Load(id)仅为其创建代理对象,而无需离开服务器。就像您的POCO(或POJO :)中其他所有延迟加载的属性一样工作。然后,您可以使用此代理作为对对象本身的引用来创建关系等。

可以将其想像成具有仅保留Id的对象,并在需要时加载其余对象。如果只是传递它来创建关系(如FK),则ID就是您所需要的。


所以您想说load(id)首先会访问数据库以检查它是否是有效的ID,然后返回代理对象,并且在访问该对象的属性时会再次访问数据库?这是不可能的情况吗?两个查询来加载单个对象?
faisalbhagat 2014年

不,load(id)完全不会验证该id,因此不会往返于数据库。仅在确定有效时才使用它。
Jorge Alves 2014年

9

session.load()将始终返回“代理”(休眠术语),而无需访问数据库。在Hibernate中,proxy是具有给定标识符值的对象,其属性尚未初始化,它看起来像一个临时的伪对象。如果未找到行,则将引发ObjectNotFoundException。

session.get()总是命中数据库并返回真实对象,该对象代表数据库行,而不是代理。如果未找到行,则返回null。

这些方法的性能也会产生差异。在两个之间


3

还有一点:

如果在缓存以及数据库中都找不到对象,则Hibernate Session类的get方法将返回null。如果在缓存和数据库上均未找到对象,但load()方法将抛出ObjectNotFoundException,但永远不会返回null。


2

使用“加载”而不是“获取”的间接结果是,使用版本属性进行乐观锁定可能无法按您期望的那样工作。如果加载只是创建一个代理而不从数据库中读取,则不会加载version属性。仅当/如果您以后引用对象上的属性并触发选择时,才会加载版本。同时,另一个会话可以更新该对象,并且您的会话将没有进行乐观锁检查所需的原始版本-因此,您会话的更新将覆盖另一个会话的更新,而不会发出警告。

这是一种尝试通过使用具有相同标识符的对象进行两次会话来勾勒此场景的尝试。DB中对象的初始版本为10。

Session 1                  Session 2
---------                  ---------
Load object
Wait a while..   
                           Load object
                           Modify object property
                           [triggers db 'select' -
                            version read as 10]
                           Commit
                           [triggers db update,
                            version modified to 11]
Modify object property
  [triggers db 'select' -
  version read as 11]
Commit
  [triggers db update,
  version modified to 12]

我们实际上希望会话1的提交因乐观锁异常而失败,但是它将在这里成功。

使用“ get”而不是“ load”可以解决此问题,因为get将立即发出选择,并且将在正确的时间加载版本号以进行乐观锁检查。


0

另外,在使用load时我们也必须小心,因为如果不存在该对象,它将引发异常。仅在确定对象存在时才必须使用它。


0

http://www.mkyong.com/hibernate/different-between-session-get-and-session-load
session.load()中找到了一个很好的解释:
它将始终返回一个“代理”(休眠术语),而不会打数据库。
在Hibernate中,proxy是具有给定标识符值的对象,其属性尚未初始化,它看起来像一个临时的伪对象。
它将始终返回具有给定标识值的代理对象,即使该标识值在数据库中不存在。但是,当您尝试通过从数据库检索代理的属性来初始化代理时,它将使用select语句访问数据库。如果未找到任何行,则将引发ObjectNotFoundException。
session.get():
它总是命中数据库(如果未在缓存中找到)并返回真实对象,该对象代表数据库行,而不是代理。
如果未找到行,则返回null。


0

load()无法从缓存或数据库中找到对象,将引发异常,并且load()方法从不返回null。

如果找不到对象,则get()方法将返回null。load()方法可能返回一个代理,而不是一个真正的持久实例get()从不返回代理。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.