Контекст
У нас есть REST-приложение Spring Boot, использующее JPA поверх Hibernate (Spring Boot 2.0.x, spring-tx 5.0.x, Hibernate 5.2.x).Базовая база данных - Oracle.
Наша архитектура не предоставляет сущности JPA, вместо этого сервисный уровень преобразует сущности в классы модели DTO, которые открыты для уровня контроллера REST.Для создания обновлений мы не используем автоматическую очистку, но всегда выполняем явный вызов методов CRUD класса обслуживания, которые переводятся в saveAndFlush()
или deleteById()
в хранилище (для создания / обновления или удаления соответственно).
Цель
Что нам нужно:
- Операции только для чтения выполняются без транзакций
- Операции записи (одиночные или множественные) выполняются внутриодна транзакция
- Мы получаем предупреждение, если забываем объявить транзакцию для операции записи.
Частичное решение
Для этого у нас есть:
Суперинтерфейс репозитория помечен @Transactional(propagation=SUPPORTS, readOnly=true)
, который охватывает пункт 1.
Классы службы не аннотированы, а методы записи службы помечены с помощью `@Транзакционные (распространение = ТРЕБУЕМЫЕ), которые охватывают пункт 2.
Репозитарий saveAndFlush(entity)
и deleteById(id)
методы (единственные методы записи, которые в настоящее время вызываются из сервисов), отмечены @Transactional(propagation=MANDATORY)
, которыевызывает исключениеn будет выброшено, если они выполняются вне контекста транзакции.
Итак, что касается пункта 3, у нас все в порядке, если кто-то определяет новый метод обслуживания, который записывает данные, и забывает применить @Transactional
на нем: будет сгенерировано исключение.
Но если кто-то использует другой метод репозитория, который записывает данные и забывает аннотировать его на уровне обслуживания, он не получит транзакцию с уровня обслуживания и выполнится в пределахтранзакционность по умолчанию (на уровне класса), то есть: SUPPORTS
.В этом случае, если текущая транзакция не выполняется, флаг readOnly будет игнорироваться, и запись будет выполняться без вывода сообщений.
Обратите внимание, что flushMode в Hibernate установлен на MANUAL, когда флаг readOnly
установлен на @Transactional
,Если бы у нас было REQUIRED
распространение, запись была бы тихо проигнорирована (что также было бы неприемлемо), тогда как в нашем контексте распространения SUPPORTS
запись выполнялась без вывода сообщений.
Вопрос
Есть ли способ настроить транзакции Spring / JPA / Hibernate таким образом, чтобы наш репозиторий разрешал нетранзакционные операции чтения, но генерировал исключение при записи любых данных, кроме как с помощью одного из методов, которые мы переопределилиобъявить @Transactional(propagation=MANDATORY)
?
Как отмечалось выше, readOnly=true
не достигает этого, даже если он устанавливает flushMode в Hibernate.