Несколько дней назад мне пришлось расследовать ошибку в производстве, связанную с транзакцией базы данных, специально определенной аннотацией @Transaction
. Это звучало как отличная возможность ознакомиться с основными концепциями и, надеюсь, исправить ошибку.
До появления ошибки приложение выглядело так: конечная точка, которая получала некоторую полезную нагрузку, а затем выполняла запись в разные таблицы в одной и той же аннотации транзакции, используя соответствующие зависимости:
@Autowired private TableOneService tableOneService; @Autowired private TableTwoService tableTwoService; @PostMapping("/") @Transactional public void save(@RequestBody Payload payload) { tableOneService.save(payload); tableTwoService.save(payload); }
До сих пор все было хорошо, приложение вело себя так, как ожидалось.
После наблюдения за данными, сохраненными во второй таблице, мы решили изменить бизнес-правило, проверить сохраненную полезную нагрузку на основе ее значений. Если вы не знаете всего приложения и просто сосредоточены на изменении кода службы второй таблицы
на основе этих конкретных требований, это возможное решение, которое вы можете рассмотреть:
- Добавьте какую-нибудь проверку в метод
сохранения
- Выдаст исключение, которое будет переведено в
Неверный запрос
ответ клиенту.
Не чувствуйте себя виноватым, если вы рассматривали это решение, мы также сделали это и внедрили его в производство.
Вот как выглядел код сейчас. Как разработчики, мы так несправедливо гордимся.
public class TableTwoService { public void save(Payload paylod) { if (isValid(payload)) { // persists on database } else { // throw some exception } } }
Через несколько дней кто-то поднял руку:
” Эй, что-то не так, нам не хватает некоторых данных в первой таблице с момента развертывания кода проверки”.
Черт, мы даже не изменили Обслуживание за первым столом
ни код контроллера API, наверняка это чья-то чужая проблема.
Короче говоря, это то, что определяет поведение метода, аннотированного @Transaction
:
- Начните транзакцию.
- Выполните набор манипуляций с данными и/или запросов.
- Если ошибки не возникает, то зафиксируйте транзакцию.
- Если произошла ошибка, откатите транзакцию.
@Transactional public void save(@RequestBody Payload payload) { // Both operations should work for transaction commit // Otherwise no operation will persist tableOneService.save(payload); tableTwoService.save(payload); }
Помимо того, что код службы table One
выполнялся без ошибок во время выполнения, исключение, созданное службой table Two
, отменяло ее сохраняемость.
Решение, которое я решил использовать, состояло в том, чтобы удалить созданное исключение и вместо этого просто зарегистрировать полученную полезную нагрузку, чтобы я мог лучше наблюдать за этим потоком. Исключение теперь не приведет к откату транзакции.
В этом случае мое решение работает, потому что для клиента, отправившего этот запрос, нормально не получать Неверный запрос
ответ всякий раз, когда он отправляет недопустимую полезную нагрузку, и просто игнорирует ее полезную нагрузку.
public class TableTwoService { public void save(Payload paylod) { if (isValid(payload)) { // persists on database } else { // log a message and do nothing } } }
Теперь ошибка исправлена, и я узнал немного больше об аннотации @Transaction
. Я надеюсь, что эта история поможет кому-то еще в будущем.
Оригинал: “https://dev.to/fabiothiroki/transaction-in-spring-boot-as-simple-as-possible-5ecf”