Java Hibernate 中不同的保存方法有什么区别?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/161224/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 09:55:39  来源:igfitidea点击:

What are the differences between the different saving methods in Hibernate?

javahibernatepersistence

提问by Henrik Paul

Hibernate has a handful of methods that, one way or another, takes your object and puts it into the database. What are the differences between them, when to use which, and why isn't there just one intelligent method that knows when to use what?

Hibernate 有一些方法,它们以一种或另一种方式获取您的对象并将其放入数据库。它们之间有什么区别,何时使用哪个,为什么没有一种智能方法知道何时使用什么?

The methods that I have identified thus far are:

到目前为止,我确定的方法是:

  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()
  • save()
  • update()
  • saveOrUpdate()
  • saveOrUpdateCopy()
  • merge()
  • persist()

采纳答案by Lee Theobald

Here's my understanding of the methods. Mainly these are based on the APIthough as I don't use all of these in practice.

以上是我对方法的理解。尽管我在实践中并没有使用所有这些,但主要是基于API

saveOrUpdateCalls either save or update depending on some checks. E.g. if no identifier exists, save is called. Otherwise update is called.

saveOrUpdate根据某些检查调用 save 或 update。例如,如果不存在标识符,则调用 save。否则调用更新。

savePersists an entity. Will assign an identifier if one doesn't exist. If one does, it's essentially doing an update. Returns the generated ID of the entity.

save持久化一个实体。如果不存在,将分配一个标识符。如果有,它本质上是在进行更新。返回生成的实体 ID。

updateAttempts to persist the entity using an existing identifier. If no identifier exists, I believe an exception is thrown.

更新尝试使用现有标识符持久化实体。如果不存在标识符,我相信会抛出异常。

saveOrUpdateCopyThis is deprecated and should no longer be used. Instead there is...

saveOrUpdateCopy这已被弃用,不应再使用。反而有...

mergeNow this is where my knowledge starts to falter. The important thing here is the difference between transient, detached and persistent entities. For more info on the object states, take a look here. With save & update, you are dealing with persistent objects. They are linked to a Session so Hibernate knows what has changed. But when you have a transient object, there is no session involved. In these cases you need to use merge for updates and persist for saving.

合并现在这是我的知识开始动摇的地方。这里重要的是瞬态、分离和持久实体之间的区别。有关对象状态的更多信息,请查看此处。使用保存和更新,您正在处理持久对象。它们与会话相关联,因此 Hibernate 知道发生了什么变化。但是当你有一个瞬态对象时,就没有涉及会话。在这些情况下,您需要使用合并进行更新并使用持久保存。

persistAs mentioned above, this is used on transient objects. It does not return the generated ID.

坚持如上所述,这是用来对暂时对象。它不返回生成的 ID。

回答by jrudolph

  • See the Hibernate Forumfor 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 savedoes return the identifier, the INSERT statement has to be executed instantly regardless of the state of the transaction (which generally is a bad thing). Persistwon'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.

  • Updateand Mergeboth 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. updatetries 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 mergeis more general than update, but may use more resources.

  • 请参阅Hibernate 论坛以获取有关persist 和 save 之间细微差别的说明。看起来区别在于 INSERT 语句最终执行的时间。由于save确实返回标识符,因此无论事务的状态如何(这通常是一件坏事)都必须立即执行 INSERT 语句。Persist不会在当前运行的事务之外执行任何语句,只是为了分配标识符。Save/Persist 都适用于瞬态实例,即尚未分配标识符的实例,因此不会保存在数据库中。

  • 更新合并都适用于分离的实例,即在数据库中有相应条目但当前未附加到(或由其管理)会话的实例。它们之间的区别在于传递给函数的实例会发生什么。update尝试重新附加实例,这意味着现在不能有其他持久实体的实例附加到会话,否则会抛出异常。但是,merge只是将所有值复制到 Session 中的持久实例(如果当前未加载,则将加载该实例)。输入对象没有改变。所以合并更新更通用,但可能会使用更多资源。

回答by bernardn

