是Hibernate批量更新

时间:2013-10-30 13:28:20

标签: java hibernate batch-updates

所以这是我的处女问题,所以在这里......我的想法是我正在为数据库表做大量的更新语句。在sql中它会很简单 update table_name set col1 = 123 where col2 = 456 and col1 is null 。由于有数百万这些,最好考虑将其中的一些批处理。我按照这里的指示:
http://docs.jboss.org/hibernate/orm/3.3/reference/en-US/html/batch.html
我在堆栈溢出和其他地方随机找到的其他一些页面,但它们都读得相似。 我的想法是不进行读取,而是直接进行更新并保持循环,如下所示:
update table_name set col1 = 123 where col2 = 456 and col1 is null

  sessionFactory = new Configuration().configure("finaldetail/hibernate.dev.cfg.xml") 
          .addPackage("com.xxx.app.ftm.domain.event").addAnnotatedClass(FinalTrainDetail.class) 
          .addAnnotatedClass(AbstractDetail.class).addAnnotatedClassFinalTrainDetailWork.class).buildSessionFactory();
      inputStream = new BufferedReader(new FileReader(new File(args[0])));
      session = sessionFactory.openSession();
      transaction = session.beginTransaction(); 
      String s; 
      int count = 0; 
      while ((s = inputStream.readLine()) != null) {
        Query query = session.createQuery("update FinalTrainDetail  detail set detail.trainSummary "
                + "=:summaryId where detail.trainDetail=:detailId and detail.trainSummary=null"); 
        query.setParameter("summaryId", new Long(s.substring(9, 18)));
        query.setParameter("detailId", new Long(s.substring(0, 9)));
        query.executeUpdate();
        count++; 
        if (count % 20 == 0) { 
          log.debug("should commit"); 
          session.flush(); 
          session.clear(); 
        } 
      } 
      transaction.commit();
      System.out.println("exit");
    } catch (IOException e) {
      transaction.rollback();
      log.error(e.toString());
    } catch (Throwable t) {
      System.out.print(t);
      log.error("exception caught during Updateing Offline", t);
      System.exit(2);
    } finally {
      if (inputStream != null)
        inputStream.close();
      session.close();
    }
所以这里的理解是flush将导致每20个更新提交,然后清除清空第一级缓存以避免OutOfMemory异常。

到目前为止,我有一个配置 sessionFactory = new Configuration().configure("finaldetail/hibernate.dev.cfg.xml") .addPackage("com.xxx.app.ftm.domain.event").addAnnotatedClass(FinalTrainDetail.class) .addAnnotatedClass(AbstractDetail.class).addAnnotatedClassFinalTrainDetailWork.class).buildSessionFactory(); inputStream = new BufferedReader(new FileReader(new File(args[0]))); session = sessionFactory.openSession(); transaction = session.beginTransaction(); String s; int count = 0; while ((s = inputStream.readLine()) != null) { Query query = session.createQuery("update FinalTrainDetail detail set detail.trainSummary " + "=:summaryId where detail.trainDetail=:detailId and detail.trainSummary=null"); query.setParameter("summaryId", new Long(s.substring(9, 18))); query.setParameter("detailId", new Long(s.substring(0, 9))); query.executeUpdate(); count++; if (count % 20 == 0) { log.debug("should commit"); session.flush(); session.clear(); } } transaction.commit(); System.out.println("exit"); } catch (IOException e) { transaction.rollback(); log.error(e.toString()); } catch (Throwable t) { System.out.print(t); log.error("exception caught during Updateing Offline", t); System.exit(2); } finally { if (inputStream != null) inputStream.close(); session.close(); }

显示sql已启用以进行调试。所以我没有看到或没有看到的是我在log4j中设置的时间

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>
  <session-factory>
    <!-- SQL dialect -->
    <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> 

    <!-- Database connection settings --> 
    <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property> 
    <property name="connection.url">jdbc:oracle:thin:@dev264.oracle.XXXX.com:1521:DEV264</property>
    <property name="connection.username">XXXX</property>
    <property name="connection.password">XXX</property>
    <property name="connection.shutdown">true</property>

    <!-- JDBC connection pool (use the built-in one) -->
    <property name="connection.pool_size">1</property> 

    <!-- Enable Hibernate's automatic session context management -->
    <property name="current_session_context_class">thread</property>

    <!-- Disable the second-level cache  --> 
    <property
     name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

    <!-- disable batching so HSQLDB will propagate errors correctly. -->
    <property name="jdbc.batch_size">20</property> 

    <!-- Echo all executed SQL to stdout -->
    <property name="show_sql">true</property> 

  </session-factory>
