<?xml version="1.0" encoding="UTF-8"?>
<!--

    Copyright ${license.git.copyrightYears} the original author or authors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<document xmlns="http://maven.apache.org/XDOC/2.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">

  <properties>
    <title>MyBatis-Spring | Transactions</title>
    <author email="hpresnall@gmail.com">Hunter Presnall</author>
    <author email="eduardo.macarron@gmail.com">Eduardo Macarron</author>
  </properties>
                             
  <body>
    <section name="Transactions">
      <p>
        One of the primary reasons for using MyBatis-Spring is that it allows
        MyBatis to participate in Spring transactions. Rather than create a new
        transaction manager specific to MyBatis, MyBatis-Spring leverages the
        existing <code>DataSourceTransactionManager</code> in Spring.
      </p>
      <p>
        Once a Spring transaction manager is configured, you can configure
        transactions in Spring as you normally would. Both
        <code>@Transactional</code> annotations and AOP style
        configurations are supported. A single <code>SqlSession</code>
        object will be created and used for the duration of the transaction. This
        session will be committed or rolled back as appropriate when then
        transaction completes.
      </p>
      <p>
        MyBatis-Spring will transparently manage transactions once they are set up.
        There is no need for additional code in your DAO classes.
      </p>
  
    <subsection name="Standard Configuration" id="configuration">
      <p>
        To enable Spring transaction processing, simply create a
        <code>DataSourceTransactionManager</code> in your Spring
        configuration file:
      </p>

        <source><![CDATA[
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <constructor-arg ref="dataSource" />
</bean>]]></source>

      <source><![CDATA[
@Bean
public DataSourceTransactionManager transactionManager() {
  return new DataSourceTransactionManager(dataSource());
}]]></source>

      <p>
        The <code>DataSource</code> specified can be any JDBC
        <code>DataSource</code> you would normally
        use with Spring. This includes connection pools as well as <code>DataSource</code>s
        obtained through JNDI lookups.
      </p>
      <p>
        Note that the <code>DataSource</code> specified for the transaction
        manager <strong>must</strong> be the same one that is used to create
        the <code>SqlSessionFactoryBean</code> or transaction management will
        not work.
      </p>
    </subsection>

    <subsection name="Container Managed Transactions" id="container">
      <p>
        If you are using a JEE container and would like Spring to participate in
        container managed transactions (CMT), then Spring should be configured
        with a <code>JtaTransactionManager</code> or one of its container
        specific subclasses. The easiest way to do this is to use the Spring
        transaction namespace or the <code>JtaTransactionManagerFactoryBean</code>:
      </p>
      <source><![CDATA[<tx:jta-transaction-manager />]]></source>

      <source><![CDATA[
@Bean
public JtaTransactionManager transactionManager() {
  return new JtaTransactionManagerFactoryBean().getObject();
}]]></source>

      <p>
        In this configuration, MyBatis will behave like any other Spring
        transactional resource configured with CMT. Spring will automatically use
        any existing container transaction and attach an <code>SqlSession</code> to it. If no
        transaction is started and one is needed based on the transaction
        configuration, Spring will start a new container managed transaction.
      </p>
      <p>
        Note that if you want to use CMT and do <strong>not</strong> want to
        use Spring transaction management, you <strong>must not</strong>
        configure any Spring transaction manager and you <strong>must</strong>
        also configure the <code>SqlSessionFactoryBean</code> to use the base
        MyBatis <code>ManagedTransactionFactory</code>:
      </p>
      <source><![CDATA[<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="transactionFactory">
    <bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" />
  </property>  
</bean>]]></source>

      <source><![CDATA[
@Bean
public SqlSessionFactory sqlSessionFactory() {
  SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
  factoryBean.setDataSource(dataSource());
  factoryBean.setTransactionFactory(new ManagedTransactionFactory());
  return factoryBean.getObject();
}]]></source>
    </subsection>

    <subsection name="Programmatic Transaction Management" id="programmatic">
      <p>
        MyBatis <code>SqlSession</code> provides you with specific methods to handle
        transactions programmatically. But when using MyBatis-Spring your beans will be
        injected with a Spring managed <code>SqlSession</code> or a Spring managed mapper.
        That means that Spring will <strong>always</strong> handle your transactions.
      </p>
      <p>
        You cannot call <code>SqlSession.commit()</code>, <code>SqlSession.rollback()</code>
        or <code>SqlSession.close()</code> over a Spring managed <code>SqlSession</code>.
        If you try to do so, a <code>UnsupportedOperationException</code> exception
        will be thrown. Note these methods are not exposed in injected mapper classes.
      </p>
      <p>
        Regardless of your JDBC connection's autocommit setting, any execution of a
        <code>SqlSession</code> data method or any call to a mapper
        method outside a Spring transaction will be automatically committed.
      </p>
      <p>
        If you want to control your transactions programmatically please refer to
        <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/data-access.html#transaction-programmatic">the Spring reference document(Data Access -Programmatic transaction management-)</a>.
        This code shows how to handle a transaction manually using the <code>PlatformTransactionManager</code>.
      </p>
        <source><![CDATA[
TransactionStatus txStatus =
    transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
  userMapper.insertUser(user);
} catch (Exception e) {
  transactionManager.rollback(txStatus);
  throw e;
}
transactionManager.commit(txStatus);]]></source>

      <p>
        You can omit to call the <code>commit</code> and <code>rollback</code> method using the <code>TransactionTemplate</code>.
      </p>

      <source><![CDATA[
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.execute(txStatus -> {
  userMapper.insertUser(user);
  return null;
});]]></source>

      <p>
        Notice that this code uses a mapper, but it will also work with a <code>SqlSession</code>.
      </p>
      </subsection>
    </section>
  </body>
</document>
