Нет активных транзакций после миграции с JDK 8 на 11, Spring 4.3.30 на 5.1.20 и Hibernate 5.2.13 на 5.4.33

Вопрос или проблема

Я недавно мигрировал веб-приложение на базе Java и столкнулся с проблемами транзакций после следующих обновлений:

JDK: с 8 на 11
Spring: с 4.3.30.RELEASE на 5.1.20.RELEASE
Hibernate: с 5.2.13.Final на 5.4.33.Final
После миграции некоторые методы сервиса, аннотированные @Transactional, больше не инициируют транзакции, что приводит к следующей ошибке:

javax.persistence.TransactionRequiredException: нет активной транзакции
Вот пример метода сервиса, который не может инициировать транзакцию:

java

@Service
public class TicketServiceImpl {

public TicketServiceImplV2() {
    System.out.println("TicketServiceImplV2 создан: " + this);
}

    @Autowired
    private TicketUpdatePersistence ticketPersistence;

    @Transactional
    public void createTicketUpdate(TicketUpdate update) {
        Ticket ticket = ticketPersistence.findById(update.getTicket().getId())
            .orElseThrow(() -> new EntityNotFoundException("Билет не найден"));

        System.out.println("Транзакция активна: " + TransactionSynchronizationManager.isActualTransactionActive());

        Ticket lockedTicket = ticketPersistence.findOneForUpdate(ticket.getId())
            .orElseThrow(() -> new BadRequestException("Нет билета для обновления"));
    }
}

Проблема: TransactionSynchronizationManager.isActualTransactionActive() возвращает false, и возникает исключение TransactionRequiredException при попытке обновить билет в базе данных.
Что я попробовал:
Конфигурация транзакций:
Есть только один лог “TicketServiceImplV2 создан”, что предполагает, что ваш сервис создается только в одном контексте (вероятно, в основном контексте), а не в обоих контекстах.

Я проверил, что менеджер транзакций правильно настроен:

xml

<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

Публичный метод: Метод является публичным, а @Transactional правильно применен к нему.

Нет самовызовов: Я убедился, что нет самовызова метода @Transactional, так как он вызывается извне.

Механизм блокировки: Уровень персистенции использует пессимистичную блокировку в методе findOneForUpdate:

java

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT t FROM Ticket t WHERE t.id = :id")
Ticket findOneForUpdate(@Param("id") String ticketId);

мой файл конфигурации db.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:jpa="http://www.springframework.org/schema/data/jpa"
       xsi:schemaLocation="http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/data/jpa
       http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd">

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" primary="true">
        <property name="jdbcUrl" value="${db.url}" />
        <property name="username" value="#{props.getDbUsername()}" />
        <property name="password" value="#{props.getDbPassword()}" />
        <property name="driverClassName" value="${db.driverClassName}" />
        <property name="maximumPoolSize" value="${db.tomcat.maxActive}" />
        <property name="minimumIdle" value="${db.tomcat.minIdle}" />
        <property name="leakDetectionThreshold" value="${db.leak-detection-threshold}" />
        <property name="connectionTimeout" value="${db.connection-timeout}" />
        <property name="idleTimeout" value="${db.idle-timeout}" />
        <property name="autoCommit" value="true" />
        <property name="maxLifetime" value="1800000" />
    </bean>

    <bean name="quartzDS" class="com.zaxxer.hikari.HikariDataSource" primary="false">
        <property name="jdbcUrl" value="#{props.getDbQuartzUrl()}"/>
        <property name="username" value="#{props.getDbUsername()}" />
        <property name="password" value="#{props.getDbPassword()}" />
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="maximumPoolSize" value="${db.tomcat.maxActive}" />
        <property name="minimumIdle" value="${db.tomcat.minIdle}" />
        <property name="leakDetectionThreshold" value="${db.leak-detection-threshold}" />
        <property name="connectionTimeout" value="${db.connection-timeout}" />
        <property name="idleTimeout" value="${db.idle-timeout}" />
    </bean>

    <bean class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"
          id="entityManagerFactory">
        <property name="dataSource" ref="dataSource"/>
        <property name="persistenceUnitName" value="spring-jpa"/>

        <property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false"/>
                <property name="generateDdl" value="true"/>
            </bean>
        </property>
        <property name="packagesToScan">
            <array>
                <value>com.quicktech.quicktech.common.model</value>
            </array>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
        <property name="dataSource" ref="dataSource"/>
        <property name="entityManagerFactory" ref="entityManagerFactory"/>
    </bean>
    <tx:annotation-driven />


    <jpa:repositories base-package="com.quicktech.quicktech.common.persistence"
                      entity-manager-factory-ref="entityManagerFactory"
                      transaction-manager-ref="transactionManager"
                      repository-impl-postfix="CustomImpl"/>


</beans>

