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

Hen*_*aul 197 java persistence hibernate

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

到目前为止我发现的方法是:

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

Lee*_*ald 117

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

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

保存 持有实体.如果不存在标识符,将分配标识符.如果有的话,它实质上是在进行更新.返回生成的实体ID.

update 尝试使用现有标识符持久保存实体.如果不存在标识符,我认为会抛出异常.

saveOrUpdateCopy 这是不推荐使用的,不应再使用.而是......

合并 现在这是我的知识开始蹒跚的地方.这里重要的是瞬态,分离和持久实体之间的区别.有关对象状态的更多信息,请查看此处.通过保存和更新,您将处理持久对象.它们链接到一个Session,因此Hibernate知道发生了什么变化.但是当你有一个瞬态对象时,就没有涉及会话.在这些情况下,您需要使用merge进行更新并保留以进行保存.

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

  • 我想接受这个作为答案,但有一点仍然不清楚:因为save()回退到update(),如果这样的项目存在,它在实践中与saveOrUpdate()有什么不同? (22认同)
  • 如果你对merge/persist的描述只对瞬态对象有重要意义,那么这很有意义并且适合我们如何使用hibernate.另请注意,与更新相比,合并通常具有性能限制,因为它似乎需要额外获取某种类型的完整性检查. (2认同)
  • 鉴于hibernate可能知道对象所处的状态,为什么我们必须在编写程序时手动执行此操作.应该只有一种保存方法. (2认同)

Ser*_*hyk 114

?????????????????????????????????????????????????????????????????????????????????
?    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()         ?
?              ?                               ?                                ?
?????????????????????????????????????????????????????????????????????????????????
Run Code Online (Sandbox Code Playgroud)


jru*_*lph 66

  • 有关持久化和保存之间细微差别的解释,请参阅Hibernate论坛.看起来不同的是INSERT语句最终执行的时间.由于save确实返回了标识符,因此无论事务的状态如何(通常都是坏事),INSERT语句都必须立即执行.Persist不会为了分配标识符而在当前运行的事务之外执行任何语句.保存/保持两者都可以处理瞬态实例,即尚未分配标识符的实例,因此不会保存在数据库中.

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


Vla*_*cea 16

大多数情况下,您应该更喜欢 JPA 方法,以及update批处理任务。

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

  • 瞬态(新)
  • 托管(持续)
  • 独立的
  • 移除(删除)

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

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

JPA 实体状态转换

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

Hibernate 实体状态转换

坚持

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

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

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

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()
    );
});
Run Code Online (Sandbox Code Playgroud)

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
)
Run Code Online (Sandbox Code Playgroud)

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

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

唯一的例外是立即IDENTITY触发 INSERT ,因为这是获取实体标识符的唯一方法。出于这个原因,Hibernate 不能使用IDENTITY生成器批量插入实体。

节省

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

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

要查看该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
    );
});
Run Code Online (Sandbox Code Playgroud)

在运行上面的测试用例时,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
)
Run Code Online (Sandbox Code Playgroud)

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

更新

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

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

要了解该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");
});
Run Code Online (Sandbox Code Playgroud)

在执行上面的测试用例时,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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

    //Code omitted for brevity
}
Run Code Online (Sandbox Code Playgroud)

并执行以下测试用例:

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);
});
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

保存或更新

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

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

现在,您可以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);
});
Run Code Online (Sandbox Code Playgroud)

当心 NonUniqueObjectException

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
    );
}
Run Code Online (Sandbox Code Playgroud)

现在,当执行上面的测试用例时,Hibernate 将抛出 aNonUniqueObjectException因为第二个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)
Run Code Online (Sandbox Code Playgroud)

合并

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

merge获取从数据库中一个新的实体快照如果在持久化上下文中没有发现实体引用,将剥离实体它复制状态传递给merge方法。

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

要了解该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);
});
Run Code Online (Sandbox Code Playgroud)

在运行上面的测试用例时,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
Run Code Online (Sandbox Code Playgroud)

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

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

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

结论

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

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

  • 最后还是大师亲自解答。在阅读了一些博客、官方文档、hibernate 文档中的旧线程以及 StackOverflow 上关于保存、合并和持久的多个变体后,我变得更加困惑。从这种背景下理清你的概念和担忧确实需要耐心。谢谢,弗拉德。不确定上面写的旧答案有一些误解和疑问,但对于像我这样多年后阅读帖子的人来说,很难理解,有时感觉就像大海捞针。 (2认同)

Hak*_*ata 12

此链接以良好的方式解释:

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();
Run Code Online (Sandbox Code Playgroud)

要了解此异常的原因,了解分离的对象以及在分离的对象上调用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();
Run Code Online (Sandbox Code Playgroud)

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

值得注意的是,Java Persistence API(JPA)没有分离和重新附加对象的概念,并使用EntityManager.persist()和EntityManager.merge().

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

你在哪里遇到过这个问题?合并是否适用于您,或者您是否需要其他解决方案?您是否希望始终使用合并,或者仅在特定情况下根据需要使用合并


小智 5

实际上,hibernate save()persist()方法之间的区别取决于我们正在使用的生成器类.

如果我们的生成器类被分配,那么save()persist(方法之间没有区别.因为生成器'已分配'意味着,作为程序员,我们需要将主键值保存在数据库中[希望您知道这个生成器概念]如果不是指定的生成器类,假设我们的生成器类名称是递增均值hibernate it self会将主键id值分配到数据库中[除了分配的生成器,hibernate只用于照顾主键id值记住],所以在这种情况下如果我们调用save()或者persist()方法那么它会将记录插入正常数据库但是听到的是, save()方法可以返回由hibernate生成的主键id值,我们可以看到它

long s = session.save(k);
Run Code Online (Sandbox Code Playgroud)

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


Out*_*ind 5

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

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

总之,根据以上链接:

保存()

  • 我们可以在事务外调用此方法.如果我们在没有事务的情况下使用它,并且我们在实体之间进行级联,那么除非我们刷新会话,否则只保存主实体.
  • 因此,如果从主对象映射了其他对象,则会在提交事务或刷新会话时保存它们.

坚持()

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

saveOrUpdate()方法

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

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

更新()

  • 当我们知道我们只更新实体信息时,应该使用Hibernate更新.此操作将实体对象添加到持久上下文,并在提交事务时跟踪和保存进一步的更改.
  • 因此,即使在调用update之后,如果我们在实体中设置任何值,它们将在事务提交时更新.

合并()

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

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