【问题标题】:Question about Hibernate session.flush()关于 Hibernate session.flush() 的问题
【发布时间】:2011-03-17 12:58:14
【问题描述】:

我想询问一下flush方法在以下情况下的实际作用:

for (int i = 0; i < myList.size(); i++) {
    Car c = new Car( car.get(i).getId(),car.get(i).getName() );
    getCurrentSession().save(c);
    if (i % 20 == 0)
        getCurrentSession().flush();
}

这是否意味着在迭代20次之后,缓存被刷新,然后这20个持有的内存对象才真正保存到数据库中?

有人可以向我解释一下条件为真时会发生什么。

【问题讨论】:

  • 所以没有人真正回答这个问题:(数据)实际上保存在数据库中吗?...

标签: java hibernate orm


【解决方案1】:

取决于 FlushMode 的设置方式。

在默认配置中,Hibernate 尝试在三个位置与数据库同步。

1. before querying data
2. on commiting a transaction
3. explictly calling flush

如果FlushMode设置为FlushMode.Manual,程序员通知hibernate他/她将处理何时将数据传递到数据库。在此配置下 session.flush() 调用会将对象实例保存到数据库中。

实际上可以使用session.clear() 调用来清除持久性上下文。

【讨论】:

  • 它不依赖于刷新模式,显式调用flush() 将显式刷新。期间。
  • 不是暗示 FlushMode 控制 flush()。试图暗示如果 FlushMode 设置为 Manual,则在查询或提交事务时休眠不会同步。
  • 好的,但除非我误解了这个问题,否则这不是问题的主体。
【解决方案2】:

来自Session#flush的javadoc:

强制此会话刷新。一定是 在工作单元结束时调用, 在提交交易之前和 关闭会话(取决于 flush-mode, Transaction.commit() 调用此方法)。

刷新是同步底层的过程 具有持久性的持久性存储 保存在内存中的状态。

换句话说,flush 告诉 Hibernate 执行同步 JDBC 连接状态与会话级缓存中保存的对象状态所需的 SQL 语句。并且条件if (i % 20 == 0) 将使每个i 20 的倍数发生。

但是,新的Car 实例仍将保存在会话级缓存中,对于大型myList.size(),您将占用所有内存并最终获得OutOfMemoryException。为避免这种情况,文档中描述的模式是定期flush AND clear 会话(与 JDBC 批处理大小相同)以保留更改,然后分离实例以便它们可以被垃圾收集:

13.1. Batch inserts

当使新对象持久化时 flush() 然后 clear() 会话 定期以控制大小 一级缓存。

Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();

for ( int i=0; i<100000; i++ ) {
    Customer customer = new Customer(.....);
    session.save(customer);
    if ( i % 20 == 0 ) { //20, same as the JDBC batch size
        //flush a batch of inserts and release memory:
        session.flush();
        session.clear();
    }
}

tx.commit();
session.close();

文档在同一章节中提到了如何设置 JDBC 批量大小。

另见

【讨论】:

  • 你救了我。我在批量插入时正在解决这个问题。谢谢+1
  • ***我们是否需要 session.clear();在每个 session.flush(); 之后?我理解是对的谢谢我得到了她的答案stackoverflow.com/questions/7757244/…
  • 解释得很漂亮 - 参考了 doco 和示例来澄清。谢谢!
【解决方案3】:
// Assume List to be of 50 
for (int i = 0; i < 50 ; i++) {
        Car c = new Car( car.get(i).getId(),car.get(i).getName() );
        getCurrentSession().save(c);
    // 20 car Objects which are saved in memory syncronizes with DB 
        if (i % 20 == 0)
            getCurrentSession().flush();

}

关于为什么刷新应该匹配批量大小的更多指针 要启用批处理,您需要设置 jdbc 批处理大小

// In your case 
hibernate.jdbc.batch_size =20

使用批处理的一个常见缺陷是,如果您使用单个对象更新或插入,这很好。但是以防万一 您正在使用导致多个插入/更新的多个对象,那么您将必须显式设置排序机制。

例如

// Assume List to be of 50 
    for (int i = 0; i < 50 ; i++) {
            Car c = new Car( car.get(i).getId(),car.get(i).getName() );
        // Adding accessory also in the card here
            Accessories a=new Accessories("I am new one");
            c.add(a);
        // Now you got two entities to be persisted . car and accessory 
        // Two SQL inserts 
            getCurrentSession().save(c);
        // 20 car Objects which are saved in memory syncronizes with DB 
        // Flush here clears the car objects from 1st level JVM cache
            if (i % 20 == 0)
            getCurrentSession().flush();
                    getCurrentSession().clear();
    }

在这种情况下 生成两条sql 1 用于插入汽车 1 用于插入配件

为了正确的批处理,您必须设置

<prop  key="hibernate.order_inserts">true</prop>

这样汽车的所有插件都被排序在一起,附件的所有插件也被排序在一起。这样一来,您将有 20 个插件批量触发,而不是一次触发 1 个 sql。

一个事务下的不同操作,你可以看看http://docs.jboss.org/hibernate/core/3.2/api/org/hibernate/event/def/AbstractFlushingEventListener.html

【讨论】:

  • 这是不正确的,flush 确实从一级缓存中清除对象。
  • 是的帕斯卡,我错过了在我的例子中用冲洗清除。相应地更新了它
【解决方案4】:

是的,每 20 次循环,为未保存的对象生成并执行 sql。您还应该将批处理模式设置为 20 以提高性能。

【讨论】:

  • 请给我更多关于设置批处理模式的细节。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-16
  • 2011-03-14
  • 1970-01-01
相关资源
最近更新 更多