Be aware that if you call an update on an detached object, there will always be an update done in the database whether you changed the object or not. If it is not what you want you should use Session.lock() with LockMode.None.

请注意,如果您对分离的对象调用更新,则无论您是否更改了对象,数据库中都将进行更新。如果这不是你想要的,你应该使用 Session.lock() 和 LockMode.None。

You should call update only if the object was changed outside the scope of your current session (when in detached mode).

仅当对象在当前会话范围之外(处于分离模式时)发生更改时,才应调用 update。

回答by Hari Krishna

Actually the difference between hibernate save()and persist()methods is depends on generator class we are using.

实际上,hibernatesave()persist()方法之间的区别取决于我们使用的生成器类。

If our generator class is assigned, then there is no difference between save()and persist() methods. Because generator ‘assigned' means, as a programmer we need to give the primary key value to save in the database right [ Hope you know this generators concept ] In case of other than assigned generator class, suppose if our generator class name is Increment means hibernate it self will assign the primary key id value into the database right [ other than assigned generator, hibernate only used to take care the primary key id value remember ], so in this case if we call save()or persist()method then it will insert the record into the database normally But hear thing is, save()method can return that primary key id value which is generated by hibernate and we can see it by

如果我们的生成器类被赋值,那么save()persist() 方法之间没有区别。因为generator 'assigned' 的意思是,作为程序员,我们需要给主键值以保存在数据库中的权利[希望你知道这个generators概念] 如果不是分配的generator class,假设我们的generator class name is Increment 意味着休眠它自己会将主键id值分配到数据库中[除了分配的生成器,休眠仅用于记住主键id值],所以在这种情况下,如果我们调用save()persist()方法,那么它会将记录插入到数据库正常但是听到的是, save()方法可以返回hibernate生成的主键id值,我们可以通过

long s = session.save(k);

In this same case, persist()will never give any value back to the client.

在同样的情况下,persist()永远不会给客户任何价值。

回答by Anton Popovich

None of the following answers are right. All these methods just seem to be alike, but in practice do absolutely different things. It is hard to give short comments. Better to give a link to full documentation about these methods: http://docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

以下答案都不对。所有这些方法看起来都一样,但实际上做的事情完全不同。很难给出简短的评论。最好提供有关这些方法的完整文档的链接:http: //docs.jboss.org/hibernate/core/3.6/reference/en-US/html/objectstate.html

回答by HakunaMatata

This link explains in good manner :

这个链接很好地解释了:

http://www.stevideter.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

http://www.stevider.com/2008/12/07/saveorupdate-versus-merge-in-hibernate/

We all have those problems that we encounter just infrequently enough that when we see them again, we know we've solved this, but can't remember how.

我们都有那些我们很少遇到的问题,以至于当我们再次看到它们时,我们知道我们已经解决了这个问题,但不记得是如何解决的。

The NonUniqueObjectException thrown when using Session.saveOrUpdate() in Hibernate is one of mine. I'll be adding new functionality to a complex application. All my unit tests work fine. Then in testing the UI, trying to save an object, I start getting an exception with the message “a different object with the same identifier value was already associated with the session.” Here's some example code from Java Persistence with 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();

To understand the cause of this exception, it's important to understand detached objects and what happens when you call saveOrUpdate() (or just update()) on a detached object.

要了解此异常的原因,了解分离的对象以及对分离的对象调用 saveOrUpdate()(或仅调用 update())时会发生什么很重要。

When we close an individual Hibernate Session, the persistent objects we are working with are detached. This means the data is still in the application's memory, but Hibernate is no longer responsible for tracking changes to the objects.

当我们关闭一个单独的 Hibernate Session 时,我们正在使用的持久对象被分离。这意味着数据仍在应用程序的内存中,但 Hibernate 不再负责跟踪对象的更改。

If we then modify our detached object and want to update it, we have to reattach the object. During that reattachment process, Hibernate will check to see if there are any other copies of the same object. If it finds any, it has to tell us it doesn't know what the “real” copy is any more. Perhaps other changes were made to those other copies that we expect to be saved, but Hibernate doesn't know about them, because it wasn't managing them at the time.

