学习了解 DriverManager 与 DataSource
从校园时期的手动写jdbc,到后面上班各种带前缀的DataSource,虽然对连接数据库已经轻车熟路,不过也是知其然不知其所以然。
最近开始看源码,从简单一点的dbutils看起,加上项目试用动态数据源,于是对jdbc的底层和各种数据库连接工具,必须要有更深入一些的了解。
原生jdbc
- 注册驱动,反射方式加载驱动类
- 设置url、username、password
- 获得连接对象Connection
- 获得执行器Statement
- 获得执行结果ResultSet
- 释放资源
DataSource
这是一个可以获取数据库连接的接口,所以我们需要引用别人写好的实现工具比如dbcp、c3p0等。
获取到数据库连接Connection以后,可以采用跟上述相同的操作,当然我们不会那么做,因为有太多别人造好的轮子给我们助力,比如我现在正在看的dbutils就是比较早期的工具。
重点是数据库连接Connection的获取
1. 通过Driver获取:
Class.forName("com.mysql.jdbc.Driver")
,可以简单的理解为新建一个Driver类,就像new Driver()
com.mysql.jdbc.Driver类:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
可以看到,在类加载以后,就自动向DriverManager
中注册自身了。
DriverManager.getConnection(..)
,获取数据库连接。
无论调用哪个getConnection
的重载,最终都会调用同一个方法,所以我们分析下这个方法的源码就可以了。
DriverManager类getConnection方法:
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(\"" + url + "\")");
SQLException reason = null;
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
if (reason != null) {
println("getConnection failed: " + reason);
throw reason;
}
println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}
- callerCL 和 caller,类加载器,只需要知道新建类是需要类加载器的就行。
- for(DriverInfo aDriver : registeredDrivers),遍历已注册Driver驱动。
- isDriverAllowed(aDriver.driver, callerCL),判定该驱动是否适配该数据库(通过比较该结构中的每一个驱动对象的Class和类加载器对象是否相同,如果相同则表示该驱动适配改数据库)
- Connection con = aDriver.driver.connect(url, info),获取连接
题外话:不能用加载DriverManager的类加载器,因为DriverManager在rt.jar里面,它的类加载器是启动类加载器。而数据库的driver(com.mysql.jdbc.Driver)是放在classpath里面的,启动类加载器是不能加载的。所以,如果严格按照双亲委派模型,是没办法解决的。而这里的解决办法是:通过调用类的类加载器去加载。而如果调用类的加载器是null,就设置为线程的上下文类加载器。
2. 通过DataSource获取
BasicDataSourceFactory.createDataSource(null)
内部的源码比较复杂就不贴了,总之返回的是BasicDataSource
而BasicDataSource
获取数据库连接时,使用的是PoolingDataSource
的方法,后者里面使用的是数据库连接池。
这里简单列举了一下dbcp的方式,还有比较出名的有c3p0以及阿里的druid
小结
与别人写好的工具框架相比,java的源码实在是简单,不过作为立身之本,其主要是定接口划规范,这也是java能跨平台大行其道的重要原因。