Является ли пакетное обновление Hibernate
Итак, это мой первый вопрос, так что вот оно... Идея в том, что я делаю большое количество операторов обновления для таблицы базы данных. В 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
и несколько других страниц, которые я случайно нашел здесь на stackru и других местах, но все они читаются одинаково. Моя идея состояла в том, чтобы не читать, а просто обновлять и продолжать цикл следующим образом:
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(); }
Таким образом, понимание здесь заключается в том, что сброс приведет к фиксации каждые 20 обновлений, а затем очистит кэш первого уровня, чтобы избежать исключения OutOfMemory.
Пока конфиг у меня есть
<?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>
Show sql включен в целях отладки. Так что я не получаю или не вижу, когда я устанавливаю в log4j
<logger name="org.hibernate.transaction"> <level value="debug"/> <appender-ref ref="file"/> <appender-ref ref="console"/> </logger>
Я вижу только
[DEBUG] [main] [org.hibernate.transaction.JDBCTransaction] [commit] [DEBUG] [main] [org.hibernate.transaction.JDBCTransaction] [commit] [DEBUG] [main] [org.hibernate.transaction.JDBCTransaction] [commit]
в самом конце файла журнала и не происходит, когда происходит сброс. Так что меня интересует, действительно ли коммит вызывается каждые 20 записей и собираю ли я слишком много объектов в памяти и получу ли я OutOfMemory в производственном процессе, когда это произойдет, если у меня нет сотен тысяч тестовых записей,
2 ответа
Ты путаешь flush()
а также commit()
, flush()
не совершает транзакцию. Все, что он делает - выполняет операторы update и delete для записи в базу данных изменений, которые были применены в памяти, к присоединенным объектам, и которые еще не были сделаны постоянными.
В вашем случае очистка и очистка сеанса бесполезны (но безвредны), так как вы не применяете никаких изменений в памяти, и, таким образом, сеанс всегда пуст.
AFAIK, создание нового запроса на каждой итерации также бесполезно. Вы можете использовать один и тот же запрос снова и снова. А также detail.trainSummary=null
это неверно. Так должно быть detail.trainSummary is null
,
Я бы прокомментировал ваш ответ выше, но количество слов слишком велико...
Хорошо, так что, по крайней мере, вы не отправили мне ссылку на этот вопрос, который мне задавали ранее, так что я чувствую себя немного лучше, но я думаю, что понимаю вашу точку зрения
Таким образом, эта версия приложения была похожа на 3-ю версию, и скоро будет 4-я версия. Мы не будем повторять запрос. Моя оригинальная версия произвела чтение (в память), затем вызвала сеттер (изменение объекта в памяти). Где-то у меня появилась идея, пропустить выбор и просто обновить. Таким образом, если 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");