如果我们随后修改了分离的对象并想要更新它,我们必须重新附加该对象。在重新附加的过程中,Hibernate 将检查是否有相同对象的任何其他副本。如果它找到了,它必须告诉我们它不再知道“真正的”副本是什么。也许对我们期望保存的其他副本进行了其他更改,但是 Hibernate 不知道它们,因为当时它没有管理它们。

Rather than save possibly bad data, Hibernate tells us about the problem via the NonUniqueObjectException.

Hibernate 没有保存可能坏的数据,而是通过 NonUniqueObjectException 告诉我们问题。

So what are we to do? In Hibernate 3, we have merge() (in Hibernate 2, use saveOrUpdateCopy()). This method will force Hibernate to copy any changes from other detached instances onto the instance you want to save, and thus merges all the changes in memory before the save.

那么我们该怎么办呢?在 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();

It's important to note that merge returns a reference to the newly updated version of the instance. It isn't reattaching item to the Session. If you test for instance equality (item == item3), you'll find it returns false in this case. You will probably want to work with item3 from this point forward.

请务必注意,merge 返回对实例新更新版本的引用。它不会将项目重新附加到会话。如果您测试实例相等性 (item == item3),您会发现在这种情况下它返回 false。从现在开始,您可能希望使用 item3。

It's also important to note that the Java Persistence API (JPA) doesn't have a concept of detached and reattached objects, and uses EntityManager.persist() and EntityManager.merge().

同样重要的是要注意 Java Persistence API (JPA) 没有分离和重新附加对象的概念,而是使用 EntityManager.persist() 和 EntityManager.merge()。

I've found in general that when using Hibernate, saveOrUpdate() is usually sufficient for my needs. I usually only need to use merge when I have objects that can have references to objects of the same type. Most recently, the cause of the exception was in the code validating that the reference wasn't recursive. I was loading the same object into my session as part of the validation, causing the error.

我发现一般来说,在使用 Hibernate 时,saveOrUpdate() 通常足以满足我的需要。我通常只在对象可以引用相同类型的对象时才需要使用合并。最近,异常的原因在于验证引用不是递归的代码中。作为验证的一部分,我将同一个对象加载到我的会话中,从而导致了错误。

Where have you encountered this problem? Did merge work for you or did you need another solution? Do you prefer to always use merge, or prefer to use it only as needed for specific cases

你在哪里遇到过这个问题?合并对您有用还是您需要其他解决方案?您更喜欢始终使用合并,还是更喜欢仅在特定情况下需要时使用它

回答by Sergii Shevchyk

╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
║    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()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝

回答by OutOfMind

I found a good example showing the differences between all hibernate save methods:

我找到了一个很好的例子,展示了所有休眠保存方法之间的差异:

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

http://www.journaldev.com/3481/hibernate-session-merge-vs-update-save-saveorupdate-persist-example

In brief, according to the above link:

简而言之,根据上面的链接:

save()

节省()

  • We can invoke this method outside a transaction. If we use this without transaction and we have cascading between entities, then only the primary entity gets saved unless we flush the session.
  • So, if there are other objects mapped from the primary object, they gets saved at the time of committing transaction or when we flush the session.
  • 我们可以在事务之外调用这个方法。如果我们在没有事务的情况下使用它并且我们在实体之间进行级联,那么除非我们刷新会话,否则只会保存主要实体。
  • 因此,如果有从主对象映射的其他对象,它们会在提交事务或刷新会话时保存。

persist()

坚持()

  • Its similar to using save() in transaction, so it's safe and takes care of any cascaded objects.
  • 它类似于在事务中使用 save() ,因此它是安全的并且可以处理任何级联对象。

saveOrUpdate()

保存或更新()

  • Can be used with or without the transaction, and just like save(), if its used without the transaction, mapped entities wont be saved un;ess we flush the session.

  • Results into insert or update queries based on the provided data. If the data is present in the database, update query is executed.

  • 可以在有或没有事务的情况下使用,就像 save() 一样,如果它在没有事务的情况下使用,映射的实体不会被保存;除非我们刷新会话。

  • 基于提供的数据插入或更新查询的结果。如果数据存在于数据库中,则执行更新查询。

