Can not understand why choice of Dynamic or Static Bean Wiring affects on Transactional(propagation = Propagation.REQUIRES_NEW) logic.
Input: Spring Boot 3.2.3 ( the same can happen in 2.3.3+), java 21 Config :
server: port: 9118 spring: datasource: driverClassName: org.postgresql.Driver url: jdbc:postgresql://${DB_HOST:localhost}:${DB_PORT:5555}/${DB_NAME:test}?ApplicationName=test username: ${DB_USERNAME:test} password: ${DB_PASSWORD:test} hikari: maxLifetime: 120000 metrics: enable: true leakDetectionThreshold: 30000 maximumPoolSize: 3 jpa: open-in-view: false hibernate: ddl-auto: none properties: hibernate: generate_statistics: true default_schema: test dialect: org.hibernate.dialect.PostgreSQLDialect logging: level: root: INFO org.springframework.orm.jpa: DEBUG org.springframework.transaction: DEBUG com: zaxxer: hikari: DEBUG (After text descr full code, can skip)
- InnerTrx : interface, this interface implemented by two classes annotated as Service. Implemented method annotated with @Transactional(propagation = Propagation.REQUIRES_NEW).
- OuterTrx: class annotated as Service + injected InnerTrx implementation + method annotated with @Transactional. This method call InnerTrx method.
public interface InnerTrx { void test(); } @Service @RequiredArgsConstructor public class InnerTrxService implements InnerTrx { private final ExchangeRepository repository; private final HikariDataSource dataSource; @Transactional(propagation = Propagation.REQUIRES_NEW) public void test() { log.info(" inside test() method :: hikari pool : active: {} idle: {} wait :{}", dataSource.getHikariPoolMXBean().getActiveConnections(), dataSource.getHikariPoolMXBean().getIdleConnections(),dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()); repository.save(ExchangeEntity.builder() .test("test") .build()); } } @Service @RequiredArgsConstructor public class InnerTrxService2 implements InnerTrx { private final ExchangeRepository repository; private final HikariDataSource dataSource; @Transactional(propagation = Propagation.REQUIRES_NEW) public void test() { log.info(" inside test() method :: hikari pool : active: {} idle: {} wait :{}", dataSource.getHikariPoolMXBean().getActiveConnections(), dataSource.getHikariPoolMXBean().getIdleConnections(),dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()); repository.save(ExchangeEntity.builder() .test("test") .build()); } } @Repository public interface ExchangeRepository extends JpaRepository { } case: Dynamic Wiring injection of InnerTrx implementation with @Qualifier
@Slf4j @Service public class OuterTrxService { private final InnerTrx innerTrx; private final HikariDataSource dataSource; public OuterTrxService(@Qualifier("innerTrxService") InnerTrx innerTrx, HikariDataSource dataSource) { this.innerTrx = innerTrx; this.dataSource = dataSource; } @Transactional public void testOuterTrx() { log.info(" inside test() method :: hikari pool : active: {} idle: {} wait :{}", dataSource.getHikariPoolMXBean().getActiveConnections(), dataSource.getHikariPoolMXBean().getIdleConnections(), dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()); innerTrx.test(); } } when we call testOuterTrx() all is ok as expected = 2 trx = 2 active connection max:
image result log 2trx expected call
case: Static Wiring choose implemetation in configuration class using @Bean ( In my real project i choose implementation by condition from config). Now we dont need to use @Qualifier when inject
@Configuration @RequiredArgsConstructor public class TestConfiguration { private final InnerTrxService2 testService2; @Bean public InnerTrx innerTrx() { return testService2; } } @Slf4j @Service public class OuterTrxService { private final InnerTrx innerTrx; private final HikariDataSource dataSource; public OuterTrxService(InnerTrx innerTrx, HikariDataSource dataSource) { this.innerTrx = innerTrx; this.dataSource = dataSource; } @Transactional public void testOuterTrx() { log.info(" inside test() method :: hikari pool : active: {} idle: {} wait :{}", dataSource.getHikariPoolMXBean().getActiveConnections(), dataSource.getHikariPoolMXBean().getIdleConnections(), dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()); innerTrx.test(); } } when we call testOuterTrx() = 3 trx = 3 active connection max:
image result log 3trx
So i just changed Wiring type and have useless transaction with one lost connection per method call. Why is this happening?
P.S.
I know we can can solve this by not annotating test() method implemetation with @Transactional(propagation = Propagation.REQUIRES_NEW). And create additional service class, where we call test() method inside new service class method annotated with @Transactional(propagation = Propagation.REQUIRES_NEW)
@Slf4j @Service @RequiredArgsConstructor public class InnerTrxAdditionalService { private final InnerTrx innerTrx; @Transactional(propagation = Propagation.REQUIRES_NEW) public void test() { innerTrx.test(); } } @Slf4j @Service public class OuterTrxService { private final InnerTrxAdditionalService innerTrx; private final HikariDataSource dataSource; public OuterTrxService(InnerTrxAdditionalService innerTrx, HikariDataSource dataSource) { this.innerTrx = innerTrx; this.dataSource = dataSource; } @Transactional public void testOuterTrx() { log.info(" inside test() method :: hikari pool : active: {} idle: {} wait :{}", dataSource.getHikariPoolMXBean().getActiveConnections(), dataSource.getHikariPoolMXBean().getIdleConnections(), dataSource.getHikariPoolMXBean().getThreadsAwaitingConnection()); innerTrx.test(); } }
Источник: https://stackoverflow.com/questions/781 ... actionalpr