моя общая конфигурация Spring XML

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:task="http://www.springframework.org/schema/task" xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.0.xsd
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
       http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">

    <context:annotation-config/>

    <context:property-placeholder location="classpath:/application.properties,classpath:/spring.properties"/>

    <context:component-scan base-package="com.quicktech.quicktech.common"/>

    <context:component-scan base-package="com.quicktech.quicktech.aspect"/>

    <aop:aspectj-autoproxy />

</beans>

Мои вопросы:
Что может быть причиной того, что транзакции не активируются после миграции с Spring 4.3 на 5.1 и Hibernate 5.2 на 5.4?
Есть ли известные изменения в Spring 5 или Hibernate 5.4, которые могут повлиять на поведение управления транзакциями?
Может ли эта проблема быть связана с миграцией с JDK 8 на JDK 11, и если да, как мне это решить?
Я был бы признателен за любые предложения или шаги по устранению неполадок для решения этой проблемы. Заранее спасибо!

Дополнительная информация:
База данных: PostgreSQL 12
Версия Spring: 5.1.20.RELEASE
Версия Hibernate: 5.4.33.Final
Версия JDK: 11.0.24
Сервер приложений: Apache Tomcat 9.0.55

Ответ или решение

Проблема, с которой вы столкнулись, связана с тем, что после миграции на более новые версии JDK, Spring и Hibernate у вас больше не активируются транзакции, что приводит к возникновению исключения javax.persistence.TransactionRequiredException: no transaction is in progress. Давайте проведем подробный анализ и разберемся в возможных причинах и решениях.

Возможные причины проблемы:

  1. Изменения в конфигурации Spring:

    • При миграции с Spring 4.3.30 до 5.1.20 произошли изменения в пространстве имен и в конфигурации XML. Убедитесь, что все XML-файлы настроены правильно на соответствие новым версиям. Например, проверьте, что у вас есть корректные схемы для spring-tx, spring-jpa и других используемых вами пространств имен.
  2. Аспекты и аспектно-ориентированное программирование:

    • Убедитесь, что ваш класс TicketServiceImpl корректно обрабатывается аспектом. Проверьте, что аннотация @Transactional действительно применяется на уровне класса или метода, и нет конфликтов, из-за которых транзакции могут не инициироваться.
    • Поскольку у вас включен <aop:aspectj-autoproxy />, это должно работать, однако посмотрите на правильность использования аспектов в зависимости от новой конфигурации Spring.
  3. Проблемы с конфигурацией JPA:

    • Убедитесь, что JpaTransactionManager настроен на использование правильного EntityManagerFactory и что dataSource корректно инициализируется. Т.к. вы используете HikariCP, убедитесь, что используемые свойства в конфигурации проверки корректны и соединение действительно устанавливается.
  4. Проблемы с управлением соединениями:

    • Убедитесь, что для вашего Datasource установлены соответствующие настройки, особенно autoCommit. В вашем случае процесс коммита на уровне DB может мешать транзакциям Spring, если он установлен в true.
  5. Влияние миграции на JDK:

    • Хотя переход с JDK 8 на 11 не должен напрямую влиять на работу транзакций, при миграции могут возникнуть другие проблемы, связанные с библиотеками или механизмами работы приложения. Убедитесь, что зависимости вашего проекта полностью совместимы с JDK 11.

Рекомендации по устранению проблемы:

  1. Убедитесь в корректной конфигурации XML:

    • Проверьте все xsi:schemaLocation для соответствия текущим версиям Spring и удалите устаревшие ссылки. Попробуйте обновить их на более актуальные.
  2. Включите отладку транзакций:

    • Добавьте в конфигурацию журналирования (log4j/logback) подробную информацию по транзакциям. Это поможет отследить, когда и почему транзакции не инициализируются, выполнив соответствующие логи в классе TicketServiceImpl.
  3. Проверьте настройки подключения:

    • Проверьте, как ваше приложение устанавливает соединение с базой данных. Убедитесь, что соединение не закрывается или не теряется до вызова методов репозитория.
  4. Обновите зависимости:

    • Просмотрите ваши зависимости, возможно, их стоит обновить или проверить на конфликты. Убедитесь, что все необходимые библиотеки совместимы и работают, как ожидается.

Заключение:

Если после выполнения вышеуказанных предложений проблема все еще сохраняется, я рекомендую создать простую тестовую конфигурацию приложения, в которой будет минимальный набор классов и методов, и проверить, сохраняются ли транзакции. Также рассмотреть возможность использования акторов тестирования, чтобы лучше отследить поведение транзакционного менеджера.

Надеюсь, эти рекомендации помогут вам решить проблему с транзакциями в вашем приложении. Если у вас есть дальнейшие вопросы или необходима дополнительная помощь, не стесняйтесь обращаться!

Оцените материал
Добавить комментарий

Капча загружается...