update()

更新()

  • Hibernate update should be used where we know that we are only updating the entity information. This operation adds the entity object to persistent context and further changes are tracked and saved when transaction is committed.
  • Hence even after calling update, if we set any values in the entity,they will be updated when transaction commits.
  • 当我们知道我们只更新实体信息时,应该使用 Hibernate update。此操作将实体对象添加到持久上下文,并在提交事务时跟踪和保存进一步的更改。
  • 因此,即使在调用 update 之后,如果我们在实体中设置任何值,它们也会在事务提交时更新。

merge()

合并()

  • Hibernate merge can be used to update existing values, however this method create a copy from the passed entity object and return it. The returned object is part of persistent context and tracked for any changes, passed object is not tracked. This is the major difference with merge() from all other methods.
  • Hibernate 合并可用于更新现有值,但是此方法从传递的实体对象创建副本并返回它。返回的对象是持久上下文的一部分,并跟踪任何更改,不跟踪传递的对象。这是 merge() 与所有其他方法的主要区别。

Also for practical examples of all these, please refer to the link I mentioned above, it shows examples for all these different methods.

对于所有这些的实际示例,请参阅我上面提到的链接,它显示了所有这些不同方法的示例。

回答by GingerBeer

None of the answers above are complete. Although Leo Theobald answer looks nearest answer.

以上答案都不完整。尽管 Leo Theobald 的回答看起来最接近。

The basic point is how hibernate is dealing with states of entities and how it handles them when there is a state change. Everything must be seen with respect to flushes and commits as well, which everyone seems to have ignored completely.

基本点是 hibernate 如何处理实体的状态以及当状态发生变化时它如何处理它们。一切都必须与刷新和提交有关,每个人似乎都完全忽略了这一点。

NEVER USE THE SAVE METHOD of HIBERNATE. FORGET THAT IT EVEN EXISTS IN HIBERNATE!

切勿使用 Hibernate 的保存方法。忘记它甚至存在于休眠状态!

Persist

坚持

As everyone explained, Persist basically transitions an entity from "Transient" state to "Managed" State. At this point, a slush or a commit can create an insert statement. But the entity will still remains in "Managed" state. That doesn't change with flush.

正如每个人所解释的,Persist 基本上是将实体从“瞬态”状态转换为“托管”状态。此时,slush 或 commit 可以创建插入语句。但实体仍将保持“受管”状态。这不会随着冲洗而改变。

At this point, if you "Persist" again there will be no change. And there wont be any more saves if we try to persist a persisted entity.

此时,如果您再次“坚持”,则不会有任何变化。如果我们尝试持久化一个持久化实体,就不会再有任何保存了。

The fun begins when we try to evict the entity.

当我们试图驱逐实体时,乐趣就开始了。

An evict is a special function of Hibernate which will transition the entity from "Managed" to "Detached". We cannot call a persist on a detached entity. If we do that, then Hibernate raises an exception and entire transaction gets rolled back on commit.

驱逐是 Hibernate 的一个特殊功能,它将实体从“托管”转换为“分离”。我们不能在分离的实体上调用持久化。如果我们这样做,那么 Hibernate 会引发异常并且整个事务在提交时回滚。

Merge vs Update

合并与更新

These are 2 interesting functions doing different stuff when dealt in different ways. Both of them are trying to transition the entity from "Detached" state to "Managed" state. But doing it differently.

这是 2 个有趣的函数,以不同的方式处理时会做不同的事情。他们都试图将实体从“分离”状态转换为“托管”状态。但做法不同。

Understand a fact that Detached means kind of an "offline" state. and managed means "Online" state.

了解一个事实,即分离意味着一种“离线”状态。和管理意味着“在线”状态。

Observe the code below:

观察下面的代码:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.merge(entity);

    ses1.delete(entity);

    tx1.commit();

When you do this? What do you think will happen? If you said this will raise exception, then you are correct. This will raise exception because, merge has worked on entity object, which is detached state. But it doesn't alter the state of object.

