Stefan Falk
Stefan Falk

Reputation: 25397

Spring nested transactions not working with jOOQ

I want to manage my transactions with Spring @Transactional and support nested transaction by following this example in order to do so.

My problem is if I call UserService.addAdmin() that SpringTransactionProvider.begin() and SpringTransactionProvider.commit() are not getting called for some reason which indicates that it is not working the way I'm doing it ..

I'm having my service implemented:

/*
 *
 */
public class UserService {

    private final static Logger LOGGER = Logger.getLogger(UserService.class.getName());

    private AdminRepository adminRepository;

    public UserService(DSLContext ctx) {        
        this.adminRepository = new AdminRepository(ctx);
    }

    @Transactional
    public void addAdmin(String userId) {
        DSLContext ctx = adminRepository.getCtx();
        ctx.insertInto(Admin.ADMIN)
            .set(Admin.ADMIN.USER_NAME, userId)
            .execute();
    }
}

As well as defined my configuration file servlet-context.xml

<bean id="dataSource"
    class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="org.postgresql.Driver" />
    <property name="url" value="jdbc:postgresql://localhost:5432/mz_db" />
    <property name="username" value="postgres" />
    <property name="password" value="huehuehue" />
</bean>

<!-- Configure Spring's transaction manager to use a DataSource -->
<bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
</bean>

<!-- Configure jOOQ's TransactionProvider as a proxy to Spring's transaction manager -->
<bean id="transactionProvider"
    class="com.mz.server.web.SpringTransactionProvider">
</bean>

<!-- Configure jOOQ's ConnectionProvider to use Spring's TransactionAwareDataSourceProxy,
     which can dynamically discover the transaction context -->
<bean id="transactionAwareDataSource"
    class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
    <constructor-arg ref="dataSource" />
</bean>

<bean class="org.jooq.impl.DataSourceConnectionProvider" name="connectionProvider">
    <constructor-arg ref="transactionAwareDataSource" />
</bean>

<!-- Configure the DSL object, optionally overriding jOOQ Exceptions with Spring Exceptions -->
<bean id="dsl" class="org.jooq.impl.DefaultDSLContext">
    <constructor-arg ref="config" />
</bean>

<!-- Invoking an internal, package-private constructor for the example
     Implement your own Configuration for more reliable behaviour -->
<bean class="org.jooq.impl.DefaultConfiguration" name="config">
    <property name="SQLDialect"><value type="org.jooq.SQLDialect">POSTGRES_9_4</value></property>
    <property name="connectionProvider" ref="connectionProvider" />
    <property name="transactionProvider" ref="transactionProvider" />
</bean>

<!-- BEGIN Services -->

<bean id="userService" class="com.mz.server.web.service.UserService">
    <constructor-arg>
        <ref bean="dsl" />
    </constructor-arg>
</bean>

and basically a copy of SpringTransactionProvider:

public class SpringTransactionProvider implements TransactionProvider {

    private final static Logger LOGGER = Logger.getLogger(SpringTransactionProvider.class);

    @Autowired
    DataSourceTransactionManager txMgr;

    public SpringTransactionProvider() {
        LOGGER.info("Ctor()");
    }

    @Override
    public void begin(TransactionContext ctx) {
        LOGGER.info("##### begin #####");
        TransactionStatus tx = txMgr.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_NESTED));
        ctx.transaction(new SpringTransaction(tx));
    }

    @Override
    public void commit(TransactionContext ctx) {
        LOGGER.info("##### commit #####");
        txMgr.commit(((SpringTransaction) ctx.transaction()).tx);
    }

    @Override
    public void rollback(TransactionContext ctx) {
        LOGGER.info("##### rollback #####");        
        txMgr.rollback(((SpringTransaction) ctx.transaction()).tx);
    }
}

I would have expected to see

INFO  com.mz.server.web.SpringTransactionProvider  - Ctor()
DEBUG com.mz.server.web.servlet.UserServletImpl    - Login request by userId: username
INFO  com.mz.server.web.SpringTransactionProvider  - #### begin ####
INFO  com.mz.server.web.service.UserService        - Yay!
INFO  com.mz.server.web.SpringTransactionProvider  - #### commit ####

but I'm only getting

INFO  com.mz.server.web.SpringTransactionProvider  - Ctor()
DEBUG com.mz.server.web.servlet.UserServletImpl    - Login request by userId: username
INFO  com.mz.server.web.service.UserService        - Yay!

Why does the SpringTrancationProvider not getting utilized?

Upvotes: 3

Views: 2245

Answers (1)

Lukas Eder
Lukas Eder

Reputation: 220952

jOOQ's TransactionProvider provides an implementation for the explicit jOOQ transaction API, wiring it to Spring, which you don't seem to be using. An example addAdmin() method that uses the explicit jOOQ transaction API would be this one:

// no @Transactional - no need for declarative transaction management
public void addAdmin(String userId) {
    adminRepository.getCtx().transaction(configuration -> {
        DSL.using(configuration)
           .insertInto(Admin.ADMIN)
           .set(Admin.ADMIN.USER_NAME, userId)
           .execute();
    });
}

The TransactionProvider is not involved if you use Spring's declarative @Transaction API. In other words, you don't need it.

Upvotes: 1

Related Questions