Транзакция базы данных — это серия операций, которые выполняются как единая логическая единица работы либо полностью, либо не выполняются вообще.
Транзакции гарантируют, что ресурсы, ориентированные на данные, не будут постоянно обновляться, пока все операции внутри транзакционной единицы не завершатся успешно. Объединив набор связанных операций в блок, в котором либо все выполняются успешно, либо все терпят неудачу, вы упрощаете восстановление ошибок и делаете свое приложение более надежным.
Чтобы логическая единица работы была транзакцией, она должна удовлетворять так называемым свойствам ACID (атомарность, согласованность, изоляция и долговечность). Транзакция — это логическая единица работы в операции с базой данных.
В реальной работе мы в основном комбинируем Spring для выполнения проектов. В настоящее время нам приходится сталкиваться с такой ситуацией.
Уровень контроллера:
UserService:addUser();
Уровень обслуживания (UserService):
addUser(): insertUser()+ insertLog()
Слой Дао:
UserDao:insertUser();
LogDao: insertDao();
Видно, что мы можем вызывать несколько методов Dao в Service для работы с данными в базе данных. Нам нужно убедиться, что соответствующие операции в методе addUser() в UserService соответствуют требованиям транзакции.
Распространенные способы запуска транзакций Spring: @Transactional
После запуска транзакции объект подключения к прокси-базе данных создается с помощью механизма AOP и помещается в контейнер связанных объектов DataSourceTransactionManager экземпляра DataSource. На протяжении транзакции все соединения с базой данных в бизнес-коде должны быть одним и тем же соединением, и Sql, который не использует это соединение, не будет откатан. При возникновении исключения в бизнес-коде будет выполнена операция отката.
Низкоуровневая реализация:
уровень изоляции( @Transactional ( isolation = Isolation.DEFAULT ) ): Чтобы решить проблемы, которые могут возникнуть в базе данных, используется стратегия обработки иерархической блокировки.
уровень изоляции | описывать |
---|---|
ISOLATION_DEFAULT | Spring 默认уровень изоляции,ксоединятьизбаза данныхизделауровень изоляции С учетом;Mysql (повторяемое чтение) |
ISOLATION_READ_UNCOMMITTED | Чтение незавершенного: Минимальный уровень уровня его смысл состоит в том, чтобы позволить одному делу прочитать другое дело без предоставления данных. Незавершенное чтение представляет собой опасность изуровень изоляции,так обычно не широко используется в нашей реальной разработке середина,Но его преимущество заключается в высокой возможности параллелизма.,Подходит для сценариев, которые не требуют согласованности данных, но требуют высокого уровня параллелизма.,Его самым большим недостатком является возникновение грязных операций чтения. |
ISOLATION_READ_COMMITTED | Чтение зафиксировано: означает, что транзакция может читать только данные, отправленные другой транзакцией, но не может читать незафиксированные данные. |
ISOLATION_REPEATABLE_READ | Повторяемое чтение. Цель состоит в том, чтобы преодолеть явление неповторяющегося чтения при фиксации чтения-записи, поскольку во время фиксации чтения-записи некоторые значения могут измениться, влияя на выполнение текущей транзакции. |
ISOLATION_SERIALIZABLE | Сериализация, база данных Самый высокийизуровень запланировано, он попросит все из SQL будет выполняться по порядку, чтобы можно было преодолеть вышеупомянутый уровень Запуск Возникают различные проблемы, поэтому он может полностью обеспечить согласованность данных. |
Время ожидания (@Transactional (timeout = 30)): определите, сколько времени будет продолжаться процесс выполнения транзакции, чтобы его можно было откатить после истечения времени ожидания. Это может предотвратить занятие ресурсов длительными транзакциями. Соответствует тайм-ауту атрибута в аннотации (примечание: этот тайм-аут находится в пределах тайм-аута транзакции базы данных).
Доступна ли она только для чтения (@Transactional (readOnly = true)): указывает, что эта транзакция только считывает данные, но не обновляет данные.
Механизм отката (@Transactional (rollbackFor = Exception.class): определите стратегию отката при возникновении исключения.
Механизм распространения (@Transactional (propagation = Propagation.REQUIRED): определяет характеристики распространения транзакции, всего существует 7 типов (одна транзакция вызывает другую транзакцию)
транзакционное поведение | иллюстрировать |
---|---|
PROPAGATION_REQUIRED | Если транзакция существует в текущем контексте, присоединитесь к транзакции. Если транзакции нет, создайте транзакцию. Это значение атрибута распространения по умолчанию. |
PROPAGATION_SUPPORTS | Если транзакция существует в текущем контексте, поддерживается присоединение к транзакции. Если транзакции нет, для ее выполнения используется нетранзакционный метод. |
PROPAGATION_MANDATORY | Поддерживает текущие транзакции. Если текущей транзакции нет, будет выдано исключение. |
PROPAGATION_REQUIRES_NEW | Каждый раз будет создаваться новая транзакция, и транзакция в контексте будет приостанавливаться. После завершения выполнения текущей новой транзакции контекстная транзакция будет возобновлена и выполнена снова. |
PROPAGATION_NOT_SUPPORTED | Если транзакция существует в текущем контексте, текущая транзакция приостанавливается, а затем новый метод выполняется в среде без транзакции. |
PROPAGATION_NEVER | Если транзакция существует в текущем контексте, выдайте исключение, в противном случае выполните код в среде без транзакций. |
PROPAGATION_NESTED | Если транзакция в настоящий момент существует, она выполняется внутри вложенной транзакции. Если текущей транзакции нет, выполните операции, аналогичные PROPAGATION_REQUIRED. |
Пример кода:
@Service
public class UserService {
@Transactional
private void add(UserModel userModel) {
saveData(userModel);
updateData(userModel);
}
}
Причина: Spring требует, чтобы прокси-метод был общедоступным.
Другими словами, если наш пользовательский метод транзакции (т. е. целевой метод) имеет права доступа, отличные от общедоступных, но закрытые, по умолчанию или защищенные, Spring не будет предоставлять функции транзакций.
Пример кода:
@Service
public class UserService {
@Transactional
public final void add(UserModel userModel){
saveData(userModel);
updateData(userModel);
}
}
причина:
Нижний уровень транзакций Spring использует AOP, то есть через динамический прокси JDK или CGLIB он помогает нам генерировать прокси-классы и реализовывать функции транзакций в прокси-классах. Но если метод изменен с помощью Final, то в его прокси-классе метод не может быть переопределен и добавлена функция транзакции.
Примечание. Если метод статически изменен, его нельзя превратить в метод транзакции через динамический прокси.
Пример кода:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
public void add(UserModel userModel) {
userMapper.insertUser(userModel);
updateStatus(userModel);
}
@Transactional
public void updateStatus(UserModel userModel) {
doSameThing();
}
}
причина:
Мы видим, что в методе транзакции add напрямую вызывается метод транзакции updateStatus. Из предыдущего введения мы знаем, что метод updateStatus имеет возможности транзакций, поскольку Spring AOP генерирует прокси-объекты. Однако этот метод напрямую вызывает метод этого объекта и не получает динамику методов, связанных с транзакциями AOP, от прокси-объекта. поэтому метод updateStatus не генерирует транзакцию
Пример кода:
//@Service
public class UserService {
@Transactional
public void add(UserModel userModel) {
saveData(userModel);
updateData(userModel);
}
}
причина:
Предварительным условием для использования транзакций Spring является то, что если объектом будет управлять Spring IOC, необходимо создать экземпляр компонента.
Пример кода:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional
public void add(UserModel userModel) throws Exception {
userMapper.insertUser(userModel);
new Thread(() -> {
roleService.doOtherThing();
}).start();
}
}
@Service
public class RoleService {
@Transactional
public void doOtherThing() {
}
}
Причина: одни и те же дела на самом деле относятся к одной и той же базе данныесоединять, имеют только одну и ту же базу данныесоединять можно одновременно зафиксировать и откатить. Если в разных темах, получите избазу данныесоединять определенно не то же самое, что из, так отличается от издела.
Пример кода:
@Service
public class UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private RoleService roleService;
@Transactional(propagation = Propagation.REQUIRED)
public void add(UserModel userModel) throws Exception {
userMapper.insertUser(userModel);
new Thread(() -> {
roleService.doOtherThing();
}).start();
}
}
@Service
public class RoleService {
@Transactional(propagation = Propagation.NEVER)
public void doOtherThing() {
}
}
причина:RoleService середина doOtherThing() Тип распространения транзакции, установленный для метода: Propagation.NEVER, выдает исключение, если есть транзакция.
Пример кода:
@Slf4j
@Service
public class UserService {
@Transactional
public void add(UserModel userModel) {
try {
saveData(userModel);
updateData(userModel);
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
Причина: Если хочешь Spring Чтобы транзакция могла выполнить нормальный откат, она должна выдать исключение, которое она может обработать. Если исключение не выбрано, то Spring Считайте, что процедура нормальная.
Пример кода:
@Slf4j
@Service
public class UserService {
@Transactional
public void add(UserModel userModel) throws Exception {
saveData(userModel);
updateData(userModel);
}
}
причина:@Transactional Тип исключения по умолчанию: RuntimeException, если не RuntimeException, Spring Если транзакция не может обработать соответствующее исключение и программа считается нормальной, транзакция не будет отменена. На этом этапе мы можем указать тип исключения, например. @Transactional(rollbackFor = Exception.class)
например Mysql серединаиз MyISAM Движок не поддерживает операции транзакций. InnoDB Это механизм, который поддерживает транзакции
В этой статье представлена аннотация @Transactional и перечислены сценарии, в которых может произойти сбой транзакции. Три наиболее распространенных случая — это самовызов, перехват исключения и несоответствие типа выдачи исключения. Из-за большой рабочей нагрузки иногда игнорируются спецификации использования аннотации @Transactional, в результате чего транзакция не вступает в силу или откатывается обычным образом, что приводит к большим аномалиям данных.