Мне очень трудно собрать всю информацию по кусочкам. Так что позвольте мне объяснить как можно подробнее.
Работая над приложением spring-jdb c, spring-transaction, MySQL db, я хочу иметь возможность блокировать строки данных из таблицы при выборе для обновления. Причина в том, что у меня есть бизнес-логи c, который преобразует данные на разных этапах. Таким образом, если экземпляр приложения (на сервере 1) получает запись для обработки, то ее не должен забирать другой экземпляр. Это несколько похоже на этот вопрос . Но ответ неприемлем для меня по тем же причинам, что и OP для этого вопроса.
Итак, после тщательного изучения ссылки на пружинную транзакцию, сделайте c и узнав, как настроить управление транзакциями с помощью jdbcTemplate, мое приложение Spring настроено следующим образом (только соответствующая часть):
applicationContext. xml
...
...
<context:annotation-config />
...
<context:component-scan base-package="org.foo.bar"/>
...
<!-- Enable Annotation based Declarative Transaction Management -->
<tx:annotation-driven proxy-target-class="true" transaction-manager="transactionManager" />
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
...
<bean id="dataSource"
class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://dbserver:3306" />
<property name="username" value="foo" />
<property name="password" value="baz" />
<property name="initialSize" value="10" />
<property name="maxTotal" value="20"/>
</bean>
Класс обслуживания с целевым методом: TransactionSvcImpl. java
public class TransactionSvcImpl implements TransactionSvc {
@Autowired
DatabaseAccessService dbAccessService;
@Transactional(isolation = Isolation.SERIALIZABLE, propagation = Propagation.REQUIRES_NEW)
@Override
public List<Foo> getFooForUpdate() {
// Below call executes a SQL like - "SELECT * FROM some_t WHERE id = 1 FOR UPDATE"
List<foo> foos = dbAccessService.getSomeRecord();
dbAccessService.updateTheRecord(foos, Status.PROCESSING);
return foos;
}
}
Теперь я проверил, как уровень изоляции работает в MySQL, следуя тому, что описано в этом ответе . Однако для того, чтобы «выбор для обновления» работал, мне пришлось изменить следующие свойства на самом сервере базы данных:
SET tx_isolation = 'SERIALIZABLE';
SET AUTOCOMMIT=0;
Без изменения этих настроек я не смог заставить SELECT ... FOR UPDATE
работать.
После того, как я проверил его вручную, я захотел посмотреть, влияет ли это на выполнение кода моего приложения. Итак, пока я поддерживал один из сеансов командной строки mysql активным, я запустил свое приложение и поставил точку останова в строке dbAccessService.updateTheRecord();
. Затем в другом сеансе я попытался выбрать эту строку (не select for update
, просто простой select
). Теперь я мог видеть, что строка была заблокирована из-за предыдущей транзакции, запущенной моим кодом приложения.
Вопросы:
- Для достижения блокировки строки (
SELECT ... FOR UPDATE
поведение) через Spring-transaction мне нужно изменить настройки на сервере базы данных? Например, уровень изоляции и автоматическая фиксация? - Допустим, сервер MySQL работает по умолчанию (ISOLATION_LEVEL = REPEATABLE-READS, AUTOCOMMIT = 1). Теперь это все еще будет работать? Это означает, что если я настрою транзакцию весной для целевого метода, как я сделал выше, будет ли строка, читаемая одним экземпляром приложения, оставаться заблокированной, так что любой другой экземпляр, пытающийся прочитать эту строку, будет заблокирован?
- Как влияет ли конфигурация, которую мы добавляем в аннотацию
@Transactional
, на базу данных (или на нее)? Я знаю, что мы можем установить характеристики транзакции для разных областей, как описано в таблице 13.9 документации MySQL здесь . В основном ниже мое воображение или предположение о том, как все это работает. Это правильно?
Как все это работает?