В этой статье описывается управление транзакциями в Spring с примерами. Поделитесь этим со всеми для справки. Конкретный анализ заключается в следующем:
Введение в бизнес:
Управление транзакциями является важной технологией при разработке корпоративных приложений, обеспечивающей целостность и согласованность данных.
Транзакция — это серия действий, которые рассматриваются как единая единица работы. Либо все эти действия работают, либо ни одно из них не работает.
Четыре ключевых атрибута транзакций (ACID)
① Атомарность: атомарная операция в транзакционной комнате состоит из ряда действий. Атомарность транзакций гарантирует, что действия либо полностью завершены, либо не имеют никакого эффекта. ② Согласованность: как только все действия транзакции завершены, транзакция фиксируется. Данные и ресурсы находятся в согласованном состоянии, соответствующем бизнес-правилам. ③ Изоляция: может быть много транзакций, обрабатывающих одни и те же данные одновременно, поэтому каждая вещь должна быть изолирована от других транзакций, чтобы предотвратить повреждение данных. ④ Долговечность: после завершения транзакции. , на его результаты не должны повлиять какие бы системные ошибки ни произошли. Обычно результаты транзакции записываются в постоянное хранилище.
Управление транзакциями весной
В качестве среды корпоративных приложений Spring определяет уровень абстракции поверх различных API управления транзакциями. Разработчикам приложений не нужно понимать базовый API управления транзакциями, чтобы использовать механизм управления транзакциями Spring.
Spring поддерживает как программное управление транзакциями, так и декларативное управление транзакциями.
Программное управление транзакциями. Встраивайте код управления транзакциями в бизнес-методы для управления отправкой и откатом транзакций. В программных транзакциях в каждую бизнес-операцию должен быть включен дополнительный код управления транзакциями.
Декларативное управление транзакциями: в большинстве случаев лучше использовать, чем программное управление транзакциями. Он отделяет код управления транзакциями от бизнес-методов и реализует управление транзакциями декларативным образом. Управление транзакциями как сквозная задача может быть модульным с помощью методов АОП. Spring поддерживает декларативное управление транзакциями через среду Spring AOP.
Свойства распространения транзакций Spring:
Когда метод транзакции вызывается другим методом транзакции, вы должны указать, как должна распространяться транзакция. Например: метод может продолжать выполняться в существующей транзакции или может запустить новую транзакцию и выполнить свою собственную транзакцию.
Поведение распространения транзакции можно указать с помощью атрибута распространения. Spring определяет 7 вариантов поведения при общении:
коммуникативное поведение | значение |
PROPAGATION_MANDATORY | Указывает, что метод должен быть запущен внутри транзакции. Если текущая транзакция не существует, будет выдано исключение. |
PROPAGATION_NESTED | Указывает, что если транзакция в настоящий момент существует, этот метод будет запущен во вложенной транзакции. Вложенные транзакции можно фиксировать или откатывать независимо от текущей транзакции. Если текущая транзакция не существует, поведение такое же, как PROPAGATION_REQUIRED. Обратите внимание, что поддержка такого поведения распространения у каждого производителя различна. Вы можете обратиться к документации ваших менеджеров ресурсов, чтобы убедиться, поддерживают ли они вложенные транзакции. |
ПРОПАГАЦИЯ_НИКОГДА | Указывает, что текущий метод не следует запускать в контексте транзакции. Если в данный момент выполняется транзакция, будет выдано исключение. |
PROPAGATION_NOT_SUPPORTED | Указывает, что этот метод не следует запускать внутри транзакции. Если текущая транзакция существует, она будет приостановлена на время работы этого метода. Если вы используете JTATransactionManager, вам необходимо получить доступ к TransactionManager. |
PROPAGATION_REQUIRED | Указывает, что текущий метод должен быть запущен внутри транзакции. Если текущая транзакция существует, метод будет запущен в этой транзакции. В противном случае будет начата новая транзакция. |
PROPAGATION_REQUIRED_NEW | Указывает, что текущий метод должен выполняться в отдельной транзакции. Будет запущена новая транзакция. Если текущая транзакция существует, она будет приостановлена во время выполнения этого метода. Если вы используете JTATransactionManager, вам необходимо получить доступ к TransactionManager. |
PROPAGATION_SUPPORTS | Указывает, что текущий метод не требует контекста транзакции, но если текущая транзакция существует, метод будет запущен в этой транзакции. |
Среди них PROPAGATION_REQUIRED — атрибут распространения по умолчанию.
Проблемы, вызванные параллельными транзакциями
Множество неожиданных проблем может возникнуть, когда несколько транзакций в одном и том же приложении или в разных приложениях выполняются одновременно с одним и тем же набором данных.
Проблемы, вызванные параллельными транзакциями, можно разделить на следующие три категории:
① Грязное чтение: Грязное чтение происходит, когда одна транзакция считывает данные, которые были перезаписаны другой транзакцией, но еще не были зафиксированы. Если позже перезапись будет отменена, данные, полученные первой транзакцией, будут недействительны.
② Неповторяемое чтение. Неповторяемое чтение происходит, когда транзакция выполняет один и тот же запрос дважды или более, но каждый раз получает разные данные. Обычно это происходит потому, что другая параллельная транзакция обновляет данные между запросами.
③ Фантомное чтение: Фантомное чтение аналогично неповторяемому чтению. Это происходит, когда одна транзакция (T1) считывает несколько строк данных, а затем другая параллельная транзакция (T2) вставляет некоторые данные. В последующих запросах первая транзакция (T1) обнаружит, что есть еще несколько записей, которых изначально не существовало.
пример кода
Сначала таблица базы данных:
Включая книгу(isbn, имя_книги, цена), учетную запись(имя пользователя, баланс), book_stock(isbn, акции)
Тогда в классах использовались:
КнигаМагазинДао
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
общедоступный интерфейс BookShopDao {
//Получить цену за единицу книги по номеру книги
public int findBookPriceByIsbn (String isbn);
//Обновляем инвентарь книги, чтобы инвентарь, соответствующий номеру книги, был равен -1
public void updateBookStock (String isbn);
//Обновляем баланс аккаунта пользователя: делаем балансовую цену имени пользователя
public void updateUserAccount (строковое имя пользователя, int цена);
}
КнигаМагазинДаоImpl
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
импортировать org.springframework.beans.factory.annotation.Autowired;
импортировать org.springframework.jdbc.core.JdbcTemplate;
импортировать org.springframework.stereotype.Repository;
@Repository("bookShopDao")
публичный класс BookShopDaoImpl реализует BookShopDao {
@Autowired
частный JdbcTemplate JdbcTemplate;
@Override
public int findBookPriceByIsbn (String isbn) {
String sql = "ВЫБРАТЬ цену ИЗ книги ГДЕ isbn =?";
return JdbcTemplate.queryForObject(sql, Integer.class, isbn);
}
@Override
public void updateBookStock(String isbn) {
//Проверяем, достаточен ли инвентарь книги, если нет, выдаем исключение
String sql2 = "ВЫБРАТЬ акцию ИЗ book_stock ГДЕ isbn =?";
int stock = JdbcTemplate.queryForObject(sql2, Integer.class, isbn);
если (запас == 0) {
throw new BookStockException("Недостаточно запасов!");
}
String sql = "UPDATE book_stock SET stock = stock - 1 WHERE isbn =?";
JdbcTemplate.update(sql, isbn);
}
@Override
public void updateUserAccount(String username, int цена) {
//Проверяем, недостаточен ли баланс, если нет, выбрасываем исключение
String sql2 = "ВЫБРАТЬ баланс ИЗ аккаунта ГДЕ имя пользователя =?";
int Balance = JdbcTemplate.queryForObject(sql2, Integer.class, username);
если (баланс <цена) {
throw new UserAccountException("Недостаточно баланса!");
}
String sql = "ОБНОВЛЕНИЕ аккаунта SET баланс = баланс - ? ГДЕ имя пользователя =?";
JdbcTemplate.update(sql, цена, имя пользователя);
}
}
КнижныйМагазинСервис
Скопируйте код следующим образом: package com.yl.spring.tx;
общедоступный интерфейс BookShopService {
публичная недействительная покупка (имя пользователя String, String isbn);
}
КнижныйМагазинСервисImpl
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
импортировать org.springframework.beans.factory.annotation.Autowired;
импортировать org.springframework.stereotype.Service;
импортировать org.springframework.transaction.annotation.Isolation;
импортировать org.springframework.transaction.annotation.Propagation;
импортировать org.springframework.transaction.annotation.Transactional;
@Service("bookShopService")
публичный класс BookShopServiceImpl реализует BookShopService {
@Autowired
частный BookShopDao bookShopDao;
/**
* 1. Добавьте аннотации транзакций
* Используйте распространение, чтобы указать поведение распространения транзакции, то есть, как использовать транзакцию, когда текущий метод транзакции вызывается другим методом транзакции.
* Значение по умолчанию ТРЕБУЕТСЯ, что означает использование транзакции, вызывающей метод.
* REQUIRES_NEW: используйте собственную транзакцию, транзакция вызванного метода транзакции приостанавливается.
*
* 2. Используйте изоляцию, чтобы указать уровень изоляции транзакции. Чаще всего используется значение READ_COMMITTED.
* 3. По умолчанию декларативная транзакция Spring откатывает все исключения времени выполнения, которые также можно установить с помощью соответствующих свойств. Обычно значения по умолчанию достаточно.
* 4. Используйте readOnly, чтобы указать, доступна ли транзакция только для чтения. Указывает, что эта транзакция только считывает данные, но не обновляет их, что может помочь ядру базы данных оптимизировать транзакцию. Если это действительно метод, который только читает базу данных, следует установить readOnly=true.
* 5. Используйте timeOut, чтобы указать время, в течение которого транзакция может пройти перед принудительным откатом.
*/
@Transactional(propagation=Propagation.REQUIRES_NEW,
изоляция = Isolation.READ_COMMITTED,
noRollbackFor={UserAccountException.class},
readOnly=истина, тайм-аут=3)
@Override
public void Purchase (String username, String isbn) {
//1. Получите цену за единицу книги.
int цена = bookShopDao.findBookPriceByIsbn (isbn);
//2. Обновить инвентарь книги.
bookShopDao.updateBookStock(isbn);
//3. Обновить баланс пользователя.
bookShopDao.updateUserAccount(имя пользователя, цена);;
}
}
BookStockException
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
публичный класс BookStockException расширяет RuntimeException {
/**
*
*/
частный статический окончательный длинный серийныйVersionUID = 1L;
общественное BookStockException() {
супер();
// TODO Автоматически сгенерированная заглушка конструктора
}
public BookStockException (String arg0, Throwable arg1, boolean arg2,
логическое значение arg3) {
супер(arg0, arg1, arg2, arg3);
// TODO Автоматически сгенерированная заглушка конструктора
}
public BookStockException (String arg0, Throwable arg1) {
супер(arg0, arg1);
// TODO Автоматически сгенерированная заглушка конструктора
}
public BookStockException (String arg0) {
супер(arg0);
// TODO Автоматически сгенерированная заглушка конструктора
}
public BookStockException (Throwable arg0) {
супер(arg0);
// TODO Автоматически сгенерированная заглушка конструктора
}
}
UserAccountException
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
общественный класс UserAccountException расширяет RuntimeException {
/**
*
*/
частный статический окончательный длинный серийныйVersionUID = 1L;
общественное исключение UserAccountException () {
супер();
// TODO Автоматически сгенерированная заглушка конструктора
}
public UserAccountException (String arg0, Throwable arg1, boolean arg2,
логическое значение arg3) {
супер(arg0, arg1, arg2, arg3);
// TODO Автоматически сгенерированная заглушка конструктора
}
public UserAccountException (String arg0, Throwable arg1) {
супер(arg0, arg1);
// TODO Автоматически сгенерированная заглушка конструктора
}
public UserAccountException (String arg0) {
супер(arg0);
// TODO Автоматически сгенерированная заглушка конструктора
}
public UserAccountException (Throwable arg0) {
супер(arg0);
// TODO Автоматически сгенерированная заглушка конструктора
}
}
Касса
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
импортировать java.util.List;
общедоступный интерфейс Касса {
public void checkout (имя пользователя String, List<String>isbns);
}
КассирИмпл. CashierImpl.checkout и bookShopService.purchase совместно тестируют поведение распространения транзакций.
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
импортировать java.util.List;
импортировать org.springframework.beans.factory.annotation.Autowired;
импортировать org.springframework.stereotype.Service;
импортировать org.springframework.transaction.annotation.Transactional;
@Service("касса")
публичный класс CashierImpl реализует Cashier {
@Autowired
частный BookShopService bookShopService;
@Транзакционный
@Override
public void checkout(String username, List<String> isbns) {
for(String isbn : isbns) {
bookShopService.purchase(имя пользователя, isbn);
}
}
}
Тестовый класс:
Скопируйте код кода следующим образом:
пакет com.yl.spring.tx;
импортировать java.util.Arrays;
импортировать org.junit.Test;
импортировать org.springframework.context.ApplicationContext;
импортировать org.springframework.context.support.ClassPathXmlApplicationContext;
общественный класс SpringTransitionTest {
частный ApplicationContext ctx = null;
частный BookShopDao bookShopDao = null;
частный BookShopService bookShopService = null;
частный кассир кассир = null;
{
ctx = новый ClassPathXmlApplicationContext("applicationContext.xml");
bookShopDao = ctx.getBean(BookShopDao.class);
bookShopService = ctx.getBean(BookShopService.class);
кассир = ctx.getBean(Cashier.class);
}
@Тест
общественная недействительность testBookShopDaoFindPriceByIsbn () {
System.out.println(bookShopDao.findBookPriceByIsbn("1001"));
}
@Тест
общественный недействительный testBookShopDaoUpdateBookStock () {
bookShopDao.updateBookStock("1001");
}
@Тест
общественный недействительный testBookShopDaoUpdateUserAccount () {
bookShopDao.updateUserAccount("AA", 100);
}
@Тест
общественный недействительный testBookShopService() {
bookShopService.purchase("AA", "1001");
}
@Тест
общественный недействительный testTransactionPropagation () {
Cashier.checkout("AA", Arrays.asList("1001", "1002"));
}
}
Я надеюсь, что эта статья будет полезна каждому, кто занимается программированием на Spring.