【问题标题】:Why is it necessary to call session.flush() after session.delete() in Hibernate?为什么在 Hibernate 中 session.delete() 之后需要调用 session.flush()?
【发布时间】:2015-07-24 18:08:41
【问题描述】:

问题是下面的代码 sn -p 没有删除数据库中的记录。

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. 

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

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

从数据存储中删除一个持久实例。该参数可以是与接收会话相关联的实例,也可以是瞬态实例,其标识符与现有持久状态相关联。

而且我在网上看到很多sn-ps的代码,说明在delete()之后不需要调用flush()。其他论坛here 也提出了类似的问题,但仍未得到解答。

此外,没有 session.flush,session.save 也能正常工作。

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

FormDO.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 配置文件

    <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

@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;
    }
    ...
    ...

更新:

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

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

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

【问题讨论】:

    标签: java spring hibernate


    【解决方案1】:

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

    1. commit()- 提交事务时
    2. 运行查询之前
    3. 当您致电session.flush()

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

    请参阅以下内容: Hibernate flush before delete 和 Flushing the Session

    【讨论】:

      【解决方案2】:

      来自 Hibernate Session 类的 Javadoc:

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

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

        • 感谢您指出 session.close。因为我刚刚开始编写这个新代码,所以我错过了它。但无论如何,当您说“关闭会话将始终将所有工作刷新到数据库”时,我不同意您的看法。我试过了,但没有。请参考以下网址,stackoverflow.com/questions/3931162/…
        • 感谢我对 close() 调用 flush() 的假设是错误的,是的,它是关闭会话的好习惯。
        猜你喜欢
        • 2011-03-14
        • 2011-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-30
        相关资源
        最近更新 更多