你什么时候这样做?你认为会发生什么?如果你说这会引发异常,那么你是对的。这将引发异常,因为合并已经处理了处于分离状态的实体对象。但它不会改变对象的状态。

Behind the scene, merge will raise a select query and basically returns a copy of entity which is in attached state. Observe the code below:

在幕后,merge 将引发一个选择查询并基本上返回一个处于附加状态的实体副本。观察下面的代码:

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();
    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    HibEntity copied = (HibEntity)ses1.merge(entity);
    ses1.delete(copied);

    tx1.commit();

The above sample works because merge has brought a new entity into the context which is in persisted state.

上述示例之所以有效,是因为合并已将一个新实体带入处于持久状态的上下文中。

When applied with Update the same works fine because update doesn't actually bring a copy of entity like merge.

当与更新一起应用时,同样的工作正常,因为更新实际上并没有带来像合并这样的实体副本。

Session ses1 = sessionFactory.openSession();

    Transaction tx1 = ses1.beginTransaction();

    HibEntity entity = getHibEntity();

    ses1.persist(entity);
    ses1.evict(entity);

    ses1.update(entity);

    ses1.delete(entity);

    tx1.commit();

At the same time in debug trace we can see that Update hasn't raised SQL query of select like merge.

同时在调试跟踪中我们可以看到更新没有像合并一样引发选择的 SQL 查询。

delete

删除

In the above example I used delete without talking about delete. Delete will basically transition the entity from managed state to "removed" state. And when flushed or commited will issue a delete command to store.

在上面的例子中,我使用了 delete 而没有谈到 delete。删除基本上会将实体从托管状态转换为“已删除”状态。并且在刷新或提交时会发出删除命令来存储。

However it is possible to bring the entity back to "managed" state from "removed" state using the persist method.

但是,可以使用persist 方法将实体从“已删除”状态恢复到“托管”状态。

Hope the above explanation clarified any doubts.

希望以上的解​​释能澄清大家的疑惑。

回答by Vlad Mihalcea

As I explained in this article, you should prefer the JPA methods most of the time, and the updatefor batch processing tasks.

正如我在本文中所解释的,大多数时候您应该更喜欢 JPA 方法,以及update批处理任务。

A JPA or Hibernate entity can be in one of the following four states:

JPA 或 Hibernate 实体可以处于以下四种状态之一:

  • Transient (New)
  • Managed (Persistent)
  • Detached
  • Removed (Deleted)
  • 瞬态(新)
  • 托管(持续)
  • 独立的
  • 移除(删除)

The transition from one state to the other is done via the EntityManager or Session methods.

从一种状态到另一种状态的转换是通过 EntityManager 或 Session 方法完成的。

For instance, the JPA EntityManagerprovides the following entity state transition methods.

例如,JPAEntityManager提供了以下实体状态转换方法。

enter image description here

在此处输入图片说明

The Hibernate Sessionimplements all the JPA EntityManagermethods and provides some additional entity state transition methods like save, saveOrUpdateand update.

HibernateSession实现了所有 JPAEntityManager方法并提供了一些额外的实体状态转换方法,如savesaveOrUpdateupdate

enter image description here

在此处输入图片说明

Persist

坚持

To change the state of an entity from Transient (New) to Managed (Persisted), we can use the persistmethod offered by the JPA EntityManagerwhich is also inherited by the Hibernate Session.

要将实体的状态从 Transient (New) 更改为 Managed (Persisted),我们可以使用persistJPA 提供的方法,该方法EntityManager也由 Hibernate 继承Session

The persistmethod triggers a PersistEventwhich is handled by the DefaultPersistEventListenerHibernate event listener.

persist方法触发PersistEventDefaultPersistEventListenerHibernate 事件侦听器处理的a 。

Therefore, when executing the following test case:

因此,在执行以下测试用例时:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    LOGGER.info(
        "Persisting the Book entity with the id: {}", 
        book.getId()
    );
});

Hibernate generates the following SQL statements:

Hibernate 生成以下 SQL 语句:

CALL NEXT VALUE FOR hibernate_sequence

