mybatis 使用 Transaction 接口封装了数据库连接 Connection 的生命周期,它由 creation、preparation、commit/rollback 和 close 组成。
public interface Transaction { Connection getConnection() throws SQLException; void commit() throws SQLException; void rollback() throws SQLException; void close() throws SQLException; }mybatis 使用两种机制来管理事物:
使用 JDBC 的事务管理机制:
即利用 java 提供的 Connection 对象完成对事务的 commit、rollback 和 close。
使用 MANAGED 的事务管理机制
这种机制 myBatis 自身不会去实现事务管理,而是让程序的容器(如 JBOSS,Spring)来实现对事务的管理。
mybatis 会根据如下配置来决定使用何种方式管理数据库事物。
<configuration> <environment> <transactionManager type="JDBC" /> </environment> </configuration>因此,mybatis 在解析配置文件 <environment /> 元素时,会生成具体的 TransactionFactory 对象。
private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { String id = child.getStringAttribute("id"); if (isSpecifiedEnvironment(id)) { TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager")); DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource")); DataSource dataSource = dsFactory.getDataSource(); Environment.Builder environmentBuilder = new Environment.Builder(id) .transactionFactory(txFactory) .dataSource(dataSource); configuration.setEnvironment(environmentBuilder.build()); } } } } private TransactionFactory transactionManagerElement(XNode context) throws Exception { if (context != null) { String type = context.getStringAttribute("type"); Properties props = context.getChildrenAsProperties(); // 获取具体的 事物工厂 TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance(); factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a TransactionFactory."); }在上面提到,mybatis 提供两种机制来管理事物:JdbcTransaction 和 ManagedTransaction。这可以看成是两种类型产品,因此 mybatis 使用了抽象工厂模式来根据不同的配置来创建具体的事物管理机制。 但是现在还只是得到能够生产 Transaction 的工厂,那么 mybatis 是从哪开始通过 TransactionFactory 来具体构建事物管理对象呢?由于 mybatis 执行数据库操作都是通过 SqlSession 来的,那么可以推测出应该是在得到 SqlSession 对象时,把这些东西都准备好。那就看看 DefaultSqlSession 的 openSession() 方法是不是跟推测的一样。
@Override public SqlSession openSession() { // 默认 executor 是 SimpleExecutor,且默认 autoCommit 为 false return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false); } private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; try { final Environment environment = configuration.getEnvironment(); // 从 Environment 对象中获取之前创建好的 TransactionFactory final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment); // 关键:生成 Transaction 对象 tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); final Executor executor = configuration.newExecutor(tx, execType); 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(); } }我们已 JdbcTransactionFactory 为例,因为 ManagedTransactionFactory 生成的 Transaction 并不会做 commit 和 rollback 操作,它会交由容器去管理,如 Spring。
@Override public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { return new JdbcTransaction(ds, level, autoCommit); }目前,我们已经知道 mybatis 是通过什么来管理事物,那么在我们具体调用 SqlSession 对象的 update 或 delete 操作它又是如何工作的呢?
@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(); } }可以看到,update 操作会设置 dirty 为 true,那这个变量又有什么用?我们可以看看 commit() 和 rollback() 方法。 PS:delete 操作其实底层调用的是 update 方法
@Override public void commit() { commit(false); } @Override 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; }在这个方法中,我们可以看到 mybatis 是通过使用三个值来决定是否需要 commit 或 rollback。默认情况下 force 为 false,而 dirty 肯定为 true,因此只要 autoCommit 为 false,就需要 commit 或 rollback,否则不需要。 rollback 的原理跟 commit 是一样的,就不再赘述了~~~
