Spring documentation "Data Access", paragraph "1.4.6. Using @Transactional" contains incorrect description of @Transactional annotation that is used on a class level and incorrect corresponding code snippets. * According to source codes of AnnotationTransactionAspect and ProxyTransactionManagementConfiguration the @Transactional annotation when used on a class level is only applied to public method even if we use AspectJ. * The are 2 incorrect code snippets that show class level @Transactional annotation for package-private methods.

Comment From: sbrannen

Good catch!

This has been merged into main.

Thanks

Comment From: sbrannen

the @Transactional annotation when used on a class level is only applied to public method even if we use AspectJ.

On second thought, that's not entirely true: it depends on the publicMethodsOnly flag in AnnotationTransactionAttributeSource.

When using @EnableTransactionManagement, ProxyTransactionManagementConfiguration.transactionAttributeSource() uses the default AnnotationTransactionAttributeSource constructor which sets publicMethodsOnly to true.

However, in the Spring TestContext Framework, the TransactionalTestExecutionListener sets the publicMethodsOnly flag to false.

In light of that, I'll modify the wording in the docs accordingly.

Comment From: sbrannen

As a proof of concept, I put together the following test case which demonstrates that @Transactional -- when declared solely at the class level -- can be configured to apply to all non-private methods in a standard Spring application as well.

import javax.sql.DataSource;

import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAttributeSource;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.transaction.support.TransactionSynchronizationManager.isActualTransactionActive;

@SpringJUnitConfig
class NonPublicTxMethods {

    @Test
    void test(@Autowired Service service) {
        assertThat(isActualTransactionActive()).isFalse();
        service.doSomething();
    }

    @Configuration
    @EnableTransactionManagement
    static class Config {

        @Bean
        DataSource dataSource() {
            return new EmbeddedDatabaseBuilder().generateUniqueName(true).build();
        }

        @Bean
        PlatformTransactionManager transactionManager(DataSource dataSource) {
            return new DataSourceTransactionManager(dataSource);
        }

        /**
         * Overrides {@link ProxyTransactionManagementConfiguration#transactionAttributeSource()}.
         */
        @Bean
        TransactionAttributeSource transactionAttributeSource() {
            return new AnnotationTransactionAttributeSource(false);
        }

        @Bean
        Service service() {
            return new Service();
        }
    }

    @Transactional
    static class Service {

        void doSomething() {
            assertThat(isActualTransactionActive()).isTrue();
        }
    }

}

Comment From: hrybs

Good explanation! But when we use AspectJ the annotation is only applied to public methods.

Comment From: sbrannen

But when we use AspectJ the annotation is only applied to public methods.

Yes, it's an unfortunate limitation of the predefined pointcuts in AnnotationTransactionAspect that limits the application to public methods when @Transactional is only declared at the class level.

However, non-public methods can at least be directly annotated with @Transactional when using AspectJ CTW or LTW without customization since AnnotationTransactionAspect uses new AnnotationTransactionAttributeSource(false).