ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

节省()

更新()

保存或更新()

保存或更新复制()

合并()

坚持()

这是作者本人 Vlad Mihalcea 迄今为止的 latest answers 之一。在挖掘了几个旧的文档线程、官方文档以及 Stack Overflow 上的许多变体之后,这是与片段一起最好的答案之一。此 link 还包含实体生命周期状态,以备不时之需。

L
Lee Theobald

这是我对这些方法的理解。主要是这些基于 API,因为我在实践中并没有使用所有这些。

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

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

update 尝试使用现有标识符来持久化实体。如果不存在标识符,我相信会引发异常。

saveOrUpdateCopy 这已被弃用,不应再使用。取而代之的是...

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

persist 如上所述,这用于瞬态对象。它不返回生成的 ID。


我想接受这个作为答案,但有一件事仍然不清楚:因为 save() 依赖于 update(),如果存在这样的项目,它在实践中与 saveOrUpdate() 有何不同?
如果您对合并/持久化的描述仅对瞬态对象很重要,那么这很有意义,并且符合我们使用休眠的方式。另请注意,与更新相比,合并通常具有性能限制,因为它似乎为某种完整性检查进行了额外的提取。
下面jrudolph的回答更准确。
鉴于hibernate可能知道对象处于哪个状态,为什么我们在编写程序时必须手动执行此操作。应该只有一种保存方法。
等等,“通过保存和更新,您正在处理持久对象”。这种说法不是很正确。我们可以在瞬态对象上调用 save ,然后会生成一个 id 并成为一个持久对象。
S
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()         ║
║              ║                               ║                                ║
╚══════════════╩═══════════════════════════════╩════════════════════════════════╝

update 瞬态对象很好,我没有得到异常。
据我所知,我们不能以任何一种方式坚持瞬态。我认为差异可能在超然和持久之间。请纠正我。
这里有很多错误......例如1)'save()'不返回“附加对象”,它返回'id'; 2) 'persist()' 不能保证设置 'id',也不能“将对象持久化到 DB”; ...
@EugenLabun 我不同意你的第二点,你可能想看看 Vlad Mihalcea 下面的 this answer。是的,persist 不会返回“Id”,但它确实会触发 INSERT。
E
Eugen Labun

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

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


如果您有自己的 Id 生成器,插入语句仍然不会发生
所以在分离对象的情况下,合并将触发选择而更新不会?
save() - If an INSERT has to be executed to get the identifier, then this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.您能告诉我插入如何在会话之外发生吗?为什么它不好?
免责声明:我已经很长时间没有使用hibernate了。 IMO 的问题是:save() 的签名和合同要求 save 返回新对象的标识符。根据您选择的 id 生成策略,标识符由数据库在值被 INSERTed 时生成。因此,在这些情况下,您不能在不生成标识符的情况下立即返回标识符,而要生成它,您必须INSERT 立即 运行。因为,长时间运行的事务现在不会运行,而只会在提交时运行,所以现在执行 INSERT 的唯一方法是在 tx.xml 之外运行它。
V
Vlad Mihalcea

大多数时候,您应该更喜欢 JPA 方法,而对于批处理任务,您应该更喜欢 update

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

瞬态(新)

托管(持久)

分离式

已移除(已删除)

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

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

https://i.stack.imgur.com/rJY2u.png

Hibernate Session 实现了所有 JPA EntityManager 方法并提供了一些额外的实体状态转换方法,例如 savesaveOrUpdateupdate

https://i.stack.imgur.com/NsKnR.png

坚持

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

persist 方法触发由 DefaultPersistEventListener Hibernate 事件侦听器处理的 PersistEvent。

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

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 生成以下 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
)

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

调用 persist 时,实体只附加到当前运行的 Persistence Context,INSERT 可以推迟到 flush 被调用。

唯一的例外是立即触发 INSERT 的 IDENTITY,因为这是它可以获取实体标识符的唯一方法。因此,Hibernate 无法使用 IDENTITY 生成器批量插入实体。

节省

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

save 方法触发一个 SaveOrUpdateEvent,它由 DefaultSaveOrUpdateEventListener Hibernate 事件监听器处理。因此,save 方法等价于 update 和 saveOrUpdate 方法。

要了解 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
    );
});

在运行上面的测试用例时,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
)

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

更新

特定于 Hibernate 的 update 方法旨在绕过 dirty checking mechanism 并在刷新时强制实体更新。