-- Persisting the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

Notice that the idis assigned prior to attaching the Bookentity to the current Persistence Context. This is needed because the managed entities are stored in a Mapstructure where the key is formed by the entity type and its identifier and the value is the entity reference. This is the reason why the JPA EntityManagerand the Hibernate Sessionare known as the First-Level Cache.

请注意,在idBook实体附加到当前持久性上下文之前分配了 。这是必需的,因为托管实体存储在一个Map结构中,其中键由实体类型及其标识符构成,值是实体引用。这就是 JPAEntityManager和 HibernateSession被称为一级缓存的原因。

When calling persist, the entity is only attached to the currently running Persistence Context, and the INSERT can be postponed until the flushis called.

调用时persist,实体只附加到当前运行的持久化上下文,插入可以推迟到flush调用。

The only exception is the IDENTITY generatorwhich triggers the INSERT right away since that's the only way it can get the entity identifier. For this reason, Hibernate cannot batch inserts for entities using the IDENTITY generator. For more details about this topic, check out this article.

唯一的例外是IDENTITY 生成器,它立即触发 INSERT,因为这是获取实体标识符的唯一方法。因此,Hibernate 无法使用 IDENTITY 生成器批量插入实体。有关此主题的更多详细信息,请查看这篇文章

Save

节省

The Hibernate-specific savemethod predates JPA and it's been available since the beginning of the Hibernate project.

特定save于Hibernate 的方法早于 JPA,并且它自 Hibernate 项目开始就可用。

The savemethod triggers a SaveOrUpdateEventwhich is handled by the DefaultSaveOrUpdateEventListenerHibernate event listener. Therefore, the savemethod is equivalent to the updateand saveOrUpdatemethods.

save方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 事件侦听器处理的a 。因此,该save方法等价于updatesaveOrUpdate方法。

To see how the savemethod works, consider the following test case:

要查看该save方法的工作原理,请考虑以下测试用例:

doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);

    Long id = (Long) session.save(book);

    LOGGER.info(
        "Saving the Book entity with the id: {}", 
        id
    );
});

When running the test case above, Hibernate generates the following SQL statements:

在运行上面的测试用例时,Hibernate 会生成以下 SQL 语句:

CALL NEXT VALUE FOR hibernate_sequence

-- Saving the Book entity with the id: 1

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

As you can see, the outcome is identical to the persistmethod call. However, unlike persist, the savemethod returns the entity identifier.

如您所见,结果与persist方法调用相同。但是,与 不同的是persist,该save方法返回实体标识符。

For more details, check out this article.

有关更多详细信息,请查看这篇文章

Update

更新

The Hibernate-specific updatemethod is meant to bypass the dirty checking mechanismand force an entity update at the flush time.

特定update于Hibernate 的方法旨在绕过脏检查机制并在刷新时强制实体更新。

The updatemethod triggers a SaveOrUpdateEventwhich is handled by the DefaultSaveOrUpdateEventListenerHibernate event listener. Therefore, the updatemethod is equivalent to the saveand saveOrUpdatemethods.

update方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 事件侦听器处理的a 。因此,该update方法等价于savesaveOrUpdate方法。

To see how the updatemethod works consider the following example which persists a Bookentity in one transaction, then it modifies it while the entity is in the detached state, and it forces the SQL UPDATE using the updatemethod call.

要了解该update方法是如何工作的,请考虑以下示例,该示例将Book实体保留在一个事务中,然后在实体处于分离状态时对其进行修改,并使用update方法调用强制执行 SQL UPDATE 。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);

    LOGGER.info("Updating the Book entity");
});

When executing the test case above, Hibernate generates the following SQL statements:

在执行上面的测试用例时,Hibernate 会生成以下 SQL 语句:

CALL NEXT VALUE FOR hibernate_sequence

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity
-- Updating the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

Notice that the UPDATEis executed during the Persistence Context flush, right before commit, and that's why the Updating the Book entitymessage is logged first.

请注意,UPDATE在 Persistence Context 刷新期间,就在提交之前执行,这就是Updating the Book entity首先记录消息的原因。

