mysql版本:Ver 14.14 Distrib 5.7.16, for Win64 (x86_64)
驱动版本号:5.1.40
所有的JDBC应用程序都具有下面的基本流程:
1、加载数据库驱动。(JDBC4.0版本后依赖service provider默认执行,也就是不再需要使用Class.forName加载驱动)
2、建立到数据库的连接。
3、执行SQL语句。
4、处理结果。
5、从数据库断开连接并释放资源。
接下来,我们依据上述流程作出解释。
1、加载数据库驱动。
try { Class.forName("com.mysql.jdbc.Driver"); } catch (ClassNotFoundException e) { e.printStackTrace(); }
public class Driver extends NonRegisteringDriver implements java.sql.Driver { static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can't register driver!"); } } public Driver() throws SQLException { } }
知道了上述作用,我们当然也可以这样使用Driver driver = new com.mysql.jdbc.Driver();来加载Mysql驱动程序。
当然没有必要,我们没有使用driver。
那么,我们不写可以吗?
测试:
import java.sql.*;
/**
* Created by N3verL4nd on 2017/4/25.
*/
public class jdbc {
public static void main(String[] args) {
String driverClass = "com.mysql.jdbc.Driver";
String jdbcUrl = "jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true";
String user = "root";
String password = "lgh123";
String sql = "SELECT * FROM persons";
Connection conn = null;
/*
try {
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
*/
try {
conn = DriverManager.getConnection(jdbcUrl, user, password);
System.out.println(conn);
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
貌似不可以?由上报错,是因为没有找到所依赖的mysql驱动程序。我们可以这样设置:
D:\N3verL4nd\Desktop>set classpath=D:\Java\test\mysql-connector-java-5.1.40-bin.jar;
D:\N3verL4nd\Desktop>java jdbc
com.mysql.jdbc.JDBC4Connection@6433a2
再来做一个有意思的测试:
将mysql-connector-java-5.1.40-bin.jar的com目录(里面包含驱动的核心文件)解压到当前目录下。
我们把加载驱动的代码注释取消,再执行
jar包只是起到了压缩的作用,内部使用过程中也一定会涉及解压缩操作来调用驱动内部代码。
命令行下使用更能体会程序的运行原理。
接下来分析下DriverManager,它是JDBC里的一个管理驱动的工具类。
private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>();保存注册过的驱动列表
static { loadInitialDrivers(); println("JDBC DriverManager initialized"); }通过系统属性jdbc.drivers加载JDBC驱动程序。
private static void loadInitialDrivers() { String drivers; try { drivers = AccessController.doPrivileged(new PrivilegedAction<String>() { public String run() { return System.getProperty("jdbc.drivers"); } }); } catch (Exception ex) { drivers = null; } AccessController.doPrivileged(new PrivilegedAction<Void>() {//找到所有的拥有权限的java.sql.Driver的实现 public Void run() { ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);//从系统服务中加载驱动 Iterator<Driver> driversIterator = loadedDrivers.iterator(); try{ while(driversIterator.hasNext()) {//遍历驱动 driversIterator.next(); } } catch(Throwable t) { } return null; } }); println("DriverManager.initialize: jdbc.drivers = " + drivers); if (drivers == null || drivers.equals("")) { return; } 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); } } }接着判断驱动对象集是否为null,如果为null则返回,否则进入for循环,这个循环会依次遍历多个数据库驱动,因为jdbc:drivers会有多个数据库驱动,驱动名是以:分割,接下来就是通过Class.forName依次装载驱动类,在其中使用了ClassLoader.getSystemClassLoader()系统类装载器。
Driver类的静态代码块关键的一句代码:
java.sql.DriverManager.registerDriver(new Driver());
public static synchronized void registerDriver(java.sql.Driver driver) throws SQLException { registerDriver(driver, null); }
public static synchronized void registerDriver(java.sql.Driver driver, DriverAction da) throws SQLException { /* Register the driver if it has not already been added to our list */ if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { // This is for compatibility with the original DriverManager throw new NullPointerException(); } println("registerDriver: " + driver); }DriverInfo驱动信息类,是一个内部类,实际上就是对Driver的封装。然后再添加到registeredDrivers集合中。
2、建立到数据库的连接。
DriverManager.getConnection()方法来获取连接;我们知道这个是获取一个Connection,那么我们首先来看看DriverManager的getConnection到底做了什么?
public static Connection getConnection(String url)//所有参数全部通过URL传递过去
public static Connection getConnection(String url,String user, String password)//用户名和密码单独传递
public static Connection getConnection(String url,java.util.Properties info)//传递多个参数的K-V结构以上三个建立数据库连接的操作都会调用下边这一个:
private static Connection getConnection(String url, java.util.Properties info, Class<?> caller)
private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) {//如果没传递CLassLoader用当前线程的 if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } if(url == null) { throw new SQLException("The url cannot be null", "08001");//没有传递URL,则抛出异常 }//注意这个日志,默认是打印不出来的,程序内部会去判定logWriter是否为空,若为空则不会输出,默认为空 println("DriverManager.getConnection(\"" + url + "\")"); SQLException reason = null; for(DriverInfo aDriver : registeredDrivers) {//循环扫描所有的Drivers if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); Connection con = aDriver.driver.connect(url, info);//调用对应Driver的connect方法,得到Connection对象 if (con != null) { // Success! println("getConnection returning " + aDriver.driver.getClass().getName()); return (con);//如果得到了,则返回Connection } } 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");//没有获取到连接会抛出一个SQLException }
synchronized(DriverManager.class) { if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } }
中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类
Connection con = aDriver.driver.connect(url, info);
找到符合的驱动器就可以建立连接了。
3、执行SQL语句。
stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT * FROM persons");
4、处理结果。
while (rs.next()) { System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3)); }
5、从数据库断开连接并释放资源。
finally { if (rs != null) { try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } }
package com.xiya.test;
import java.io.PrintWriter;
import java.sql.*;
public class Test {
public static void main(String[] args) {
try {
Class.forName("com.mysql.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
//遍历加载的驱动程序
/*System.out.println("-----------------------------------------------");
Enumeration<Driver> enumeration = DriverManager.getDrivers();
while (enumeration.hasMoreElements()) {
System.out.println(enumeration.nextElement());
}
System.out.println("-----------------------------------------------");*/
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
//设置将数据输出到控制台
DriverManager.setLogWriter(new PrintWriter(System.out));
conn = DriverManager.getConnection("jdbc:mysql:///test?useUnicode=true&characterEncoding=UTF-8&useSSL=true", "root", "lgh123");
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM persons");
while (rs.next()) {
System.out.println(rs.getInt(1) + " " + rs.getString(2) + " " + rs.getInt(3));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
参考:
http://blog.csdn.net/luanlouis/article/details/29850811
http://blog.csdn.net/brilliancezhou/article/details/5425655
http://blog.csdn.net/xieyuooo/article/details/8502585