浅入浅出MyBatis(12):使用事务

2015-01-14

该文章是《专题:浅入浅出MyBatis》系列文章的一篇。点击进入该专题目录

工具与环境:Intellij IDEA 14,JDK 1.7,MyBatis 3.2,MySQL 5.5。

浅入浅出MyBatis(01):查询id为1的用户的信息的基础上,我们看一下该如何实现。

关于MyBatis的配置

mybatis-config.xml内容如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/blog_db?useUnicode=true&amp;characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="letian/mybatis/mapper/UserMapper.xml"/>
    </mappers>
</configuration>

从中可以找到这样一行:

<transactionManager type="JDBC"/>

我们看一下http://mybatis.github.io/mybatis-3/zh/configuration.html给出的解释:

—摘录start—>>>

事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):

JDBC – 这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。
MANAGED – 这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。默认情况下它会关闭连接,然而一些容器并不希望这样,因此需要将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。例如:

<transactionManager type="MANAGED">
  <property name="closeConnection" value="false"/>
</transactionManager>

NOTE:如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

—摘录end—<<<

修改letian.mybatis.dao下的UserMapper.java

package letian.mybatis.dao;

import letian.mybatis.bean.User;

public interface UserMapper {

    User findById(int id);
    void insertUser(User user);
}

letian.mybatis.mapper下的UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC
        "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="letian.mybatis.dao.UserMapper">

    <select id="findById" parameterType="Integer" resultType="letian.mybatis.bean.User">
        select *
        from blog_db.user where id=#{id}
    </select>

    <insert id="insertUser" parameterType="letian.mybatis.bean.User" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO `blog_db`.`user` ( `name`, `email`, `password`)
        VALUES (#{name}, #{email}, #{password})
    </insert>

</mapper>

修改Main.java

import java.io.IOException;
import java.util.Iterator;
import java.util.List;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import letian.mybatis.bean.User;
import letian.mybatis.dao.UserMapper;


public class Main {

    public static void main(String[] args) throws IOException {

        SqlSessionFactory sessionFactory;
        sessionFactory = new SqlSessionFactoryBuilder()
                .build(Resources.getResourceAsReader("mybatis-config.xml"));

        SqlSession sqlSession = sessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        User user1 = new User();
        user1.setName("xiaowei");
        user1.setEmail("xiaowei@111.com");
        user1.setPassword("456");

        User user2 = new User();
        user2.setName("xiaosi");
        user2.setEmail("xiaosi@111.com");
        user2.setPassword("456");

        try {
            userMapper.insertUser(user1);
            System.out.println(userMapper.findById(user1.getId()));
            userMapper.insertUser(user2);
            System.out.println(userMapper.findById(user2.getId()));
            System.out.println("提交事务");
            sqlSession.commit();
            System.out.println("事务成功执行");
        } catch(Exception e) {
            System.out.println("事务执行失败,回滚");
            sqlSession.rollback();

        } finally {
            sqlSession.close();
        }

    }
}

sessionFactoryopenSession多个重载方法中有以下两个方法:

SqlSession openSession()
SqlSession openSession(boolean autoCommit)

不带参数的openSession()方法默认不开启autoCommit。openSession(boolean autoCommit)根据参数是true还是false来判断是否开启autoCommit。如果没有开启autoCommit,那么在insert、update、delete操作后,需要显式地调用commit()方法;如果开启了autoCommit,那么每个执行的sql语句都被看作一个事务来处理,且不需要显式地调用commit()方法。

user表的name字段要求是unique

当前user表里面有以下数据:

mysql> select * from blog_db.user;
+----+--------+----------------+----------+
| id | name   | email          | password |
+----+--------+----------------+----------+
|  1 | letian | letian@111.com | 123      |
|  2 | xiaosi | xiaosi@111.com | 123      |
+----+--------+----------------+----------+
2 rows in set (0.00 sec)

Main.java是想在一个事务里将对象user1和user2放入user表中。但是由于对象user2的name是xiaosi,和表中已有数据重复,因此在sqlSession.commit()时候会产生异常,导致事务失败。

Main.java的执行结果如下:

User{id=13, name='xiaowei', email='xiaowei@111.com', password='456'}
事务执行失败,回滚