mybatis-3.4.6 事务管理

系列

  • mybatis-3.4.6 配置介绍
  • mybatis-3.4.6 顶层配置解析
  • mybatis-3.4.6 子配置解析
  • mybatis-3.4.6 mapper解析
  • mybatis-3.4.6 SQL执行流程
  • mybatis-3.4.6 SqlSession执行过程
  • mybatis-3.4.6 缓存介绍
  • mybatis-3.4.6 自增主键
  • mybatis-3.4.6 foreach 自增主键
  • mybatis-3.4.6 事务管理
  • mybatis-spring 基于xml配置原理介绍


开篇

  • 这个系列是基于mybatis-3.4.6版本的源码解析,这篇文章主要分析mybatis的事务管理。
  • mybatis的事务管理与mybatis本身的SQL执行过程无关,由单独的commit操作来实现。


Class.forName("com.mysql.jdbc.Driver");

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "root", "123");
conn.setAutoCommit(false);
PreparedStatement pstm = conn.prepareStatement("insert into students(name, email) values(?, ?)",
Statement.RETURN_GENERATED_KEYS);

pstm.setString(1, "name1");
pstm.setString(2, "email1");
pstm.addBatch();
pstm.executeBatch();

// 返回自增主键值
ResultSet rs = pstm.getGeneratedKeys();
while (rs.next()) {
        Object value = rs.getObject(1);
        System.out.println(value);
    }
// 执行JDBC的commit操作
conn.commit();

rs.close();
pstm.close();
conn.close();
  • JDBC最终通过connection的commit操作来提交事务。


demo

public class MybatisHelloWorld {

    public static void main(String[] args) {

        String resouce = "configuration.xml";
        Reader reader;
        try {
            reader = Resources.getResourceAsReader(resouce);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

            SqlSession sqlSession = sqlSessionFactory.openSession();
            try {
                UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
                ImcUser imcUser = new ImcUser();
                imcUser.setUserNick("demo");
                List imcUserList = new ArrayList<>();
                imcUserList.add(imcUser);
                // 执行SQL操作
                userMapper.bachAddUser(imcUserList);
                // 提交事务操作
                sqlSession.commit();
            } finally {
                sqlSession.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • SQL的执行过程包含单独执行SQL语句和提交事务两个过程,如果不通过commit提交事务,那么在sqlSession关闭的时候会回滚提交的数据。


commit流程分析

mybatis-3.4.6 事务管理_第1张图片
事务提交流程
  • mybatis的事务提交流程如上图所示,可以重点关注下JdbcTransaction。


public class DefaultSqlSession implements SqlSession {

  public void commit(boolean force) {
    try {
      executor.commit(isCommitOrRollbackRequired(force));
      dirty = false;
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error committing transaction.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }

  private boolean isCommitOrRollbackRequired(boolean force) {
    return (!autoCommit && dirty) || force;
  }
}
  • isCommitOrRollbackRequired的判断逻辑是在force=true或者非自动提交且dirty=true的场景结果为true。表示需要提交或回滚。
public class DefaultSqlSession implements SqlSession {

  @Override
  public int insert(String statement, Object parameter) {
    return update(statement, parameter);
  }

  @Override
  public int update(String statement) {
    return update(statement, null);
  }

  @Override
  public int update(String statement, Object parameter) {
    try {
      dirty = true;
      MappedStatement ms = configuration.getMappedStatement(statement);
      return executor.update(ms, wrapCollection(parameter));
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error updating database.  Cause: " + e, e);
    } finally {
      ErrorContext.instance().reset();
    }
  }
}
  • DefaultSqlSession在执行inser/update等操作的时候dirty=true。
  • dirty=true为影响是否需要回滚的操作。


close操作回滚

public class DefaultSqlSession implements SqlSession {

  @Override
  public void close() {
    try {
      executor.close(isCommitOrRollbackRequired(false));
      closeCursors();
      dirty = false;
    } finally {
      ErrorContext.instance().reset();
    }
  }
  • close操作的过程中isCommitOrRollbackRequired用来判断是否需要回滚。
  • isCommitOrRollbackRequired方法在dirty=true且非自动提交为true
public abstract class BaseExecutor implements Executor {

    public void close(boolean forceRollback) {
    try {
      try {
        // 执行回滚操作,根据forceRollback来执行
        rollback(forceRollback);
      } finally {
        if (transaction != null) {
          transaction.close();
        }
      }
    } catch (SQLException e) {
    } finally {
    }
  }

  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {
          // 执行回滚
          transaction.rollback();
        }
      }
    }
  }
}
  • forceRollback为true的时候会执行transaction.rollback()实现回滚。


结论

  • autoCommit=false,但是没有手动commit,在sqlSession.close()时,Mybatis会将事务进行rollback()操作,然后才执行conn.close()关闭连接,当然数据最终也就没能持久化到数据库中了。


参考文章

  • 源码参考
  • mybatis官网介绍
  • 深入理解mybatis原理
  • Mybatis3.4.x技术内幕
  • mybatis 3.x源码深度解析与最佳实践

你可能感兴趣的:(mybatis-3.4.6 事务管理)