update 方法触发了一个由 DefaultSaveOrUpdateEventListener Hibernate 事件监听器处理的 SaveOrUpdateEvent。因此,update 方法等价于 save 和 saveOrUpdate 方法。

要了解 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");
});

在执行上述测试用例时,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

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

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

现在,即使实体在分离状态下没有更改,也会始终执行更新。为防止这种情况,您可以使用 @SelectBeforeUpdate Hibernate 注释,该注释将触发获取 loaded stateSELECT 语句,然后由脏检查机制使用。

因此,如果我们使用 @SelectBeforeUpdate 注释来注释 Book 实体:

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

    //Code omitted for brevity
}

并执行以下测试用例:

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 执行以下 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

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

保存或更新

Hibernate 特定的 saveOrUpdate 方法只是 saveupdate 的别名。

saveOrUpdate 方法触发由 DefaultSaveOrUpdateEventListener Hibernate 事件侦听器处理的 SaveOrUpdateEvent。因此,update 方法等价于 save 和 saveOrUpdate 方法。

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

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);
});

当心 NonUniqueObjectException

saveupdatesaveOrUpdate 可能出现的一个问题是,如果 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
    );
}

现在,当执行上面的测试用例时,Hibernate 将抛出一个 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)

合并

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

如果在持久性上下文中找不到实体引用,merge 会从数据库中获取新的实体快照,并将已分离实体的状态复制到 merge 方法。

merge 方法触发一个 MergeEvent,它由 DefaultMergeEventListener Hibernate 事件监听器处理。

要了解 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);
});

在运行上面的测试用例时,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

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

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

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

结论

要持久化实体,您应该使用 JPA persist 方法。要复制分离的实体状态,应该首选 mergeupdate 方法仅对批处理任务有用。 savesaveOrUpdate 只是 update 的别名,您根本不应该使用它们。

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


最后,来自大师本人的回答。在阅读了一些博客、官方文档、hibernate 文档中的旧线程以及 StackOverflow 上的多个变体以及关于保存、合并和持久化的多个变体之后,我变得更加困惑。从这种情况下清除你的概念和顾虑当然需要耐心。谢谢,弗拉德。不确定上面写的旧答案有一些误解和怀疑,但对于像我这样多年后阅读帖子的人来说,很难理解,有时就像大海捞针一样。
我很高兴能提供帮助并继续关注更多信息。
H
HakunaMatata

这个链接很好地解释了:

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();

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

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

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

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


链接到 webarchive 上的文章,因为原件不可用:web.archive.org/web/20160521091122/http://www.stevideter.com:80/…
D
Dr. Rajesh Rolen

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

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

long s = session.save(k);

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


O
OutOfMind

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

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

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

节省()

我们可以在事务之外调用这个方法。如果我们在没有事务的情况下使用它并且我们在实体之间有级联,那么除非我们刷新会话,否则只有主实体会被保存。

因此,如果从主对象映射了其他对象,它们会在提交事务或刷新会话时保存。

坚持()

它类似于在事务中使用 save() ,因此它是安全的并且可以处理任何级联对象。

保存或更新()

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

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

更新()

当我们知道我们只更新实体信息时,应该使用 Hibernate 更新。此操作将实体对象添加到持久上下文中,并在提交事务时跟踪和保存进一步的更改。

因此,即使在调用更新之后,如果我们在实体中设置任何值,它们也会在事务提交时更新。

合并()

Hibernate 合并可用于更新现有值,但是此方法从传递的实体对象创建一个副本并返回它。返回的对象是持久上下文的一部分并跟踪任何更改,不跟踪传递的对象。这是 merge() 与所有其他方法的主要区别。

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


b
bernardn

请注意,如果您对分离的对象调用更新,无论您是否更改了对象,数据库中总会有更新完成。如果这不是您想要的,您应该使用 Session.lock() 和 LockMode.None。

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


A
Anton Popovich

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


请告诉我们为什么以下答案不正确。
G
GingerBeer

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

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

永远不要使用休眠的保存方法。忘记它甚至存在于 HIBERNATE 中!

坚持

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

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

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

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

合并与更新

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

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

观察下面的代码:

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();

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

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

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();

上面的示例有效,因为合并将一个新实体带入了处于持久状态的上下文中。

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

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();

同时在调试跟踪中我们可以看到 Update 没有引发 select like merge 的 SQL 查询。

删除

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

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

希望以上解释能澄清任何疑问。