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


199

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

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

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

Answers:


117

这是我对方法的理解。这些主要是基于API尽管在实践中我并未全部使用。

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

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

更新 尝试使用现有标识符持久化实体。如果不存在标识符,则认为会引发异常。

saveOrUpdateCopy 已过时,不应再使用。相反,有...

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

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


22
我想接受此作为答案,但还有一件事尚不清楚:由于save()依赖于update(),因此,如果存在此项,实际上它与saveOrUpdate()有何不同?
亨里克·保罗

在哪里指定,该保存将对分离的实例起作用?
jrudolph

2
如果您对merge / persist的描述仅对瞬态对象很重要,那么这很有意义,并且符合我们使用休眠的方式。还应注意,与更新相比,合并通常具有性能限制,因为它似乎为某种形式的完整性检查进行了额外的提取。
Martin Dale Lyness

1
以下jrudolph的答案更为准确。
azerole 2012年

2
鉴于休眠可能知道对象处于哪个状态,为什么在编写程序时我们必须手动执行此操作。应该只有一种保存方法。
masterxilo 2014年

116
╔══════════════╦═══════════════════════════════╦════════════════════════════════╗
    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临时对象很好,但我没有例外。
GMsoF

我所知道的,我们不能以任何一种方式保持瞬态。我认为分离和持久之间可能存在差异。请纠正我。
拉姆

这里有很多错误...例如1)save()不返回“附加对象”,而是返回“ id”;2)不能保证“ persist()”设置“ id”,也不能“持久地反对数据库”。...
Eugen Labun '19

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

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


如果您拥有自己的ID生成器,insert语句仍然不会发生
kommradHomer 2012年

因此,在分离对象的情况下,合并将触发选择,而更新不会?
加布2014年

1
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.能否请您告诉我,如何在会话外进行插入,为什么这样做不好?
Erran Morad 2014年

免责声明:我已经很长时间没有使用过休眠了。IMO的问题是这样的:save()的签名和约定要求save返回新对象的标识符。根据您选择的ID生成策略,当INSERTed 值被使用时,标识符由数据库生成。因此,在这种情况下,你不能现在无需产生它返回一个标识符,并生成它,你必须运行INSERT 现在。因为,长时间运行的事务不会运行,现在却只能在提交时,执行的唯一方法INSERT是现在的TX之外运行。
jrudolph 2014年

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

要了解导致此异常的原因,了解分离的对象以及对分离的对象调用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返回对实例的最新版本的引用。它不是将项目重新附加到会话。如果测试实例是否相等(item == item3),在这种情况下,您会发现它返回false。从现在开始,您可能要使用item3。

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

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

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


链接到
Webarchive

5

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

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

long s = session.save(k);

在这种情况下,persist()永远不会将任何价值返还给客户。


5

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

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

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

保存()

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

坚持()

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

saveOrUpdate()

  • 可以与事务一起使用,也可以不与事务一起使用,就像save()一样,如果在不使用事务的情况下使用它,则映射的实体将不被保存;我们刷新会话。

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

update()

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

合并()

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

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


3

正如我在本文中所解释的,大多数情况下,您应该首选JPA方法,并且update批处理任务。

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

  • 瞬态(新)
  • 托管(永久)
  • 独立式
  • 已删除(已删除)

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

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

在此处输入图片说明

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

在此处输入图片说明

坚持

要将实体的状态从“瞬态”(“新”)更改为“托管”(“持久”),我们可以使用persistJPA提供的方法,该方法EntityManager也由Hibernate继承Session

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

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

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
)

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

打电话时 persist,实体仅附加到当前正在运行的Persistence Context,并且INSERT可以推迟到flush调用之前。

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

保存

特定save于Hibernate的方法早于JPA,自Hibernate项目开始以来就一直可用。

save方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate事件侦听器处理的。因此,该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
    );
});

当运行上述测试用例时,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方法调用相同。但是,与不同persistsave方法方法返回实体标识符。

有关更多详细信息,请参阅本文

更新资料

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

update方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate事件侦听器处理的。因此,该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");
});

在执行上述测试用例时,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以避免不必要的更新

现在,即使实体处于分离状态时未更改,也始终将执行UPDATE。为了防止这种情况,您可以使用@SelectBeforeUpdateHibernate批注,该批注将触发SELECT获取的语句loaded state然后由脏检查机制使用。

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

@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方法触发SaveOrUpdateEventDefaultSaveOrUpdateEventListenerHibernate事件侦听器处理的。因此,该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);
});

当心 NonUniqueObjectException

能与发生的一个问题saveupdatesaveOrUpdate是如果持久性上下文已经包含具有相同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将抛出a,NonUniqueObjectException因为第二个EntityManager已经包含一个Book具有与我们传递给它的标识符相同的标识符的实体update,并且Persistence Context无法容纳同一实体的两个表示形式。

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,您需要使用mergeJPA提供的方法以及EntityManagerHibernate继承的方法Session

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

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

要了解该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方法的分离引用不同。

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

因此,您应该优先使用 update当您确定当前运行的持久性上下文中没有附加任何实体引用并且分离的实体已被修改时。

有关此主题的更多详细信息,请参阅本文

结论

要保留实体,您应该使用JPA persist方法。要复制独立实体状态,merge应该是首选。该update方法仅对批处理任务有用。该savesaveOrUpdate只是别名update,并在所有你不应该可能使用它们。

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

有关更多详细信息,请参阅本文


2

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

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



0

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

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

切勿使用HIBERNATE的保存方法。忘了它甚至还存在吗!

坚持

正如大家所解释的,Persist基本上将实体从“瞬态”状态转换为“托管”状态。此时,slush或commit可以创建一个insert语句。但是实体仍将保持“托管”状态。同花顺不会改变。

在这一点上,如果您再次“坚持”,将没有任何改变。如果我们尝试保留一个持久化的实体,将不会再有任何节省。

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

逐出是Hibernate的特殊功能,它将使实体从“托管”过渡到“独立”。我们不能对分离的实体调用持久化。如果这样做,那么Hibernate会引发异常,并且整个事务都会在提交时回滚。

合并与更新

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

了解一个事实,即“分离”意味着某种“离线”状态。并且托管表示“在线”状态。

观察下面的代码:

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

什么时候做的?您认为会发生什么?如果您说这将引发异常,那么您是正确的。这将引发异常,因为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();

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

当与Update一起应用时,相同效果很好,因为update实际上不会带来实体合并之类的副本。

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之类的SQL查询,例如merge。

删除

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.