How to use @Transactional annotation in mybatis-spring?

Searene picture Searene · Dec 7, 2016 · Viewed 11.2k times · Source

I tried to use @Transactional annotation in spring and mybatis using mybatis-spring dependency. This is the service layer.

@Service
public class OracleService {

    @Autowired
    private TestMapper mapper;

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Transactional(propagation = Propagation.REQUIRED,
                   rollbackFor = Exception.class,
                   value = "transactionManager")
    void insert1() {
        TestModel testModel = new TestModel(1, "title1", "content1");
        mapper.insert(testModel);
        throw new RuntimeException();
    }
}

As you can see, a RuntimeException is thrown out in insert1(), so the insert operation should fail. But in fact, it didn't. The record was inserted successfully. Why?

Here is my main method.

public class Launcher {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("spring-config.xml");
        OracleService oracleService = cxt.getBean(OracleService.class);
        try {
            oracleService.insert1();
        } catch(Exception e) {
            System.out.println("Exception occurred!");
        }
    }
}

Spring configuration.

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

       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">


    <context:component-scan base-package="com.database.learn"/>

    <bean id="oracleDataSource" class="oracle.jdbc.pool.OracleDataSource">
        <property name="URL" value="OracleJdbcUrl"/>
        <property name="user" value="user"/>
        <property name="password" value="password"/>
    </bean>

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="oracleDataSource"/>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

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

    <bean id="testMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.database.learn.TestMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager" />
</beans>

I used the following dependencies in pom.xml

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.1</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.0</version>
    </dependency>
    <dependency>
        <groupId>com.oracle</groupId>
        <artifactId>ojdbc6</artifactId>
        <version>11.2.0.3</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.2.RELEASE</version>
    </dependency>

Answer

Searene picture Searene · Dec 7, 2016

As @M.Deinum metioned, I have to make the method where @Transactional applied public, in other words, I have to change

@Transactional(propagation = Propagation.REQUIRED)
void insert1() {
    TestModel testModel = new TestModel(1, "title1", "content1");
    mapper.insert(testModel);
    throw new RuntimeException();
}

to

@Transactional(propagation = Propagation.REQUIRED)
public void insert1() {
    TestModel testModel = new TestModel(1, "title1", "content1");
    mapper.insert(testModel);
    throw new RuntimeException();
}

The reason is written in the spring documentation.

Method visibility and @Transactional

When using proxies, you should apply the @Transactional annotation only to methods with public visibility. If you do annotate protected, private or package-visible methods with the @Transactional annotation, no error is raised, but the annotated method does not exhibit the configured transactional settings. Consider the use of AspectJ (see below) if you need to annotate non-public methods.