一、MyBatis数据源DataSource分类
MyBatis把数据源DataSource分为三种:
- UNPOOLED 不使用连接池的数据源
- POOLED 使用连接池的数据源
- JNDI 使用JNDI实现的数据源,从tomcat中获取一个内置的数据库连接池
MyBatis内部定义了实现java.sql.DataSource接口的UnpooledDataSource,PooledDataSource类来表示UNPOOLED、POOLED类型的数据源。
二、数据源DataSource的创建过程
DataSource对象的创建发生在MyBatis初始化的过程中。
在XML配置文件中,使用<dataSource>
元素来配置数据源:
<dataSource type="POOLED">
<!-- 配置数据库信息 -->
<property name="driver" value="${mysqlDriver}"/>
<property name="url" value="${mysqlURL}"/>
<property name="username" value="${mysqlUser}"/>
<property name="password" value="${mysqlPaw}"/>
</dataSource>
- MyBatis在初始化时,解析此文件,根据的type属性来创建相应类型的的数据源DataSource。
- MyBatis是通过工厂模式来创建数据源DataSource对象的,其定义了抽象的工厂接口 : DataSourceFactory, 通过其 getDataSource()方法返回数据源DataSource:
public interface DataSourceFactory {
void setProperties(Properties props);
DataSource getDataSource(); //生产DataSource
}
上述三种类型,对应一下DateSource工厂
3. 创建DataSource实例后,将其放到Configuration对象内的Environment 中, 供以后使用。
三、DataSource什么时候创建Connection对象
当需要创建SqlSession对象并需要执行SQL语句时,MyBatis才会去调用dataSource对象来创建java.sql.Connection对象。即Connection对象的创建一直延迟到执行SQL语句。触发以下方法:
protected void openConnection() throws SQLException {
if (log.isDebugEnabled())
log.debug("Opening JDBC Connection");
connection = dataSource.getConnection();
if (level != null)
connection.setTransactionIsolation(level.getLevel());
setDesiredAutoCommit(autoCommmit);
}
四、不使用连接池的UnpooledDataSource
使用UnpooledDataSource实例的getConnection()方法, 每调用一次都会通过DriverManager. getConnection() 返回新的Connection实例。
public Connection getConnection() throws SQLException
{
return doGetConnection(username, password);
}
/*
* 获取数据连接
*/
private Connection doGetConnection(Properties properties) throws SQLException
{
//1.初始化驱动
initializeDriver();
//2.从DriverManager中获取连接,获取新的Connection对象
Connection connection = DriverManager.getConnection(url, properties);
//3.配置connection属性
configureConnection(connection);
return connection;
}
UnpooledDataSource会做以下事情:
- 初始化驱动:判断driver驱动是否已经加载到内存中,如果还没有,则会动态地加载driver类,并实例化一个Driver对象,使用DriverManager.registerDriver()将其注册到内存中,供后续使用
- 创建Connection对象:使用DriverManager.getConnection()方法创建连接。
- 配置Connection对象:设置是否自动提交autoCommit和隔离级别isolationLevel。
- 返回Connection对象。
五、使用连接池的PooledDataSource
PooledDataSource 将Connection对象包裹成PooledConnection对象放到容器中维护。 存在两种状态: 空闲状态(idle)和活动状态(active),这些对象分别被存储到容器内的idleConnections和activeConnections两个List集合中。
使用时,会优先从空闲集合中取PooledConnection对象,若没有,则看活动集合是否已满,未满就创建一个连接,添加到活动集合中,若已满,则做先进入集合的对象是否已过期,若没有过期,则线程等待。(在synchronized同步代码块中执行)
![在这里插入图片描述](https://img-blog.csdnimg.cn/20200508104452251.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzE5MTc0Nw==,size_16,color_FFFFFF,t_70)
怎样实现Connection对象调用了close()方法,而实际是将其添加到连接池中?
使用代理模式,为真正的Connection对象创建一个代理对象,当代理对象执行close()方法时,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。
PooledConnection对象实现了InvocationHandler接口,proxyConnection对象也是根据这个它来生成的代理对象,当调用PooledDataSource的getConnection()方法返回的就是这个代理对象。当调用close方法时,会调用代理对象的invoke方法。
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
String methodName = method.getName();
//当调用关闭的时候,回收此Connection到PooledDataSource中
if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
} else {
try {
if (!Object.class.equals(method.getDeclaringClass())) {
checkConnection();
}
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);