Using @SelectBeforeUpdateto avoid unnecessary updates

使用@SelectBeforeUpdate以避免不必要的更新

Now, the UPDATE is always going to be executed even if the entity was not changed while in the detached state. To prevent this, you can use the @SelectBeforeUpdateHibernate annotation which will trigger a SELECTstatement that fetched loaded statewhich is then used by the dirty checking mechanism.

现在,即使实体在分离状态下未更改,也将始终执行 UPDATE。为了防止这种情况,您可以使用@SelectBeforeUpdateHibernate 注释,它将触发一条SELECT语句,loaded state该语句随后被脏检查机制使用。

So, if we annotate the Bookentity with the @SelectBeforeUpdateannotation:

所以,如果我们用注释来注释Book实体@SelectBeforeUpdate

@Entity(name = "Book")
@Table(name = "book")
@SelectBeforeUpdate
public class Book {

    //Code omitted for brevity
}

And execute the following test case:

并执行以下测试用例:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);

    session.update(_book);
});

Hibernate executes the following SQL statements:

Hibernate 执行以下 SQL 语句:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

Notice that, this time, there is no UPDATEexecuted since the Hibernate dirty checking mechanism has detected that the entity was not modified.

请注意,这次没有UPDATE执行,因为 Hibernate 脏检查机制检测到实体没有被修改。

SaveOrUpdate

保存或更新

The Hibernate-specific saveOrUpdatemethod is just an alias for saveand update.

特定于 Hibernate 的saveOrUpdate方法只是saveand的别名update

The saveOrUpdatemethod triggers a SaveOrUpdateEventwhich is handled by the DefaultSaveOrUpdateEventListenerHibernate event listener. Therefore, the updatemethod is equivalent to the saveand saveOrUpdatemethods.

saveOrUpdate方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate 事件侦听器处理的a 。因此,该update方法等价于savesaveOrUpdate方法。

Now, you can use saveOrUpdatewhen you want to persist an entity or to force an UPDATEas illustrated by the following example.

现在,您可以saveOrUpdate在想要持久化实体或强制使用时使用UPDATE,如下例所示。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle("High-Performance Java Persistence, 2nd edition");

doInJPA(entityManager -> {
    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(_book);
});

Beware of the NonUniqueObjectException

当心 NonUniqueObjectException

One problem that can occur with save, update, and saveOrUpdateis if the Persistence Context already contains an entity reference with the same id and of the same type as in the following example:

save, update, and可能出现的一个问题saveOrUpdate是,如果 Persistence Context 已经包含与以下示例中具有相同 id 和相同类型的实体引用:

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    Session session = entityManager.unwrap(Session.class);
    session.saveOrUpdate(book);

    return book;
});

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

try {
    doInJPA(entityManager -> {
        Book book = entityManager.find(
            Book.class, 
            _book.getId()
        );

        Session session = entityManager.unwrap(Session.class);
        session.saveOrUpdate(_book);
    });
} catch (NonUniqueObjectException e) {
    LOGGER.error(
        "The Persistence Context cannot hold " +
        "two representations of the same entity", 
        e
    );
}

Now, when executing the test case above, Hibernate is going to throw a NonUniqueObjectExceptionbecause the second EntityManageralready contains a Bookentity with the same identifier as the one we pass to update, and the Persistence Context cannot hold two representations of the same entity.

现在,在执行上面的测试用例时,Hibernate 将抛出 a,NonUniqueObjectException因为第二个EntityManager已经包含一个Book与我们传递给的具有相同标识符的实体update,并且持久化上下文不能保存同一实体的两个表示。

