Пакетная вставка Hibernate в однонаправленную коллекцию «один ко многим»

У меня есть класс ItemList, содержащий набор Items, отображаемых следующим образом:

<class name="ItemList">
   <id name="PID" column="PID">
      <generator class="uuid" />
   </id>
   <version name="Version" />
   <set cascaed="save-update" name="Items">
      <key column="itemlist_id" />
      <one-to-many class="Item" />
   </set>
</class>

<class name="Item">
   <id name="PID" column="PID">
      <generator class="uuid" />
   </id>
   ...
</class>

Это односторонняя ассоциация. Мне нужно пакетно вставить 1000 Items в ItemList. Используя документ hibernate для пакетных вставок, у меня есть что-то вроде этого:

ItemList itemList = ...

int i = 0;
for (Item item : someItems) {
    // .. some processing..

    itemList.getItems().add(item);

    if (++i % 30 == 0) {
       session.update(itemList);
       session.flush();
       session.clear();
    }
}

С ним явно какие-то проблемы. При каждом сбросе вставляется 30 предметов. Затем обновляется версия ItemList. Затем все 30 элементов обновляются с помощью PID ItemList.

  1. Как избежать увеличения версии ItemList после каждой партии?
  2. Как я могу избежать 30 обновлений?

person takteek    schedule 26.06.2012    source источник


Ответы (1)


  1. Извините, я не могу помочь вам с этим (но какое вам дело?).

  2. Вы имеете в виду обновление каждые 30 пунктов? Вы тоже не хотите ... если вы не готовы рискнуть нехваткой памяти, поскольку все тысячи Item хранятся в кеше уровня сеанса.

  3. Это работает, потому что, как указано в документации из Session.flush() утверждает:

Очистка – это процесс синхронизации базового постоянного хранилища с сохраняемым состоянием, хранящимся в памяти.

Итак, после сброса вы закончили с последними 30 элементами, они находятся на стороне БД. Однако это не означает, что они зафиксированы. БД знает, что вы находитесь в середине транзакции, которая еще не была зафиксирована.

ИЗМЕНИТЬ

Может быть, теперь я понимаю, почему версия увеличивается после каждого flush. Я тут рассуждаю. Hibernate использует столбец version, чтобы убедиться, что с момента запуска транзакции ни одна другая транзакция не изменила сущность, которая была изменена текущей транзакцией. Но эта проверка выполняется исключительно от имени Hibernate, без использования для этого каких-либо встроенных функций БД. Я предполагаю, что это правда, потому что DDL таблиц версий показывает, что столбец version является простым int (это также имеет смысл, поскольку Hibernate — это API для разных поставщиков БД, но OTOH, у них могут быть специальные инструменты для каждого поставщика). Таким образом, непосредственно перед сбросом у Hibernate есть последний шанс убедиться, что сброшенные объекты не являются устаревшими данными. После сброса эти обновленные объекты больше не помечаются для обновления, поэтому будущие сбросы, даже в той же транзакции, не вызовут событие проверки версии.

person yair    schedule 26.06.2012
comment
(1) Потому что все вставки — это только одна операция, и версия должна это отражать. (2) После вставки 30 элементов выполняется 30 операторов UPDATE. Я стараюсь избегать их. (3) Мой последний вопрос был неясен, но я думаю, что понял. - person takteek; 26.06.2012
comment
(1) Я отредактировал ответ, чтобы попытаться объяснить, почему это невозможно. (2) Я удивлен этим... У меня нет ответа на этот вопрос, и я не понимаю, почему Hibernate не может сделать эту оптимизацию автоматически. (3) отлично. - person yair; 26.06.2012
comment
и (1) version не должен отражать это, поскольку он предназначен для управления параллелизмом оптимистичной блокировки. Так что это не обязательно отражает транзакции. - person yair; 26.06.2012