今天看了jdbc的源碼,感覺特別有意思,下面是jdbc的傳統步驟:
Connection connection=null; PreparedStatement preparedStatement=null; ResultSet resultSet=null; try { Class.forName("com.mysql.cj.jdbc.Driver"); connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/myshool?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong","root","root"); String sql="select * from student where name= ? "; preparedStatement=connection.prepareStatement(sql); preparedStatement.setString(1,"小明"); resultSet=preparedStatement.executeQuery(); while (resultSet.next()){ System.out.println(resultSet.getString("id")+" " +resultSet.getString("name")); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); }
這里我用的是最新的mysql驅動包
第一點:加載驅動。
Class.forName("com.mysql.cj.jdbc.Driver");
在最開始用的時候很不明白,首先為什么要加載一個驅動類,之后就可以取得connection,也非常好奇DriverManager是怎么來拿到驅動類的信息,另外還有為什么要用Class.forName加載。
后來點進Driver里,看到:

所以也就是說,在Class.forName加載完驅動類后,開始執行靜態代碼塊時,會new一個Driver,並調用DriverManager的registerDriver把Driver給注冊到自己的驅動程序管理器(DriverManager)中去。
在這里說一下ClassLoader.loadClass和Class.forName的區別:
- Classloder.loaderClass(String name)
其實該方法內部調用的是:Classloder. loadClass(name, false)
方法:Classloder. loadClass(String name, boolean resolve)
1:參數name代表類的全限定類名
2:參數resolve代表是否解析,resolve為true是解析該類
- Class.forName(String name)
其實該方法內部調用的是:Class.forName(className, true, ClassLoader.getClassLoader(caller))
方法:Class.forName0(String name, boolean initialize, ClassLoader loader)
參數name代表全限定類名
參數initialize表示是否初始化該類,為true是初始化該類
參數loader 對應的類加載器
- 兩者最大的區別
Class.forName得到的class是已經初始化完成的
Classloder.loaderClass得到的class是沒有初始化的
這里Driver類也繼承NonRegisteringDriver並實現java.sql.Driver接口
我們點進NonRegisteringDriver類會發現,它也是實現java.sql.Driver接口:

最主要看一下Driver接口:
public interface Driver { //獲取Connection 方法。數據庫的url,及info至少得包含user,password key Connection connect(String url, java.util.Properties info) throws SQLException; //判斷是否是一個正確的url字符串。 boolean acceptsURL(String url) throws SQLException; //得到驅動的屬性(user,password,port等)。 DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info) throws SQLException; //得到主要版本 int getMajorVersion(); //得到次要版本 int getMinorVersion(); //判斷是否是一個正確的driver boolean jdbcCompliant(); //------------------------- JDBC 4.1 ----------------------------------- //返回父日志 public Logger getParentLogger() throws SQLFeatureNotSupportedException; }
其實Driver接口是每個數據庫驅動都必須繼承的接口。
言歸正傳,我們可以看到在Driver類中的靜態代碼塊有DriverManager.registerDriver(這是把Driver給注冊到自己的驅動程序管理器(DriverManager)中)的方法,我們點進去可以看到DriverManager類,此類沒有繼承和實現任何接口,它是管理一組 JDBC 驅動程序的基本服務。
這里顯示的是一些重要的方法
public class DriverManager { //已經注冊的驅動列表 private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>(); private static volatile int loginTimeout = 0; private static volatile java.io.PrintWriter logWriter = null; private static volatile java.io.PrintStream logStream = null; // Used in println() to synchronize logWriter private final static Object logSync = new Object(); //阻止被初始化,DriverManager里面都是靜態的方法。 private DriverManager(){} //初始化加載驅動,其中用到了ServiceLoader機制 static { //初始化加載驅動 loadInitialDrivers(); //打印日志 println("JDBC DriverManager initialized"); } /*因為源代碼過長,這里我們只講重要的一些方法*/ //初始化加載驅動 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; } //這里涉及到一個ServiceLoader概念。上面我推薦了一篇文章,想了解的可以去看看。通過ServiceLoader去加載所有的driver。 AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { //通過ServiceLoader.load()方法加載所有驅動 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); } } } //3個獲取connection方法,對外提供的方法。 @CallerSensitive public static Connection getConnection(String url, java.util.Properties info) throws SQLException { return (getConnection(url, info, Reflection.getCallerClass())); } //這個方法中可以看到,properties中至少配置參數其實就是user和password @CallerSensitive public static Connection getConnection(String url, String user, String password) throws SQLException { java.util.Properties info = new java.util.Properties(); if (user != null) { info.put("user", user); } if (password != null) { info.put("password", password); } return (getConnection(url, info, Reflection.getCallerClass())); } @CallerSensitive public static Connection getConnection(String url) throws SQLException { java.util.Properties info = new java.util.Properties(); return (getConnection(url, info, Reflection.getCallerClass())); } // 內部真正工作的方法(私有) private static Connection getConnection( String url, java.util.Properties info, Class<?> caller) throws SQLException { //檢查callerCL是否為空,如果為空則通過Thread.currentThread().getContextClassLoader()去加載 ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; synchronized(DriverManager.class) { // synchronize loading of the correct classloader. if (callerCL == null) { callerCL = Thread.currentThread().getContextClassLoader(); } } //這里判斷url if(url == null) { throw new SQLException("The url cannot be null", "08001"); } println("DriverManager.getConnection(\"" + url + "\")"); SQLException reason = null; //遍歷registeredDrivers去獲得正確的connection for(DriverInfo aDriver : registeredDrivers) { // 如果callerCL不允許讀取驅動,就會跳過。 if(isDriverAllowed(aDriver.driver, callerCL)) { try { println(" trying " + aDriver.driver.getClass().getName()); //真正的獲取connection的方法,其實還是通過driver接口中的connect方法。 Connection con = aDriver.driver.connect(url, info); if (con != null) { 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"); } //通過url獲取driver。 @CallerSensitive public static Driver getDriver(String url) throws SQLException { println("DriverManager.getDriver(\"" + url + "\")"); Class<?> callerClass = Reflection.getCallerClass(); // 通過遍歷registeredDrivers中每個驅動 for (DriverInfo aDriver : registeredDrivers) { // acceptsURL()方法判斷url是否符合driver if(isDriverAllowed(aDriver.driver, callerClass)) { try { if(aDriver.driver.acceptsURL(url)) { println("getDriver returning " + aDriver.driver.getClass().getName()); return (aDriver.driver); } } catch(SQLException sqe) { } } else { println(" skipping: " + aDriver.driver.getClass().getName()); } } println("getDriver: no suitable driver"); throw new SQLException("No suitable driver", "08001"); } //注冊驅動的方法 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 { //判斷driver是否已經被加載到registeredDrivers,沒有就加進去 if(driver != null) { registeredDrivers.addIfAbsent(new DriverInfo(driver, da)); } else { throw new NullPointerException(); } println("registerDriver: " + driver); } //注銷driver方法。 @CallerSensitive public static synchronized void deregisterDriver(Driver driver) throws SQLException { if (driver == null) { return; } SecurityManager sec = System.getSecurityManager(); if (sec != null) { sec.checkPermission(DEREGISTER_DRIVER_PERMISSION); } println("DriverManager.deregisterDriver: " + driver); DriverInfo aDriver = new DriverInfo(driver, null); if(registeredDrivers.contains(aDriver)) { if (isDriverAllowed(driver, Reflection.getCallerClass())) { DriverInfo di = registeredDrivers.get(registeredDrivers.indexOf(aDriver)); if(di.action() != null) { di.action().deregister(); } //通過remove()注銷。 registeredDrivers.remove(aDriver); } else { throw new SecurityException(); } } else { println(" couldn't find driver to unload"); } } }
getConnection這個方法中的properties類注意一下,它是以后能用到的以.properties為后綴名的屬性文件
第二點:獲取數據庫鏈接Connection
代表與數據庫的鏈接,並擁有創建SQL語句的方法,以完成基本的SQL操作,同時為數據庫事務提供提交和回滾方法
這里說一下Connection接口的重要方法:
public interface Connection extends Wrapper, AutoCloseable { //創建statement Statement createStatement() throws SQLException; //創建prepareStatement PreparedStatement prepareStatement(String sql) throws SQLException; //創建CallableStatement CallableStatement prepareCall(String sql) throws SQLException; //轉換sql為本機執行sql String nativeSQL(String sql) throws SQLException; //設置是否自動提交 狀態 void setAutoCommit(boolean autoCommit) throws SQLException; //判斷是否是自動提交 boolean getAutoCommit() throws SQLException; //提交 void commit() throws SQLException; //回滾 void rollback() throws SQLException; //關閉連接 void close() throws SQLException; //判斷是否關閉 boolean isClosed() throws SQLException; ...//都是一些規范的接口,太多就不一一列舉了。 }
第三點:獲取statement、preparedstatement
statement和preparedStatement的作用是一樣的,都是用來執行sql。區別:
1.代碼的可讀性和可維護性.
雖然用PreparedStatement來代替Statement會使代碼多出幾行,但這樣的代碼無論從可讀性還是可維護性上來說.都比直接用Statement的代碼高很多檔次:
stmt.executeUpdate(“insert into tb_name (col1,col2,col2,col4) values (‘”+var1+”’,’”+var2+”’,”+var3+”,’”+var4+”’)”);//stmt是Statement對象實例
perstmt = con.prepareStatement(“insert into tb_name (col1,col2,col2,col4) values (?,?,?,?)”);
perstmt.setString(1,var1);
perstmt.setString(2,var2);
perstmt.setString(3,var3);
perstmt.setString(4,var4);
perstmt.executeUpdate(); //prestmt是 PreparedStatement 對象實例
2.PreparedStatement盡最大可能提高性能.
3.最重要的一點是極大地提高了安全性.
由此可見:開發的時候盡量用preparedStatement,少用statement。
preparedStatement中一些重要的方法:
public interface PreparedStatement extends Statement { //用於產生單個結果集的語句,例如 SELECT 語句 ResultSet executeQuery() throws SQLException; //用於執行 INSERT、UPDATE 或 DELETE 語句以及 SQL DDL(數據定義語言)語句 int executeUpdate() throws SQLException; //設置空值,必須穿入type,不然可能報空指針異常 void setNull(int parameterIndex, int sqlType) throws SQLException; ...(同理有很多set的方法) //清空屬性 void clearParameters() throws SQLException; //用於執行返回多個結果集、多個更新計數或二者組合的語句 boolean execute() throws SQLException; ...//都是一些規范的接口,太多就不一一列舉了。 }
還有一個接口CallableStatement 提供了一種以標准形式調用已儲存過程的方法。
第四點:ResultSet
結果集(ResultSet)是數據中查詢結果返回的一種對象,可以說結果集是一個存儲查詢結果的對象,但是結果集並不僅僅具有存儲的功能,他同時還具有操縱數據的功能,可能完成對數據的更新等
ResultSet中一些重要的方法:
public interface ResultSet extends Wrapper, AutoCloseable { //是否有下一個值 boolean next() throws SQLException; //關閉 void close() throws SQLException; //是否為空 boolean wasNull() throws SQLException; //得到第幾列的String類型數據 String getString(int columnIndex) throws SQLException; boolean getBoolean(int columnIndex) throws SQLException; ...(太多get方法不一一列舉) //得到列名為columnLabel的值 String getString(String columnLabel) throws SQLException; //更新第幾列為空(各種update方法) void updateNull(int columnIndex) throws SQLException; //插入(updateRow、deleteRow、refreshRow 等) void insertRow() throws SQLException; }
以上就是jdbc中所注意的類,接口源碼。
