spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.61k stars 40.55k forks source link

HibernateJpaAutoConfiguration should be applied before DataSourceTransactionManagerAutoConfiguration #38861

Closed tvahrst closed 9 months ago

tvahrst commented 9 months ago

HibernateJpaAutoConfiguration should be applied before DatasourceTransactionManagerAutoconfiguration

Explanation: HibernateJpaAutoConfiguration imports HibernateJpaBaseConfiguration which contains a Bean definition of a JPATransactionManager with ConditionalOnMissingBean(TransactionManager) (in superclass JpaBaseConfiguration):

@Bean
@ConditionalOnMissingBean(TransactionManager.class)
public PlatformTransactionManager transactionManager(
        ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    JpaTransactionManager transactionManager = new JpaTransactionManager();
    transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    return transactionManager;
}

The DatasourceTransactionManagerAutoConfiguration does also define a TransactionManager Bean with the same condition:

@Bean
@ConditionalOnMissingBean(TransactionManager.class)
DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource,
        ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
    DataSourceTransactionManager transactionManager = createTransactionManager(environment, dataSource);
    transactionManagerCustomizers.ifAvailable((customizers) -> customizers.customize(transactionManager));
    return transactionManager;
}

If the application uses Hibernate, the Hibernate auto-configuration should match, which means that the HibernateJpaAutoConfiguration should be applied before DatasourceTransactionManagerAutoConfiguration.

Currently, there is no explicit before/after Condition for these Autoconfigurations, but the 'natural' sort order leads 'by chance' to the right order.

We provided for our application a additional auto-configuration with these conditions:

@AutoConfiguration(
    after = {RestTemplateAutoconfiguration.class, TransactionAutoConfiguration.class}
)

which leaded to a different order of many Spring auto-configuration, especially now sorting DatasourceTransactionManagerAutoConfiguration before HibernateJpaAutoConfiguration. As a result, we saw this exception during execution of the application:

14:22:47 [43-exec-7] ERROR d.l.a.a.b.r.GlobalExceptionHandler  - Caught unhandled exception
org.springframework.dao.InvalidDataAccessApiUsageException: no transaction is in progress
    at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:400)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:234)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:550)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:243)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:164)
...
Caused by: jakarta.persistence.TransactionRequiredException: no transaction is in progress
    at org.hibernate.internal.AbstractSharedSessionContract.checkTransactionNeededForUpdateOperation(AbstractSharedSessionContract.java:483)
    at org.hibernate.internal.SessionImpl.checkTransactionNeededForUpdateOperation(SessionImpl.java:2528)
    at org.hibernate.internal.SessionImpl.doFlush(SessionImpl.java:1406)
    at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1401)
philwebb commented 9 months ago

Thanks for the report @tvahrst, I've added the additional condition.

vanta commented 9 months ago

@philwebb when it will be ported to v3.2? I mean when do you plan to release it?

bclozel commented 9 months ago

@vanta today. See https://github.com/spring-projects/spring-boot/milestones and https://calendar.spring.io