Почему запрос Spring Data JPA завершается ошибкой при использовании указанного диспетчера транзакций?

#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