Подтвердить что ты не робот

Spring @Transactional для чтения только для чтения

Я экспериментирую с использованием шаблона команды, чтобы позволить моему веб-уровню работать с объектами Hibernate в контексте одной транзакции (таким образом, избегая ленивых загрузочных исключений). Я, однако, теперь запутался в том, как я должен разбираться с транзакциями.

Мои команды вызывают методы уровня обслуживания, которые аннотируются аннотациями @Transactional. Некоторые из этих методов уровня обслуживания доступны только для чтения - например, @Transactional(readOnly=true) - и некоторые из них читаются/записываются.

Мой сервисный уровень предоставляет обработчик команд, который выполняет команды, переданные ему от имени веб-уровня.

@Transactional
public Command handle( Command cmd ) throws CommandException

Я предполагаю, что я прав, сделав метод обработчика команд handle() транзакционным. Здесь возникает путаница. Если реализация команды вызывает вызовы для нескольких методов уровня обслуживания, для обработчика команд не существует способа узнать, будут ли операции, вызываемые внутри команды, только для чтения, чтения/записи или комбинации из двух.

Я не понимаю, как работает распространение в этом примере. Если я должен был сделать метод handle() readOnly=true, то что произойдет, если команда затем вызовет метод уровня сервиса, который аннотируется с помощью @Transactional(realOnly=false)?

Буду признателен за лучшее понимание этого и приветствую ваши комментарии...

Эндрю

4b9b3361

Ответ 1

Прежде всего, поскольку Spring не выполняет персистентность, он не может указать, что именно означает readOnly. Этот атрибут является лишь подсказкой для провайдера, в этом случае поведение зависит от Hibernate.

Если вы укажете readOnly как true, режим очистки будет установлен как FlushMode.NEVER в текущем сеансе Hibernate, чтобы сеанс не выполнял транзакцию.

Кроме того, в соединении JDBC будет вызываться setReadOnly (true), что также указывает на базовую базу данных. Если ваша база данных поддерживает это (скорее всего, это так), это имеет в основном тот же эффект, что и FlushMode.NEVER, но это сильнее, поскольку вы даже не можете рушить вручную.

Теперь посмотрим, как работает распространение транзакций.

Если вы явно не устанавливаете readOnly в true, у вас будут транзакции чтения/записи. В зависимости от атрибутов транзакции (например, REQUIRES_NEW) иногда ваша транзакция приостанавливается в какой-то момент, начинается новый и, в конечном итоге, завершается, и после этого первая транзакция возобновляется.

Хорошо, мы почти там. Посмотрим, что привнесет readOnly в этот сценарий.

Если метод транзакции read/write вызывает метод, требующий транзакции readOnly, первая должна быть приостановлена, поскольку в противном случае произойдет флеш/фиксация в конце второго метода.

И наоборот, если вы вызываете метод из транзакции readOnly, для которой требуется чтение/запись, снова первая будет приостановлена, так как она не может быть сброшена/и второй метод нуждается в этом.

В операциях readOnly-to-readOnly и чтения/записи-чтения/записи внешняя транзакция не требуется приостанавливать (если только вы не укажите распространение в противном случае, очевидно).

Ответ 2

Протеировать довольно легко.

BeanS вызывает транзакцию = только для чтения Bean1, которая выполняет поиск и вызывает транзакцию = чтение-запись Bean2, которая сохраняет новый объект.

  • Bean1 запускает tx. только для чтения.

31 09: 39: 44.199 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - Создание новой транзакции с именем [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_REQUIRED, ISOLATION_DEFAULT, только для чтения; '

  • Bean 2 участвует в этом.

31 09: 39: 44.230 [пул-1-поток-1] DEBUG o.s.orm.jpa.JpaTransactionManager - участие в существующей транзакции

Ничто не привязано к базе данных.

Теперь измените аннотацию Bean2 @Transactional на добавление propagation=Propagation.REQUIRES_NEW

  • Bean1 запускает tx. только для чтения.

31 09: 31: 36.418 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - создание новой транзакции с именем [nz.co.vodafone.wcim.business.Bean1.startSomething]: PROPAGATION_REQUIRED, ISOLATION_DEFAULT, только для чтения; '

  • Bean2 запускает новое чтение-запись tx

31 09: 31: 36.449 [pool-1-thread-1] DEBUG osorm.jpa.JpaTransactionManager - приостановка текущей транзакции, создание новой транзакции с именем [nz.co.vodafone.wcim.business.Bean2.createSomething]

И изменения, сделанные Bean2, теперь привязаны к базе данных.

Вот пример, протестированный с помощью spring -data, hibernate и oracle.

@Named
public class BeanS {

@Inject
Bean1 bean1;

@Scheduled(fixedRate = 20000)
public void runSomething() {
    bean1.startSomething();
}

}


@Named
@Transactional(readOnly = true)
public class Bean1 {

Logger log = LoggerFactory.getLogger(Bean1.class);

@Inject
private CircuitStateRepository csr;

@Inject
private Bean2 bean2;

public void startSomething() {

    Iterable<CircuitState> s = csr.findAll();
    CircuitState c = s.iterator().next();
    log.info("GOT CIRCUIT {}", c.getCircuitId());
    bean2.createSomething(c.getCircuitId());

}

}


@Named
@Transactional(readOnly = false)
public class Bean2 {

    @Inject
    CircuitStateRepository csr;

    public void createSomething(String circuitId) {

        CircuitState c = new CircuitState(circuitId + "-New-" + new DateTime().toString("hhmmss"), new DateTime());

        csr.save(c);

    }


}

Ответ 3

По умолчанию распространение транзакций ТРЕБУЕТСЯ, что означает, что одна и та же транзакция будет распространяться от транзакционного вызывающего абонента до транзакционного вызываемого абонента. В этом случае также будет распространяться статус только для чтения. Например. если транзакция только для чтения вызовет транзакцию чтения и записи, вся транзакция будет доступна только для чтения.

Можете ли вы использовать шаблон Open Session in View, чтобы позволить ленивую загрузку? Таким образом, ваш метод обработки не должен быть транзакционным вообще.

Ответ 4

Кажется, что он игнорирует настройки текущей активной транзакции, он применяет только настройки к новой транзакции:

org.springframework.transaction.PlatformTransactionManager
TransactionStatus getTransaction(TransactionDefinition definition)
                         throws TransactionException
Return a currently active transaction or create a new one, according to the specified propagation behavior.
Note that parameters like isolation level or timeout will only be applied to new transactions, and thus be ignored when participating in active ones.
Furthermore, not all transaction definition settings will be supported by every transaction manager: A proper transaction manager implementation should throw an exception when unsupported settings are encountered.
An exception to the above rule is the read-only flag, which should be ignored if no explicit read-only mode is supported. Essentially, the read-only flag is just a hint for potential optimization.