美文网首页mybatis
mybatis-3.4.6 事务管理

mybatis-3.4.6 事务管理

作者: 晴天哥_王志 | 来源:发表于2020-07-25 15:35 被阅读0次

    系列

    开篇

    • 这个系列是基于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<ImcUser> 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的事务提交流程如上图所示,可以重点关注下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-3.4.6 事务管理

        本文链接:https://www.haomeiwen.com/subject/iyoelktx.html