在Hibernate中session.get()和session.byId()。load()有什么区别?

时间:2017-10-26 19:40:22

标签: java hibernate orm entity identifier

当一个对象被写入数据库并且主要标识符(id)已知时,可以通过以下代码检索它:

MyObject myObject = session.get(Class<MyObject>, id);

看来,还有另一种方法类似于get()方法:

IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);

我正在寻找一个方案来澄清它们之间的差异,并描述了在API中为同一个工作提供两种类似方法的原因。

可以询问有关session.load()session.byId().getReference()的相同问题。

编辑1:
根据API文档:

  • session.get()session.byId().load()返回具有给定标识符的持久化实例,如果没有此类持久性实例则返回null。

  • session.load()session.byId().getReference()可能会返回按需初始化的代理实例。

4 个答案:

答案 0 :(得分:3)

IdentifierLoadAccess允许您指定:

  • LockOptions
  • CacheMode

甚至一次指定它们:

Post post = session
.byId( Post.class )
.with( new LockOptions( LockMode.OPTIMISTIC_FORCE_INCREMENT) )
.with( CacheMode.GET )
.load( id );

getting a Proxy reference通过getReference(id)进行相同的操作。

因此,它们比仅使用实体标识符的标准getload更灵活。

答案 1 :(得分:1)

之间的相似性
MyObject myObject = session.get(Class<MyObject>, id); 
and 
IdentifierLoadAccess<MyObject> ila = session.byId(Class<MyObject>);
MyObject myObject = ila.load(id);

是两者都使用了hibernate缓存机制的副本,但差异在于从数据库中获取数据,即

当我们使用 session.get(Class,id)来自数据库的数据进入缓存时,您可以对该数据进行更改并将反映回数据库,因为hibernate内部维护时间戳缓存。此时间戳缓存记录特定Hibernate托管表被修改的时间,并且在从实体缓存返回数据之前,它会验证结果缓存在表修改时间方面是否较旧。

但是在 session.byId()。getReference()的情况下,hibernate使用自然id的概念,其中来自数据库的数据进入缓存但只有onces。如果你对该数据做了任何更改使用session.save(实体对象)方法hibernate将抛出一个异常,如果你手动修改表(插入,更新,删除),当你再次获取数据时它将不会被反射回来,因为它始终从缓存中获取数据检查该实体的表是否再次被修改。

如果 session.get() session.load(),如果数据库中有任何更改,例如(插入,删除,更新)记录,它将会如果记录被删除,则以记录或空指针异常的形式反映。但是在session.byId()。load()和session.byId()。getReference()的情况下,它将首先从数据库中获取记录尝试第一次获取然后它将在会话中保存这些记录,并且只有在发生任何(插入,删除,更新)然后它不会被反射回来时才会从会话向用户显示

答案 2 :(得分:0)

它主要用于多态关联/查询。假设您有一个名为User的实体与BillingDetails关联。如果BillingDetails已映射 懒惰=#&34;真&#34; (这是默认值),Hibernate将代理关联目标。在这种情况下,您将无法在运行时对具体类CreditCard(它是BillingDetails的子类)执行类型转换,甚至instanceof运算符也会表现得很奇怪:

User user = (User) session.get(User.class, userid);
BillingDetails bd = user.getDefaultBillingDetails();
System.out.println( bd instanceof CreditCard ); // Prints "false"
CreditCard cc = (CreditCard) bd; // ClassCastException!

要执行代理安全的类型转换,请使用load()

User user = (User) session.get(User.class, userId);
BillingDetails bd = user.getDefaultBillingDetails();
// Narrow the proxy to the subclass, doesn't hit the database
CreditCard cc =
(CreditCard) session.load( CreditCard.class, bd.getId() );
expiryDate = cc.getExpiryDate();

请注意,您可以通过避免延迟抓取来避免这些问题,如下面的代码所示,使用预先获取的查询

User user = (User)session.createCriteria(User.class)
.add(Restrictions.eq("id", uid) )
.setFetchMode("defaultBillingDetails", FetchMode.JOIN)
.uniqueResult();
// The users defaultBillingDetails have been fetched eagerly
CreditCard cc = (CreditCard) user.getDefaultBillingDetails();
expiryDate = cc.getExpiryDate();

真正面向对象的代码不应该使用instanceof或众多的类型转换。如果你发现自己遇到代理问题,你应该质疑你的设计,询问是否有更多的多态方法。

答案 3 :(得分:-1)

get()和load()方法的主要区别在于,如果找不到传递给它的id的对象,load()将抛出异常,但get()将返回null。另一个重要的区别是,除非需要(当你访问id以外的任何属性)但是get()总是转到数据库时,load可以在不命中数据库的情况下返回代理,因此有时使用load()可能比get()方法更快。如果您知道对象存在则使用load()方法是有意义的,但如果您不确定对象是否存在则使用get()方法。

相关问题