这里讲述的相关信息是单纯MyBatis相关的,不涉及Spring,代码是基于mybatis-3.4.6,MyBatis之所以能够配置第三方的连接池,主要得益于java在数据库连接上作的规范接口,如javax.sql.DataSource,这个接口只有两个方法:
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password) throws SQLException;
}
由此可见,Java认为数据源就是一个提供特定数据库连接的接口,除此以外,不提供任何功能,当然,连接的管理、关闭等由数据源具体实现去做,数据源的含义必须理解透彻。
MyBatis针对DataSource接口做了两个实现:
UnpooledDataSource所作的工作就是直接通过DriverManager取得连接并返回,PooledDataSource就会维护一个连接池,这个池是PoolState,作为数据成员存在于PooledDataSource中,从PooledDataSource中取连接和关闭连接都是PoolState操作完成的,同时,创建新连接是使用UnpooledDataSource,也即PooledDataSource会聚合UnpooledDataSource作为数据成员。
接下来我们讨论下MyBatis使用第三方连接池的原理,这里先来看下一个不使用Spring的Java工程中MyBatis的主配置文件:
配置当中DruidDataSourceFactory是自定义的一个类,
public class DruidDataSourceFactory extends UnpooledDataSourceFactory{
public DruidDataSourceFactory(){
this.dataSource = new DruidDataSource();
}
}
从这里可以看出,Druid连接池就是以数据源形式嵌入MyBatis的,那么在MyBatis中谁持有DataSource,就是入口了,这个类就是java.sql.Connection.Transaction的实现类,MyBatis提供了两个实现类:
其中JdbcTransaction对应的是原生jdbc事务的使用,ManagedTransaction是将事务托管给web容器的,如WebLogic等,这个实现刚好对应了我们上面配置的
在Java接口规范中,事务相关功能是聚合在Connection里面的,或者说,编程人员是通过Connection接口来使用jdbc事务的,所以MyBatis的设计中,JdbcTransaction就是聚合了Connection和DataSource,以此来封装事务能力,那么我们每次使用SqlSession来执行sql时就必然使用到Connection,在SqlSession中有个方法可以获取连接:
@Override
public Connection getConnection() {
try {
return executor.getTransaction().getConnection();
} catch (SQLException e) {
throw ExceptionFactory.wrapException("Error getting a new connection. Cause: " + e, e);
}
}
从这里可以看出,连接被封装在Executor中的Transaction中,那我们来看看Executor接口是什么:
public interface Executor {
ResultHandler NO_RESULT_HANDLER = null;
int update(MappedStatement ms, Object parameter) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey cacheKey, BoundSql boundSql) throws SQLException;
List query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
Cursor queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException;
List flushStatements() throws SQLException;
void commit(boolean required) throws SQLException;
void rollback(boolean required) throws SQLException;
CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql);
boolean isCached(MappedStatement ms, CacheKey key);
void clearLocalCache();
void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class> targetType);
Transaction getTransaction();
void close(boolean forceRollback);
boolean isClosed();
void setExecutorWrapper(Executor executor);
}
可以看到,MyBatis将数据库操作封装到Executor中,Executor接口有一个抽象实现和一个具体实现类:
BaseExecutor抽象类中聚合了Transaction,到这里,我们基本上把整个流程串起来了,MyBatis中,首先通过SqlSessionFactory获取SqlSession,由SqlSession来执行sql语句,SqlSession中由Excutor实现sql语句执行,Excutor中聚合Transaction,Transaction聚合了Connection和DataSource,能够获取到连接,所有的语句最终交给Connection实现,Connection的具体实现就是jdbc驱动完成的了,其实底层所有工作还是jdbc驱动做的,要理解这个,必须要有jdbc编程经验,否则无法真正快速理解,MyBatis中封装的Connection只是jdbc Connection的一个引用而已。