diff --git a/docs/Mybatis/基础支持层/2、DataSource及Transaction.md b/docs/Mybatis/基础支持层/2、DataSource及Transaction.md index f600e73..1339e5f 100644 --- a/docs/Mybatis/基础支持层/2、DataSource及Transaction.md +++ b/docs/Mybatis/基础支持层/2、DataSource及Transaction.md @@ -1,4 +1,4 @@ -在数据持久层,数据源和事务是两个非常重要的组件,对数据持久层的影响很大,在实际开发中,一般会使用mybatis集成第三方数据源组件,如:c3p0、Druid,另外,mybatis也提供了自己的数据源实现。而事务方面,一般使用spring进行事务的管理。下面我们看一下mybatis是如何对这两部分进行封装的。 +在数据持久层,数据源和事务是两个非常重要的组件,对数据持久层的影响很大,在实际开发中,一般会使用mybatis集成第三方数据源组件,如:c3p0、Druid,另外,mybatis也提供了自己的数据库连接池实现,本文会通过mybatis的源码实现来了解数据库连接池的设计。而事务方面,一般使用spring进行事务的管理,这里不做详细分析。下面我们看一下mybatis是如何对这两部分进行封装的。 ## 1 DataSource 常见的数据源都会实现javax.sql.DataSource接口,mybatis中提供了两个该接口的实现类,分别是:PooledDataSource和UnpooledDataSource,并使用不同的工厂类分别管理这两个类的对象。 ### 1.1 DataSourceFactory @@ -701,5 +701,285 @@ public class PooledDataSource implements DataSource { } ``` ## 2 Transaction +遵循接口-实现类的设计原则,mybatis也是先使用Transaction接口对数据库事务做了抽象,而实现类则只提供了两个,即:JdbcTransaction和ManagedTransaction。这两种对象的获取,使用了两个对应的工厂类JdbcTransactionFactory和ManagedTransactionFactory。 +不过一般我们并不会使用mybatis管理事务,而是将mybatis集成到spring,由spring进行事务的管理。细节部分会在后面的文章中详细讲解。 +```java +public interface Transaction { + + /** + * 获取连接对象 + */ + Connection getConnection() throws SQLException; + + /** + * 提交事务 + */ + void commit() throws SQLException; + + /** + * 回滚事务 + */ + void rollback() throws SQLException; + + /** + * 关闭数据库连接 + */ + void close() throws SQLException; + + /** + * 获取配置的事务超时时间 + */ + Integer getTimeout() throws SQLException; +} + +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; + } + + // 提交、回滚、关闭等操作的代码都比较简单,只对原生的JDBC操作做了简单封装 + @Override + public void commit() throws SQLException { + if (connection != null && !connection.getAutoCommit()) { + if (log.isDebugEnabled()) { + log.debug("Committing JDBC Connection [" + connection + "]"); + } + connection.commit(); + } + } + + @Override + public void rollback() throws SQLException { + if (connection != null && !connection.getAutoCommit()) { + if (log.isDebugEnabled()) { + log.debug("Rolling back JDBC Connection [" + connection + "]"); + } + connection.rollback(); + } + } + + @Override + public void close() throws SQLException { + if (connection != null) { + resetAutoCommit(); + if (log.isDebugEnabled()) { + log.debug("Closing JDBC Connection [" + connection + "]"); + } + connection.close(); + } + } + + protected void setDesiredAutoCommit(boolean desiredAutoCommit) { + try { + if (connection.getAutoCommit() != desiredAutoCommit) { + if (log.isDebugEnabled()) { + log.debug("Setting autocommit to " + desiredAutoCommit + " on JDBC Connection [" + connection + "]"); + } + connection.setAutoCommit(desiredAutoCommit); + } + } catch (SQLException e) { + // Only a very poorly implemented driver would fail here, + // and there's not much we can do about that. + throw new TransactionException("Error configuring AutoCommit. " + + "Your driver may not support getAutoCommit() or setAutoCommit(). " + + "Requested setting: " + desiredAutoCommit + ". Cause: " + e, e); + } + } + + protected void resetAutoCommit() { + try { + if (!connection.getAutoCommit()) { + // MyBatis does not call commit/rollback on a connection if just selects were performed. + // Some databases start transactions with select statements + // and they mandate a commit/rollback before closing the connection. + // A workaround is setting the autocommit to true before closing the connection. + // Sybase throws an exception here. + if (log.isDebugEnabled()) { + log.debug("Resetting autocommit to true on JDBC Connection [" + connection + "]"); + } + connection.setAutoCommit(true); + } + } catch (SQLException e) { + if (log.isDebugEnabled()) { + log.debug("Error resetting autocommit to true " + + "before closing the connection. Cause: " + e); + } + } + } + + protected void openConnection() throws SQLException { + if (log.isDebugEnabled()) { + log.debug("Opening JDBC Connection"); + } + connection = dataSource.getConnection(); + if (level != null) { + connection.setTransactionIsolation(level.getLevel()); + } + setDesiredAutoCommit(autoCommit); + } + @Override + public Integer getTimeout() throws SQLException { + return null; + } + +} + +public class ManagedTransaction implements Transaction { + + private static final Log log = LogFactory.getLog(ManagedTransaction.class); + + // 数据源 + private DataSource dataSource; + // 事务隔离级别 + private TransactionIsolationLevel level; + // 对应的数据库连接 + private Connection connection; + // 控制是否关闭持有的连接,在close()方法中用其判断是否真的关闭连接 + private final boolean closeConnection; + + // 本类的实现也很简单,commit、rollback方法都是空实现 + public ManagedTransaction(Connection connection, boolean closeConnection) { + this.connection = connection; + this.closeConnection = closeConnection; + } + + public ManagedTransaction(DataSource ds, TransactionIsolationLevel level, boolean closeConnection) { + this.dataSource = ds; + this.level = level; + this.closeConnection = closeConnection; + } + + @Override + public Connection getConnection() throws SQLException { + if (this.connection == null) { + openConnection(); + } + return this.connection; + } + + @Override + public void commit() throws SQLException { + // Does nothing + } + + @Override + public void rollback() throws SQLException { + // Does nothing + } + + @Override + public void close() throws SQLException { + if (this.closeConnection && this.connection != null) { + if (log.isDebugEnabled()) { + log.debug("Closing JDBC Connection [" + this.connection + "]"); + } + this.connection.close(); + } + } + + protected void openConnection() throws SQLException { + if (log.isDebugEnabled()) { + log.debug("Opening JDBC Connection"); + } + this.connection = this.dataSource.getConnection(); + if (this.level != null) { + this.connection.setTransactionIsolation(this.level.getLevel()); + } + } + + @Override + public Integer getTimeout() throws SQLException { + return null; + } +} + +public interface TransactionFactory { + + /** + * 配置TransactionFactory对象,一般会在完成TransactionFactory对象 + * 初始化之后 就进行自定义属性配置 + */ + default void setProperties(Properties props) { + // NOP + } + + /** + * 在指定的数据库连接上创建Transaction事务对象 + */ + Transaction newTransaction(Connection conn); + + /** + * 从指定数据源获取数据库连接,并在此连接上创建Transaction对象 + */ + Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit); +} + +public class JdbcTransactionFactory implements TransactionFactory { + + @Override + public Transaction newTransaction(Connection conn) { + return new JdbcTransaction(conn); + } + + @Override + public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { + return new JdbcTransaction(ds, level, autoCommit); + } +} + +public class ManagedTransactionFactory implements TransactionFactory { + + private boolean closeConnection = true; + + @Override + public void setProperties(Properties props) { + if (props != null) { + String closeConnectionProperty = props.getProperty("closeConnection"); + if (closeConnectionProperty != null) { + closeConnection = Boolean.valueOf(closeConnectionProperty); + } + } + } + + @Override + public Transaction newTransaction(Connection conn) { + return new ManagedTransaction(conn, closeConnection); + } + + @Override + public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) { + // Silently ignores autocommit and isolation level, as managed transactions are entirely + // controlled by an external manager. It's silently ignored so that + // code remains portable between managed and unmanaged configurations. + return new ManagedTransaction(ds, level, closeConnection); + } +} +``` \ No newline at end of file