org.hibernate.NonUniqueObjectException: 
    A different object with the same identifier value was already associated with the session : [com.vladmihalcea.book.hpjp.hibernate.pc.Book#1]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:651)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:284)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:227)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:92)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:73)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:682)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:674)

Merge

合并

To avoid the NonUniqueObjectException, you need to use the mergemethod offered by the JPA EntityManagerand inherited by the Hibernate Sessionas well.

为了避免NonUniqueObjectException,您需要使用merge由 JPA 提供并由EntityManagerHibernate 继承的方法Session

As explained in this article, the mergefetches a new entity snapshot from the database if there is no entity reference found in the Persistence Context, and it copies the state of the detached entity passed to the mergemethod.

本文所述merge如果在持久性上下文中没有找到实体引用,它会从数据库中获取一个新的实体快照,并复制传递给该merge方法的分离实体的状态。

The mergemethod triggers a MergeEventwhich is handled by the DefaultMergeEventListenerHibernate event listener.

merge方法触发MergeEventDefaultMergeEventListenerHibernate 事件侦听器处理的a 。

To see how the mergemethod works consider the following example which persists a Bookentity in one transaction, then it modifies it while the entity is in the detached state, and pass the detached entity to mergein a subsequence Persistence Context.

要了解该merge方法的工作原理,请考虑以下示例,该示例Book在一个事务中持久化实体,然后在实体处于分离状态时修改它,并将分离的实体传递到子merge序列持久性上下文中。

Book _book = doInJPA(entityManager -> {
    Book book = new Book()
    .setIsbn("978-9730228236")
    .setTitle("High-Performance Java Persistence")
    .setAuthor("Vlad Mihalcea");

    entityManager.persist(book);

    return book;
});

LOGGER.info("Modifying the Book entity");

_book.setTitle(
    "High-Performance Java Persistence, 2nd edition"
);

doInJPA(entityManager -> {
    Book book = entityManager.merge(_book);

    LOGGER.info("Merging the Book entity");

    assertFalse(book == _book);
});

When running the test case above, Hibernate executed the following SQL statements:

在运行上面的测试用例时,Hibernate 执行了以下 SQL 语句:

INSERT INTO book (
    author, 
    isbn, 
    title, 
    id
) 
VALUES (
    'Vlad Mihalcea', 
    '978-9730228236', 
    'High-Performance Java Persistence', 
    1
)

-- Modifying the Book entity

SELECT 
    b.id,
    b.author AS author2_0_,
    b.isbn AS isbn3_0_,
    b.title AS title4_0_
FROM 
    book b
WHERE 
    b.id = 1

-- Merging the Book entity

UPDATE 
    book 
SET 
    author = 'Vlad Mihalcea', 
    isbn = '978-9730228236', 
    title = 'High-Performance Java Persistence, 2nd edition'
WHERE 
    id = 1

Notice that the entity reference returned by mergeis different than the detached one we passed to the mergemethod.

请注意,返回的实体引用merge与我们传递给merge方法的分离的引用不同。

Now, although you should prefer using JPA mergewhen copying the detached entity state, the extra SELECTcan be problematic when executing a batch processing task.

现在,尽管merge在复制分离的实体状态时您应该更喜欢使用 JPA,但SELECT在执行批处理任务时,额外的内容可能会出现问题。

For this reason, you should prefer using updatewhen you are sure that there is no entity reference already attached to the currently running Persistence Context and that the detached entity has been modified.

出于这个原因,update当您确定没有实体引用已经附加到当前运行的持久性上下文并且分离的实体已被修改时,您应该更喜欢使用。

For more details about this topic, check out this article.

有关此主题的更多详细信息,请查看这篇文章

Conclusion

结论

To persist an entity, you should use the JPA persistmethod. To copy the detached entity state, mergeshould be preferred. The updatemethod is useful for batch processing tasks only. The saveand saveOrUpdateare just aliases to updateand you should not probably use them at all.

要持久化实体,您应该使用 JPApersist方法。要复制分离的实体状态,merge应首选。该update方法仅对批处理任务有用。该savesaveOrUpdate只是别名update,并在所有你不应该可能使用它们。

Some developers call saveeven when the entity is already managed, but this is a mistake and triggers a redundant event since, for managed entities, the UPDATE is automatically handled at the Persistence context flush time.

save即使实体已经被管理,一些开发人员也会调用,但这是一个错误并触发一个冗余事件,因为对于被管理的实体,更新是在持久化上下文刷新时自动处理的。

For more details, check out this article.

有关更多详细信息,请查看这篇文章