#spring #spring-boot #hibernate #jpa #spring-data-jpa
Вопрос:
У меня есть служба, которая использует 2 источника данных — один, который обрабатывает чтение, и один, который обрабатывает операции изменения (вставка, удаление, обновление). Для каждого источника данных я настроил отдельный менеджер транзакций.
Диспетчер транзакций по умолчанию с именем просто transactionManager
обрабатывает чтение, в то время как другой с именем writeTransactionManager
обрабатывает запись.
Для чтения все работает нормально, но у меня есть custom delete query
проблема, которую просто не может решить правильный менеджер транзакций.
Это мое хранилище:
public interface DeviceWriteRepository extends JpaRepository<Device, String> {
@Transactional
@Modifying
@Query("DELETE FROM Device d WHERE d.userId = ?1 AND d.tenantId = ?2 AND d.appId = ?3")
void unregisterDevices(String userId, String tenantId, String appId);
}
Включив журналы отладки и трассировки JPA/гибернации, при вызове unregisterDevices
устройств я получаю следующие журналы:
Creating new transaction with name [com.my.app.repository.SoftDeleteCrudRepositoryImpl.unregisterDevice]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
Opened new EntityManager [SessionImpl(1270255427<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@464a9dcc]
delete from device where user_id=? and tenant_id=? and app_id=?
delete from device where user_id=? and tenant_id=? and app_id=?
binding parameter [1] as [VARCHAR] - [user1]
binding parameter [2] as [VARCHAR] - [someTenant]
binding parameter [3] as [VARCHAR] - [someApp]
Initiating transaction commit
Committing JPA transaction on EntityManager [SessionImpl(1270255427<open>)]
Closing JPA EntityManager [SessionImpl(1270255427<open>)] after transaction
Все в порядке, и если я проверю базу данных, запрос на удаление сработает, но он не был выполнен writeTransactionManager
.
Чтобы принудительно использовать нужного мне менеджера транзакций, я обновляю @Transactional
аннотацию , чтобы включить имя менеджера как такового @Transactional("writeTransactionManager")
, я получаю следующее исключение:
org.springframework.dao.InvalidDataAccessApiUsageException: Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query
Более того, если я еще раз проверю журналы, я увижу, что, в отличие от моей первой попытки, они на самом writeTransactionManager
деле выбраны в соответствии со следующими журналами с акцентом на первую строку:
Creating new transaction with name [com.my.app.repository.SoftDeleteCrudRepositoryImpl.unregisterDevice]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; 'writeTransactionManager'
Opened new EntityManager [SessionImpl(1868680430<open>)] for JPA transaction
Exposing JPA transaction as JDBC [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@1c459897]
Opening JPA EntityManager
Initiating transaction rollback
Rolling back JPA transaction on EntityManager [SessionImpl(1868680430<open>)]
Closing JPA EntityManager [SessionImpl(1868680430<open>)] after transaction
Как это может быть не внутри транзакции, а в начальной? Почему указание имени менеджера транзакций нарушает поведение?
Еще одна вещь, которую я хотел бы упомянуть, это то, что я также пытался использовать DSL и определить другой метод репозитория, подобный этому:
@Transactional("writeTransactionManager")
void deleteByUserIdAndTenantIdAndAppIdAndPlatform(String userId, String tenantId, String appId, PlatformType platform);
Когда я использую этот метод, writeTransactionManager
он выбирается, но сначала он выполняет SELECT
запрос, чтобы ввести управляемые объекты — и неожиданно — удаление никогда не происходит.
Комментарии:
1. @Транзакционный(распространение = Распространение. REQUIRES_NEW)
2. @NajeebArif но почему? нет никакой другой транзакции, которая инкапсулирует эту. Вы можете видеть в журналах, что транзакция была открыта специально для этого удаления.
3. Как вы настроили менеджеры транзакций и EntityMangerFactory?
4. Я сомневаюсь, что это сработает, потому что один EntityManagerFactory может иметь только в TransactionManager, а один объект-только один EntityManagerFactory