</hibernate-configuration>

<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> <hibernate-configuration> <session-factory> <!-- SQL dialect --> <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> <!-- Database connection settings --> <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property> <property name="connection.url">jdbc:oracle:thin:@dev264.oracle.XXXX.com:1521:DEV264</property> <property name="connection.username">XXXX</property> <property name="connection.password">XXX</property> <property name="connection.shutdown">true</property> <!-- JDBC connection pool (use the built-in one) --> <property name="connection.pool_size">1</property> <!-- Enable Hibernate's automatic session context management --> <property name="current_session_context_class">thread</property> <!-- Disable the second-level cache --> <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property> <!-- disable batching so HSQLDB will propagate errors correctly. --> <property name="jdbc.batch_size">20</property> <!-- Echo all executed SQL to stdout --> <property name="show_sql">true</property> </session-factory> </hibernate-configuration>

我只看到了

<logger name="org.hibernate.transaction">
    <level value="debug"/>
    <appender-ref ref="file"/>
    <appender-ref ref="console"/>
</logger>

<logger name="org.hibernate.transaction"> <level value="debug"/> <appender-ref ref="file"/> <appender-ref ref="console"/> </logger> 在日志文件的最后,当&#39; flush&#39;正在发生。所以我想知道的是,提交是否真的被每20个记录调用,以及我是否在内存中构建了太多的对象,并且当我运行时,我将生产OutOfMemory,因为我没有数十万个测试记录。

2 个答案:

答案 0 :(得分:1)

您让flush()commit()感到困惑。 flush()不提交交易。它所做的就是执行update和delete语句,在数据库中写入已在内存中,附加实体上应用的更改,以及尚未使用的更改。

在您的情况下,刷新和清除会话是无用的(但无害),因为您不会对内存应用任何更改,因此会话始终为空。

AFAIK,在每次迭代时创建一个新查询也没用。您可以一次又一次地重复使用相同的查询。 detail.trainSummary=null不正确。它应该是detail.trainSummary is null

答案 1 :(得分:0)

我会对你上面的答案发表评论,但字数过高......
好的,所以至少你没有发给我之前被问过的这个问题的链接,所以这让我觉得好一点,但我想我明白了你的观点。
所以这个应用程序的版本就像修订版3,很快就会有修订版本4.关于不要继续重新创建查询的点。我的原始版本读取(进入内存)然后调用了一个setter(更改内存中的对象)。在某个地方我明白了,跳过选择并进行更新。因此,如果a执行读取然后更改状态,则刷新和清除将变得必要。由于没有阅读,没有什么可以清除或冲洗。所以我无法在第一级缓存上耗尽内存。我真正担心的是在Oracle中使用了太多的程序全局区域(PGA)内存并占用了太多的撤消空间。我在这里读到了:
http://www.oracle.com/technetwork/issue-archive/2008/08-mar/o28plsql-095155.html所以每20到100次更新,不要在会话上调用flush,而是在事务上提交。另外,我应该监视从executeUpdate调用中更改的行数,而不是每次执行查询时都计数。所以我结束了这样的事情:

Query query = session.createQuery("update FinalTrainDetail  detail set detail.trainSummary "
      + "=:summaryId where detail.trainDetail=:detailId and detail.trainSummary=null");
  while ((s = inputStream.readLine()) != null) {
    transaction = session.beginTransaction();
    query.setParameter("summaryId", new Long(s.substring(9, 18)));
    query.setParameter("detailId", new Long(s.substring(0, 9)));
    count+=query.executeUpdate();
    if (count % 100 == 0) {
      log.debug("should commit");
      transaction.commit();
    }
  }
  transaction.commit();
  System.out.println("exit");

Query query = session.createQuery("update FinalTrainDetail detail set detail.trainSummary " + "=:summaryId where detail.trainDetail=:detailId and detail.trainSummary=null"); while ((s = inputStream.readLine()) != null) { transaction = session.beginTransaction(); query.setParameter("summaryId", new Long(s.substring(9, 18))); query.setParameter("detailId", new Long(s.substring(0, 9))); count+=query.executeUpdate(); if (count % 100 == 0) { log.debug("should commit"); transaction.commit(); } } transaction.commit(); System.out.println("exit");