动力节点首页 全国咨询热线:400-8080-105

绑定手机号,登录
手机号

验证码

微信登录
手机号登录
手机号

验证码

微信登录与注册
微信扫码登录与注册

扫码关注微信公众号完成登录与注册
手机号登录
首页 > 文章

MyBatis的事务管理

06-19 16:02 815浏览
举报 T字号
  • 大字
  • 中字
  • 小字

 MyBatis在结合Spring或SpringBoot时会将事务功能交由后者来进行管理,但这并不妨碍我们对它的学习,不然看着mybatis-config.xml中的<transactionManager type="JDBC"/>配置项总会觉得这个是咋个玩起来了,总体来说MyBatis的事务管理还是很简单的。

Demo示例:

想要使用MyBatis事务功能的话我们在环境变量配置的时候一般需要指定事务管理器以及类型,即上面提到的<transactionManager type="JDBC"/>, 所以,我么上面提到的type的值有两种,除了JDBC类型,还有MANAGED类型,我们一般是使用前者,JDBC类型是默认使用JDBC提供的事务管理功能,MANAGED是将事务管理交由容器处理,在MyBatis中,接下来,我们开始以JDBC类型的的常规使用作为入口进行分析:


public class TransactionTest {
    public static void main(String[] args) throws Exception {
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 创建sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();
        try {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = userMapper.getUserById("1");
            System.out.println(user.toString());
            doUpdateTest(userMapper, "1");
            sqlSession.commit();
        } catch (Exception e) {
            sqlSession.rollback();
        } finally {
            sqlSession.close();
        }
    }

    private static Integer doUpdateTest(UserMapper mapper, String id) {
        User u = new User();
        u.setId(id);
        u.setUserName("小明");
        u.setPassword("123456789");
        u.setGender("男");
        u.setTelephone("1234567890");
        u.setEmail("xxx@126.com");
        u.setAddress("四川成都");
        return mapper.updateUser(u);
    }
}

事务的被抽象成接口,目前具体实有两种实现模式,以继承图方式看一下:

我们要知道,在MyBatis中,事务功能是默认开启的,所以我们就需要知道事务是何时开启,何时使用?源码之下无秘密,在openSession阶段,为当前SqlSession注入了事务能力,TransactionIsolationLevel是对应数据库的四种隔离级别,我们可以手动指定:

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
    Transaction tx = null;
    try {
      final Environment environment = configuration.getEnvironment();
      final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); 
      // 创建对应的实例jdbcTransaction或者ManagedTransaction
      tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
      final Executor executor = configuration.newExecutor(tx, execType);
      // 返回我们熟悉的DefaultSqlSession实例对象
      return new DefaultSqlSession(configuration, executor, autoCommit);
    } catch (Exception e) {
      closeTransaction(tx); // may have fetched a connection so lets call close()
      throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

正如我们前面所说,指定为JDBC事务是使用JDBC自身事务功能来实现的,MyBatis只是封装了而已:

public class JdbcTransaction implements Transaction {

  private static final Log log = LogFactory.getLog(JdbcTransaction.class);

  protected Connection connection;
  protected DataSource dataSource;
  protected TransactionIsolationLevel level;
  protected boolean autoCommit;

  public JdbcTransaction(DataSource ds, TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    dataSource = ds;
    level = desiredLevel;
    autoCommit = desiredAutoCommit;
  }

  public JdbcTransaction(Connection connection) {
    this.connection = connection;
  }

  @Override
  public Connection getConnection() throws SQLException {
    if (connection == null) {
      openConnection();
    }
    return connection;
  }

  @Override
  public void commit() throws SQLException {
    // 提交事务
    if (connection != null && !connection.getAutoCommit()) {
      connection.commit();
    }
  }

  @Override
  public void rollback() throws SQLException {
    // 回滚
    if (connection != null && !connection.getAutoCommit()) {
      connection.rollback();
    }
  }

  @Override
  public void close() throws SQLException {
     // 关闭
    if (connection != null) {
      resetAutoCommit();
      connection.close();
    }
  }

  protected void setDesiredAutoCommit(boolean desiredAutoCommit) {
      if (connection.getAutoCommit() != desiredAutoCommit) {
        connection.setAutoCommit(desiredAutoCommit);
      }
  }

  protected void resetAutoCommit() {
      if (!connection.getAutoCommit()) {   
        connection.setAutoCommit(true);
      } 
  }

  protected void openConnection() throws SQLException {
    connection = dataSource.getConnection();
    if (level != null) {
      connection.setTransactionIsolation(level.getLevel());
    }
    setDesiredAutoCommit(autoCommit);
  }
}

ManagedTransaction实现类:通过容器来进行事务管理,它对事务提交和回滚并不会做任何操作,源码如下:

  @Override
  public void commit() throws SQLException {
    // Does nothing
  }

  @Override
  public void rollback() throws SQLException {
    // Does nothing
  }

我们都知道,MySQL数据库的默认隔离级别是可重复读,是通过next-keys技术解决幻读问题的,客户端查看一下:

mysql> select @@global.tx_isolation,@@tx_isolation;
+-----------------------+-----------------+
| @@global.tx_isolation | @@tx_isolation  |
+-----------------------+-----------------+
| REPEATABLE-READ       | REPEATABLE-READ |
+-----------------------+-----------------+

接下来,数据库的事务隔离级别的设置可分为全局和当前会话两种,我们在客户端如果使用默认的隔离级别那么请求传递到数据库服务端对当前请求会话其实就是使用了global全局的隔离级别:

public enum TransactionIsolationLevel {
  NONE(Connection.TRANSACTION_NONE),
  READ_COMMITTED(Connection.TRANSACTION_READ_COMMITTED),
  READ_UNCOMMITTED(Connection.TRANSACTION_READ_UNCOMMITTED),
  REPEATABLE_READ(Connection.TRANSACTION_REPEATABLE_READ),
  SERIALIZABLE(Connection.TRANSACTION_SERIALIZABLE);

  private final int level;

  TransactionIsolationLevel(int level) {
    this.level = level;
  }

  public int getLevel() {
    return level;
  }
}

MyBatis事务管理的核心就是有两种管理方式:JDBC和MANAGED,前者是使用原生JDBC事务管理,后者是交由JBoss这类的容器管理,在结合Spring的时候,再继续补充,因为那个时候就完全就是使用Spring的统一事务管理器来进行事务的管理了。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵652
程序员代码小兵652

113篇文章贡献392215字

相关课程 更多>

作者相关文章更多>

推荐相关文章更多>

Java面试题及答案整理

提枪策马乘胜追击04-21 20:01

Spring常见面试题

代码小兵92504-17 16:07

Java零基础实战项目——五子棋

代码小兵98804-25 13:57

Java string类详解

杨晶珍05-11 14:54

6道经典算法面试题

杨晶珍05-12 16:39

发评论

举报

0/150

取消