Как гарантировать пакетную вставку atomi c с использованием чистого Hibernate внутри метода @Transactional в приложении Spring Boot - PullRequest
0 голосов
/ 26 апреля 2020

Чтобы упростить мой сценарий, я бы привел следующий пример:

Я использую приложение Spring Boot со свойством spring.jpa.properties.hibernate.order_inserts = true.

Внутри java метод, помеченный аннотацией @Transactional. Я хочу выполнить пакетную вставку нескольких тысяч записей в чистом спящем режиме. Пакетная вставка выполняется отдельным методом, который вызывается основным методом с помощью @Transactional. Я делаю это так:

@Transactional
public void doSomeStuff() {
......
insertRecords(records, session);
......
}


private void insertRecords(final List<Record> records, final Session session) {
  int counter = 0;
  int batchSize = 50;
  for(Record r: records){
    session.save(r);

    counter ++;

    if(counter > 0 && counter % batchSize == 0){
       session.flush(); 
       session.clear();
    }

    }
}     

Проблема в том, что я хочу, чтобы операция была атома c и, если один из пакетных вставок, не удается откатить все. Я знаю, что аннотация @Transactional автоматически обеспечит откат RunTimeException или Error. Означает ли это, что в случае сбоя одного из пакетов @Transactional покроет откат, или я должен окружить цикл for блоком try / catch и сгенерировать проверенное исключение и использовать свойство rollbackFor свойства @Transactional? Я не уверен, что исключения, выданные при сбое пакета, отмечены или не отмечены.

Другой важный вопрос: в этом случае hibernate автоматически фиксирует каждый пакет в случае успешной вставки? Это будет означать, что в случае сбоя одного пакета я не смог бы откатить уже зафиксированный один раз, что нарушает атомарность.

Кроме того, я не хочу управлять коммитами вручную и хочу обработать это до транзакционный контекст, предоставленный Spring.

1 Ответ

0 голосов
/ 26 апреля 2020

Вы правы. По умолчанию @Transactional будет выполнять откат только для RuntimeException и Error, но не для проверенного исключения. Это означает, что если вы также хотите откатить проверенное исключение, вы должны перехватить все отмеченные исключения и повторно выдать их как RuntimeException или просто использовать настройку rollbackFor в @Transactional.

* 1009. * Я не уверен, что исключения, сгенерированные при сбое пакета, отмечены или не отмечены.

Компилятор java поможет проверить это. Если некоторые из внутренних методов выдают проверенное исключение, это требует вы должны обработать его, который вы не можете ни перехватить, ни указать, чтобы выбросить это проверенное исключение в объявлении метода. Коды не могут быть скомпилированы, если вы этого не сделаете.

Это означает, что если ваши коды могут компилироваться, из внутренних методов не выдается проверенное исключение, и вы можете просто придерживаться текущих настроек. С другой стороны, если какое-то проверенное исключение выдается из внутренних методов, вы можете выбрать перехват и повторный выброс как RuntimeException:

@Transactional
public void doSomeStuff()  {
    try{

    }catch(Exception ex){
      throw new RuntimeException("something goes wrong.." ,ex);
    }
}

или настроить rollbackFor

@Transactional(rollbackFor={Exception.class})
public void doSomeStuff() throws Exception{

}

Также обратите внимание, что session.flush() - это не то же самое, что совершить транзакцию, поэтому ваши коды не будут фиксироваться для каждой партии. Вместо этого транзакция будет зафиксирована после успешного завершения метода, помеченного как @Transactional, который в вашем случае будет возвращен doSomeStuff() при вставке всех записей.

Точка flush() предназначена для последующих session.clear() которые очищают память от сеанса так, что это не приведет к исчерпанию памяти JVM, если вы вставите много записей.

...