java 为什么在 Hibernate 中需要在 session.delete() 之后调用 session.flush()?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/30215081/
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-11-02 16:41:20  来源:igfitidea点击:

Why is it necessary to call session.flush() after session.delete() in Hibernate?

javaspringhibernate

提问by Gaurang Patel

The problem is following code snippet doesn't delete the record in DB.

问题是以下代码片段不会删除数据库中的记录。

import org.hibernate.Session;
import org.hibernate.SessionFactory;
...
...
void deleteForm() {
   Session session = sessionFactory.openSession();
   FormDO formDO = new FormDO();
   formDO.setId(formId);
   session.delete(formDO); // No delete SQL query is getting fired. 

However, if I call session.flush()after delete, it works perfectly. Please note, I am NOT using any Transaction.

但是,如果我在删除后调用session.flush(),它就可以完美运行。请注意,我没有使用任何交易。

In JavaDoc of Session class the description of delete method is:

在Session类的JavaDoc中delete方法的描述是:

Remove a persistent instance from the datastore. The argument may be an instance associated with the receiving Session or a transient instancewith an identifier associated with existing persistent state.

从数据存储中删除持久性实例。参数可以是与接收会话相关联的实例或具有与现有持久状态相关联的标识符的瞬态实例

And I have seen many code snippets online which shows that it is not necessary to call flush() after delete(). Similar question was raised in other forum herebut it remained unanswered.

而且我在网上看到很多代码片段表明在delete()之后没有必要调用flush()。类似的问题在其他论坛here中提出,但仍未得到答复。

Also, session.save works fine without session.flush.

此外,session.save 在没有 session.flush 的情况下也能正常工作。

I am using Hibernate 4.2.16 + Spring 4.0.9 + JPA 1.0 annotations. Following are the source files for further reference,

我正在使用 Hibernate 4.2.16 + Spring 4.0.9 + JPA 1.0 注释。以下是进一步参考的源文件,

FormDO.java

表单DO.java

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="form")
    public class FormDO {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    @Column(name="id")
    Integer id;
    @Column(name="name")
    String name;
    ...
    ...

Spring Configuration File

弹簧配置文件

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/tempdb" />
        <property name="username" value="root" />
        <property name="password" value="****" />
    </bean>
    <!-- Hibernate 4 SessionFactory Bean definition -->
    <bean id="hibernate4AnnotatedSessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean" >
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>com.test.FormDO</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.current_session_context_class">thread</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

FormDAO.java

FormDAO.java

@Named
public class FormDAO {
    @Inject
    private SessionFactory sessionFactory;

    public boolean deleteForm(Integer formId) {
        Session session = sessionFactory.openSession();
        FormDO formDO = new FormDO();
        formDO.setId(formId);
        session.delete(formDO);
        session.flush(); // If this line is commented, record DOES NOT get deleted
        return true;
    }
    public boolean saveForm(FormDO formDO) { 
       Session session = sessionFactory.openSession();
       session.save(formDO); // Save doesn't require session.flush
       return true;
    }
    ...
    ...

UPDATE:

更新:

My dilemma was largely due to the inconsistency.. session.save was inserting the record immediately but session.delete doesn't reflect unless flush() was called explicitly. But when I refer the Flushing the Sessionlink posted by Afsun, my doubts were cleared by reading following line,

我的困境主要是由于不一致...... session.save 正在立即插入记录,但 session.delete 不会反映除非明确调用 flush() 。但是当我参考Afsun 发布的Flushing the Session链接时,通过阅读以下行,我的疑虑被清除了,

An exception is that objects using native ID generation are inserted when they are saved.

一个例外是使用本机 ID 生成的对象在保存时被插入。

I really appreciate the answers posted by everyone as almost all of them were pointing to the right direction but Afsun clears my doubt totally. Thanks!

我非常感谢每个人发布的答案,因为几乎所有人都指出了正确的方向,但 Afsun 完全消除了我的疑虑。谢谢!

采纳答案by Afsun Khammadli

When your work with database via Hibernate you are using Hibernate session. Hibernate sessionsflushed to the database by following three situations.

当您通过 Hibernate 使用数据库时,您使用的是Hibernate session. Hibernate sessions通过以下三种情况刷新到数据库。

  1. commit()- When you commit a transaction
  2. Before you run a query
  3. When you call session.flush()
  1. commit()- 当您提交交易时
  2. 运行查询之前
  3. 你打电话的时候 session.flush()

Here the most important is second. After every query does not Hibernate sessionflush database. If we run Native SQL Queryvia Hibernate,Hibernate does not know to flush the session or also if run HQLalso Hibernate does not know to flush session. The call to flush will synchronise the session state with the database.

See the following: Hibernate flush before deleteand Flushing the Session

这里最重要的是第二个。每次查询后不Hibernate session刷新数据库。如果我们Native SQL Query通过 Hibernate运行,Hibernate 不知道刷新会话,或者如果运行HQLHibernate 也不知道刷新会话。对flush 的调用将使会话状态与数据库同步。

请参阅以下内容: 删除刷新会话之前的休眠刷新

回答by Tim Biegeleisen

From the Javadocfor the Hibernate Session class:

来自Hibernate Session 类的Javadoc

Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.

刷新是将底层持久存储与内存中保持的持久状态同步的过程。

When you make your call to session.delete()you tell Hibernate to remove the entity from being managed. However, the corresponding record will still exist in the underlying database. You need to make the call to session.flush()in order to sync the database with the Hibernate session. Note that if your program were to end without your calling session.flush()then next time you start up the app again the entity in question would reappear.

当您打电话给session.delete()您时,您告诉 Hibernate 将实体从管理中移除。但是,相应的记录仍将存在于底层数据库中。您需要调用 以session.flush()将数据库与 Hibernate 会话同步。请注意,如果您的程序在没有您调用的情况session.flush()下结束,那么下次您再次启动应用程序时,相关实体将重新出现。

回答by Viraj Nalawade

flush()This method forces current session to flush. Must be called at the end of a unit of work, before commiting the transaction and closing the session (depending on flush-mode, Transaction.commit() calls this method). Flushing is the process of synchronizing the underlying persistent store with persistable state held in memory.

flush()此方法强制当前会话刷新。必须在工作单元结束时,在提交事务和关闭会话之前调用(取决于刷新模式,Transaction.commit() 调用此方法)。刷新是将底层持久存储与内存中保持的持久状态同步的过程。

Therefore it is necessary flush the session when you call delete or else the deleted record will still exist in the underlying database.Also you are not closing the sessions you are opening.This may be issue when you start using pooling mechanism. The best practice is in most cases session-per-request.Closing a session will always flush al work to the database.
Also you need to flush in batch processing otherwise it may give OutOfMemoryException.

因此,当您调用 delete 时有必要刷新会话,否则被删除的记录将仍然存在于底层数据库中。此外,您没有关闭正在打开的会话。当您开始使用池机制时,这可能是问题。在大多数情况下,最佳实践是 session-per-request。关闭会话将始终将所有工作刷新到数据库中。
您还需要在批处理中刷新,否则可能会出现 OutOfMemoryException。