Hibernate有一些方法,它们以某种方式获取对象并将其放入数据库中。它们之间的区别是什么,什么时候使用哪个,为什么没有一种智能方法知道什么时候使用什么?
到目前为止,我已经确定的方法是:
save () (更新) saveOrUpdate () saveOrUpdateCopy () 合并() persist ()
Hibernate有一些方法,它们以某种方式获取对象并将其放入数据库中。它们之间的区别是什么,什么时候使用哪个,为什么没有一种智能方法知道什么时候使用什么?
到目前为止,我已经确定的方法是:
save () (更新) saveOrUpdate () saveOrUpdateCopy () 合并() persist ()
当前回答
请注意,如果对已分离对象调用更新,无论是否更改对象,数据库中总是会完成更新。如果不是你想要的,你应该使用Session.lock()和LockMode.None。
只有当对象在当前会话范围之外被更改时(当处于分离模式时),才应该调用update。
其他回答
See the Hibernate Forum for a explanation of the subtle differences between persist and save. It looks like the difference is the time the INSERT statement is ultimately executed. Since save does return the identifier, the INSERT statement has to be executed instantly regardless of the state of the transaction (which generally is a bad thing). Persist won't execute any statements outside of the currently running transaction just to assign the identifier. Save/Persist both work on transient instances, ie instances which have no identifier assigned yet and as such are not saved in the DB. Update and Merge both work on detached instances, ie instances which have a corresponding entry in the DB but which are currently not attached to (or managed by) a Session. The difference between them are what happens to the instance which is passed to the function. update tries to reattach the instance, that means that there must be no other instance of the persistent entity attached to the Session right now, otherwise an exception is thrown. merge, however, just copies all values to a persistent instance in the Session (which will be loaded if it is not currently loaded). The input object is not changed. So merge is more general than update, but may use more resources.
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║ METHOD ║ TRANSIENT ║ DETACHED ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║ ║ sets id if doesn't ║ sets new id even if object ║
║ save() ║ exist, persists to db, ║ already has it, persists ║
║ ║ returns attached object ║ to DB, returns attached object ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║ ║ sets id on object ║ throws ║
║ persist() ║ persists object to DB ║ PersistenceException ║
║ ║ ║ ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║ ║ ║ ║
║ update() ║ Exception ║ persists and reattaches ║
║ ║ ║ ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║ ║ copy the state of object in ║ copy the state of obj in ║
║ merge() ║ DB, doesn't attach it, ║ DB, doesn't attach it, ║
║ ║ returns attached object ║ returns attached object ║
╠══════════════╬═══════════════════════════════╬════════════════════════════════╣
║ ║ ║ ║
║saveOrUpdate()║ as save() ║ as update() ║
║ ║ ║ ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝
以下是我对这些方法的理解。这些主要是基于API的,尽管我在实践中没有使用所有这些。
saveOrUpdate 根据某些检查调用save或update。例如,如果不存在标识符,则调用save。否则调用update。
保存 持久化一个实体。如果标识符不存在,将分配标识符。如果有,它本质上是在进行更新。返回生成的实体ID。
更新 尝试使用现有标识符持久化实体。如果不存在标识符,则抛出异常。
saveOrUpdateCopy 这已弃用,不应再使用。取而代之的是……
合并 现在我的知识开始动摇了。这里重要的是瞬态实体、分离实体和持久实体之间的区别。有关对象状态的更多信息,请查看这里。使用save & update,你处理的是持久对象。它们被链接到一个会话,因此Hibernate知道发生了什么变化。但是当你有一个瞬态对象时,就不涉及会话了。在这些情况下,您需要使用合并进行更新,使用持久化进行保存。
坚持 如上所述,这用于瞬态对象。它不返回生成的ID。
请注意,如果对已分离对象调用更新,无论是否更改对象,数据库中总是会完成更新。如果不是你想要的,你应该使用Session.lock()和LockMode.None。
只有当对象在当前会话范围之外被更改时(当处于分离模式时),才应该调用update。
这个链接很好地解释了:
http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/
我们都有一些偶尔遇到的问题,当我们再次看到它们时,我们知道我们已经解决了这个问题,但不记得是如何解决的。
在Hibernate中使用Session.saveOrUpdate()时抛出的NonUniqueObjectException就是我的一个。我将为一个复杂的应用程序添加新功能。我所有的单元测试都运行正常。然后在测试UI时,试图保存一个对象,我开始得到一个异常,消息是“具有相同标识符值的不同对象已经与会话关联”。下面是一些来自Java Persistence with Hibernate的示例代码。
Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close(); // end of first session, item is detached
item.getId(); // The database identity is "1234"
item.setDescription("my new description");
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
session2.update(item); // Throws NonUniqueObjectException
tx2.commit();
session2.close();
要理解此异常的原因,重要的是要理解分离对象以及在分离对象上调用saveOrUpdate()(或仅仅是update())时会发生什么。
当我们关闭一个单独的Hibernate Session时,我们正在使用的持久对象被分离。这意味着数据仍然在应用程序的内存中,但Hibernate不再负责跟踪对象的更改。
如果我们随后修改分离对象并想要更新它,则必须重新附加该对象。在重新连接过程中,Hibernate将检查是否有相同对象的其他副本。如果它找到了,它必须告诉我们它不再知道“真正的”副本是什么了。也许对我们期望保存的其他副本做了其他更改,但Hibernate不知道这些更改,因为它当时没有管理它们。
Hibernate不是保存可能的坏数据,而是通过NonUniqueObjectException告诉我们问题。
那么我们该怎么办呢?在Hibernate 3中,我们有merge()(在Hibernate 2中,使用saveOrUpdateCopy())。此方法将强制Hibernate将其他分离实例中的任何更改复制到您想要保存的实例上,从而在保存之前将所有更改合并到内存中。
Session session = sessionFactory1.openSession();
Transaction tx = session.beginTransaction();
Item item = (Item) session.get(Item.class, new Long(1234));
tx.commit();
session.close(); // end of first session, item is detached
item.getId(); // The database identity is "1234"
item.setDescription("my new description");
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Item item2 = (Item) session2.get(Item.class, new Long(1234));
Item item3 = session2.merge(item); // Success!
tx2.commit();
session2.close();
需要注意的是,merge返回对实例最新更新版本的引用。它不是将项重新附加到Session。如果您测试实例是否相等(item == item3),您将发现在这种情况下它返回false。从现在开始,您可能希望使用item3。
还需要注意的是,Java Persistence API (JPA)没有分离和重新连接对象的概念,而是使用EntityManager.persist()和EntityManager.merge()。
我发现在使用Hibernate时,saveOrUpdate()通常足以满足我的需要。通常只有当我的对象可以引用相同类型的对象时,我才需要使用merge。最近,异常的原因是在验证引用不是递归的代码中。作为验证的一部分,我将相同的对象加载到会话中,导致了错误。
您在哪里遇到过这个问题?合并对你有用吗?还是你需要另一种解决方案?你喜欢总是使用归并,还是喜欢只在特定情况下使用它