Search code examples
spring-mvcjdbctransactionsautocommit

Spring MVC JDBC DataSourceTransactionManager: Data committed even after readonly=true


I am currently developing a Spring MVC application.I have configured a JDBC TransactionManager and I am doing declarative transaction management using AOP XML.However, even if I configure the method to run on a read-only=true, it still commits the transaction.

Database : Oracle 10g

My database-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schem...ring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">


    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close">
        <property name="driverClassName" value="${driver}" />
        <property name="url" value="${url}" />
        <property name="username" value="${username}" />
        <property name="password" value="${password}" />
        <property name="defaultAutoCommit" value="false" />
    </bean>

    <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:com/mybatis/mappers/*.xml" />
    </bean>


    <!--
        the transactional advice (what 'happens'; see the <aop:advisor/> bean
        below)
    -->
    <tx:advice id="txAdvice" transaction-manager="txManager">
        <!-- the transactional semantics... -->
        <tx:attributes>
            <!-- all methods starting with 'get' are read-only -->
            <tx:method name="get*" read-only="true" />
            <!-- other methods use the default transaction settings (see below) -->
            <tx:method name="*" read-only="true" rollback-for="RuntimeException"/>
        </tx:attributes>
    </tx:advice>

    <!--
        ensure that the above transactional advice runs for any execution of
        an operation defined by the FooService interface
    -->
    <aop:config>
        <aop:pointcut id="fooServiceOperation"
            expression="execution(* com.service.EmployeeService.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="fooServiceOperation" />
    </aop:config>


</beans>

My controller

package com.service;

import java.util.List;

import com.mybatis.dao.EmployeeMapperInterface;
import com.spring.model.Employee;

public class EmployeeService implements EmployeeBaseService{

    EmployeeMapperInterface employeeMapper;

    public EmployeeMapperInterface getEmployeeMapper() {
        return employeeMapper;
    }

    public void setEmployeeMapper(EmployeeMapperInterface employeeMapper) {
        this.employeeMapper = employeeMapper;
    }

    @Override
    public Employee getEmployeeById(long empId){
        //retrieve from database
        List empList = employeeMapper.getEmployeeWithId(empId);
        if(empList != null && empList.size()>0){
            return (Employee) empList.get(0);   
        }
        return null;

    }




      @Override
    public long saveEmployee(Employee employee){
        long empId = 0l;
        if(employee.getEmpId()==0){
            empId  = new Long( employeeMapper.insertEmployee(employee));
        }else{
             employeeMapper.updateEmployee(employee);
             empId  =  employee.getEmpId();
        }
        try {
            System.out.println("gonna sleep");
            Thread.sleep(10);

        } catch (InterruptedException e) {

            e.printStackTrace();
        }
        return empId;
    }

How do I prevent the auto commit?I have also noticed that even if I don't put any transaction management code, the code still commits. Note, the transaction advice is however,invoked as when I put a no-rollback-for for RuntimeException and then do a 1/0, it correctly commits the data and rolls back if I put the same as rollback-for. I have also tried out the query timeout by putting the thread on sleep, even that doesn't work, but I figure that timeout might be for an actual query, so thats fine. Thanks in advance!


Solution

  • The answer is on Spring MVC Mybatis transaction commit

    Detailed stack traces are also available. To summarize,

    1. Read-only is only an advice and it guarantees nothing, and I would really like the Spring docs to be updated about this.
    2. whenever a query is executed in Oracle using Mybatis, it is in the context of a transaction which is automatically started, committed(or rolled back, if execption is raised),and closed by Mybatis.
    3. Logging the application was a good idea and it helped me to find out how the actual transactions are started etc

    .