系列
开篇
- 这个系列是基于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流程分析
![](https://img.haomeiwen.com/i6302559/9e6d770af4eaca12.jpg)
事务提交流程
- 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()关闭连接,当然数据最终也就没能持久化到数据库中了。
参考文章
网友评论