前言
这一章主要理一下Mybatis里自带的三个数据源实现上的差异,不过需要注意的是,DataSource的实现不止三个,而且我们一般使用其他的数据源,比如HiKari、Druid等。
三个工厂
JNDI的比较特殊,是基于外部配置的,忽略不讲。Pooled工厂和Unpooled工厂是继承关系,主要是因为其读取配置的方式都是一致的,区别在于其管理连接资源的方式,后面会提到。
三个数据源
基于上面三个工厂,我们也主要介绍一下三个数据源
- JNDI数据源:主要是给EJB或者容器外部配置数据源,比如tomcat的context.xml文件中中配置,不常见;
- 池化数据源:链接资源初始化之后,保持活性,供不同线程使用;
- 非池化数据源:获取链接时初始化,执行完成之后销毁;
JNDI数据源的一个例子
@Test
public void testJndi() throws Exception {
InitialContext context = new InitialContext();
//查找数据源
Object datasourceRef = context.lookup("java:comp/env/jdbc/mybatis");
DataSource dataSource = (DataSource) datasourceRef;
Connection conn = dataSource.getConnection();
// 执行sql语句
ResultSet resultSet = conn.createStatement()
.executeQuery("select * from tb_image ");
log.info(">>>>>>>>>>>>>>>>>>>=>,{}", resultSet);
conn.close();
}
非连接池
非连接池工厂,这里主要看获取链接的方式,如下:
private Connection doGetConnection(Properties properties) throws SQLException {
initializeDriver();
Connection connection = DriverManager.getConnection(url, properties);
configureConnection(connection);
return connection;
}
关注点:每次都会生成新的连接,然后也不会归还链接,执行完成之后直接关闭。
连接池
这个数据源获取连接的方式如下
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
可以看到,是从一个链接集合中pop一个资源并返回,如果获取不到,则根据配置判定是否需要新建一个链接。
popConnection的具体细节,篇幅略长,下一个章节单独介绍。
归还链接
这是PooledConnection源码中的invoke方法,代码中执行数据库查询或者更新时,判断具体的执行方法,如果是执行提交、回滚或者创建Statement等,则直接处理; 如果调用conn.close(),则将链接放回池中,以供复用。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
// 如果调用conn.close(),则将链接放回池中,以供复用
if (CLOSE.equals(methodName)) {
dataSource.pushConnection(this);
return null;
}
try {
// 执行提交、回滚或者创建Statement等
return method.invoke(realConnection, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
连接池的优势
先给出结论:优势主要在于创建连接的频率。
@Test
public void testConn() throws Exception {
String sql = "select * from tb_image where md5 = ?";
StopWatch sw = new StopWatch();
Class.forName("com.mysql.jdbc.Driver");
sw.start("create conn");
Connection con = DriverManager.getConnection(
"jdbc:mysql://192.168.1.172:3306/test8", "test", "123456");
sw.stop();
System.out.println("创建连接耗时:" + sw.getLastTaskTimeMillis() + " 毫秒");
PreparedStatement st = con.prepareStatement(sql);
//设置参数
st.setString(1, "6e705a7733ac5gbwopmp02");
sw.start("execute");
//查询,得出结果集
ResultSet rs = st.executeQuery();
sw.stop();
System.out.println("查询连接耗时:" + sw.getLastTaskTimeMillis() + " 毫秒");
System.out.println(sw.prettyPrint());
}
输出结果
创建连接耗时:263 毫秒
查询连接耗时:1 毫秒
StopWatch '': running time = 264639000 ns
---------------------------------------------
ns % Task name
---------------------------------------------
263557200 100% create conn
001081800 000% execute
这个结果对比很直观,创建连接263毫秒,执行耗时1毫秒,连接池的优势显而易见。