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)
.