前一篇地址:jdbc详解:1、创建数据库connection连接
先上代码
static String driverName = "com.mysql.jdbc.Driver";
static String url = "jdbc:mysql://127.0.0.1:3306/mysql";
static String username = "root";
static String password = "";
@Test
public void getConnection1() {
DriverManager.setLogWriter(new PrintWriter(System.out));
try {
// 1、加载驱动,不加载驱动依然正常可以连接
// Class.forName(driverName);
// 2、获取connection
Connection conn = DriverManager.getConnection(url, username, password);
// 3、依然可以获取链接
System.out.println(conn);
// 查看已经加载的driver
Enumeration<Driver> drivers = DriverManager.getDrivers();
System.out.println("------加载的diver--------");
while(drivers.hasMoreElements()) {
System.out.println(drivers.nextElement().getClass().getName());
}
} catch (Exception e) {
e.printStackTrace();
}
}
上面代码,没有手动加载驱动,但是依然可以获取连接
控制台输出信息
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql")
trying com.mysql.jdbc.Driver
getConnection returning com.mysql.jdbc.Driver
com.mysql.jdbc.JDBC4Connection@72d8c235
------加载的diver--------
com.mysql.jdbc.Driver
com.mysql.fabric.jdbc.FabricMySQLDriver
从输出可以看到,DriverManager自动加载了com.mysql.jdbc.Driver和com.mysql.fabric.jdbc.FabricMySQLDriver这两个驱动,这也可以说明DriverManager可以管理多个驱动。
下面我们来看一下DriverManager的源码:
public class DriverManager {
static {
// 类加载时候就进行了加载数据库操作
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
//其他代码省略
private static void loadInitialDrivers() {
String drivers;
try {
// AccessController.doPrivileged这个方法可以看做临时扩大该类的权限
// 读取系统jdbc.drivers的配置
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers");
}
});
} catch (Exception ex) {
drivers = null;
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 扫描 java.sql.Driver的实现类,并加载
// 加载就按照前一篇文章一样进行驱动注册
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
// 加载System中的jdbc.drivers参数指定的驱动,可以是多个驱动,以“:”分割
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
}
加载mysql和oracle驱动,分别连接到mysql和oracle数据库
static String MYSQL_DRIVERNAME = "com.mysql.jdbc.Driver";
static String MYSQL_URL = "jdbc:mysql://127.0.0.1:3306/mysql";
static String MYSQL_USERNAME = "root";
static String MYSQL_PASSWORD = "";
static String ORALE_DRIVERNAME = "oracle.jdbc.driver.OracleDriver";
static String ORALE_URL = "jdbc:oracle:thin:@10.211.55.6:1521:ORCL";
static String ORALE_USERNAME = "system";
static String ORALE_PASSWORD = "orcl";
@Test
public void getConnection1() {
// 输出DriverManager 的日志信息到控制台
DriverManager.setLogWriter(new PrintWriter(System.out));
// 获取mysql连接
try {
// 1、加载驱动
Class.forName(MYSQL_DRIVERNAME);
// 2、获取connection
Connection conn = DriverManager.getConnection(MYSQL_URL, MYSQL_USERNAME, MYSQL_PASSWORD);
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
// 获取oracle连接
try {
// 1、加载驱动
Class.forName(ORALE_DRIVERNAME);
// 2、获取connection
Connection conn = DriverManager.getConnection(ORALE_URL, ORALE_USERNAME, ORALE_PASSWORD);
System.out.println(conn);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
执行,控制台输出
DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/mysql")
trying com.mysql.jdbc.Driver
getConnection returning com.mysql.jdbc.Driver
com.mysql.jdbc.JDBC4Connection@22b3428e
DriverManager.getConnection("jdbc:oracle:thin:@10.211.55.6:1521:ORCL")
trying com.mysql.jdbc.Driver
trying com.mysql.fabric.jdbc.FabricMySQLDriver
trying oracle.jdbc.OracleDriver
getConnection returning oracle.jdbc.OracleDriver
oracle.jdbc.driver.T4CConnection@28d51032
上述代码DriverManager同时连接oracle和mysql,换句话说,DriverManager同时管理着oracle和mysql两个驱动。那我们会产生疑问DriverManager到底是如果多个管理驱动的,怎么样根据我们的连接配置信息(url,密码...)获取到对应的连接?
驱动的管理或者说记录是封装成DriverInfo后放到DriverInfoCopyOnWriteArrayList,可以看做是一个线程安全的ArrayList。
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<DriverInfo>();
当我们要获取连接的时候,再遍历registeredDrivers这个列表,然后使用列表中的驱动尝试连接,当获取到连接以后就停止遍历,然后返回connection
DriverManager的核心连接代码
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) throws SQLException {
// 省略。。。
println("DriverManager.getConnection(\"" + url + "\")");
SQLException reason = null;
// 遍历驱动注册列表
for(DriverInfo aDriver : registeredDrivers) {
// 判断是否能使用该驱动
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
println(" trying " + aDriver.driver.getClass().getName());
// 尝试连接
/**
* 其实java.sql.Driver接口定义了一个方法判断驱动能不能接受url的连接,
* boolean acceptsURL(String url) throws SQLException;
* 这里其实先使下面代码进行判断
* if (!aDriver.driver.acceptsURL(url)) {
* continue;
* }
* 但是没有使用上面代码进行判断,我猜可能的原因是:
* 1、java官方并不要求实现acceptsURL()方法,我们平常
* 使用也很早用到这个方法。
* 2、存在一种这样的情况:某个数据库厂商协议进行升级
* 了,但是为了兼容旧的协议还是允许连接,
* acceptsURL(旧协议的url)方法,返回false,
* 起到一个提示的作用。
*/
Connection con = aDriver.driver.connect(url, info);
if (con != null) {
// Success!
println("getConnection returning " + aDriver.driver.getClass().getName());
// 没有报异常并且 connection 不为空,则返回connection
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
} else {
println(" skipping: " + aDriver.getClass().getName());
}
}
// if we got here nobody could connect.
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");
}
}