摘要:到此我們發現其實維護的只是驅動而已,我們要獲取那種類型數據庫的連接,以及獲取那個數據庫連接還是取決于我們自己,因為獲取數據庫連接的時候,連接信息是我們自己指定的。
1.DriverManager維護了一個驅動列表
以我們熟悉的MysqlDriver來舉例:
package com.mysql.jdbc; import java.sql.SQLException; public class Driver extends NonRegisteringDriver implements java.sql.Driver { // // Register ourselves with the DriverManager // static { try { java.sql.DriverManager.registerDriver(new Driver()); } catch (SQLException E) { throw new RuntimeException("Can"t register driver!"); } } public Driver() throws SQLException { // Required for Class.forName().newInstance() } }
在我們執行如下語句的時候,static塊的內容會被執行,于是com.mysql.jdbc.Driver就成功的把自己給注冊到DriverManager的驅動列表里面去了。
Class.forName("com.mysql.jdbc.Driver");
來看看DriverManager的注冊實現:
private final static CopyOnWriteArrayListregisteredDrivers = new CopyOnWriteArrayList<>(); 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); }
代碼的意思就是如果當前的Driver不存在就添加,否則就啥也不執行。
于是在DriverManager這個類里面就有了我們Mysql的驅動類了。
對于Oracle也是一樣的,被加載的驅動都需要在被加載的時候,在static塊中,自動把自己給注冊到DriverManager中。
于是我們明白DriverManager就是維護了一個數據庫的驅動列表,而且這個列表中同類型的數據庫連接只有一份,比如我們系統里面即用到了mysql也用到了oracle那么我們的DriverManager里面只維護了2種類型的數據庫驅動,不論我們實際上用了多個mysql數據庫,驅動都是一樣的。
2.獲取邏輯由具體驅動自己實現看看DriverManager是如何獲取數據庫連接的:
第一步:構造用戶信息
@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())); }
第二步:獲取連接
// Worker method called by the public getConnection() methods. private static Connection getConnection( String url, java.util.Properties info, Class> caller) throws SQLException { ClassLoader callerCL = caller != null ? caller.getClassLoader() : null; // 線程同步,防止并發出問題 synchronized(DriverManager.class) { // synchronize loading of the correct classloader. 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 the caller does not have permission to load the driver then // skip it. 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 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"); }
對于上面的代碼,我們不需要全部關注,只需要知道,連接的獲取過程是通過循環已有的驅動,然后由每個驅動自己來完成的。我們來看看mysql的驅動實現:
public java.sql.Connection connect(String url, Properties info) throws SQLException { if (url == null) { throw SQLError.createSQLException(Messages.getString("NonRegisteringDriver.1"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); } // 首先判斷當前的url是不是負載均衡的url,如果是走負載均衡的獲取邏輯 if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) { return connectLoadBalanced(url, info); } else if (StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { return connectReplicationConnection(url, info); } Properties props = null; // 這個地方會判斷當前url是不是屬于mysql連接的前綴,不是就return if ((props = parseURL(url, info)) == null) { return null; } if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) { return connectFailover(url, info); } // 總之經過了一系列的判斷我們的程序開始真正的去拿我們要的連接了 try { Connection newConn = com.mysql.jdbc.ConnectionImpl.getInstance(host(props), port(props), props, database(props), url); return newConn; } catch (SQLException sqlEx) { // Don"t wrap SQLExceptions, throw // them un-changed. throw sqlEx; } catch (Exception ex) { SQLException sqlEx = SQLError.createSQLException( Messages.getString("NonRegisteringDriver.17") + ex.toString() + Messages.getString("NonRegisteringDriver.18"), SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE, null); sqlEx.initCause(ex); throw sqlEx; } }
我們看看parseURL方法實現:
private static final String URL_PREFIX = "jdbc:mysql://"; @SuppressWarnings("deprecation") public Properties parseURL(String url, Properties defaults) throws java.sql.SQLException { Properties urlProps = (defaults != null) ? new Properties(defaults) : new Properties(); if (url == null) { return null; } // 判斷當前的url是不是以"jdbc:mysql://";開始 if (!StringUtils.startsWithIgnoreCase(url, URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX) && !StringUtils.startsWithIgnoreCase(url, REPLICATION_URL_PREFIX)) { return null; } ...還有一大堆邏輯 return urlProps; }
對于不同的數據庫,因為使用的連接url不一樣,比如mysql的連接格式如下
jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
而oracle的連接字符串如下:
jdbc:oracle:thin:@127.0.0.1:1521:news
所以通過連接字符串的前綴不同可以區分出當前的驅動是不是目標驅動,如果不是,DriverManager接著循環下一個驅動來嘗試獲取連接。這樣就可以通過DriverManager通過url來獲取不同類型數據庫的連接了。到此我們發現其實DriverManager維護的只是驅動而已,我們要獲取那種類型數據庫的連接,以及獲取那個數據庫連接還是取決于我們自己,因為獲取數據庫連接的時候,連接信息是我們自己指定的。
3.如何維護多個數據庫連接從上面的分析我們知道了,我們獲取數據庫的連接就是提供連接的url,用戶名,密碼就可以獲取一個相應數據庫的連接了,而如果要維護多個數據庫連接,不就是提供多套url,用戶名和密碼嗎?而如果你想手動的來把這些連接管理起來也很簡單,其實就是如何管理多套數據庫連接信息而已。舉例如下:
1.數據庫信息有2個數據庫:jdbc:mysql://localhost:3306/test 和 jdbc:mysql://localhost:3306/demo
2.表結構信息CREATE TABLE `user` ( `id` int(20) NOT NULL AUTO_INCREMENT, `username` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, `password` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf83.數據信息 3.1.test庫的user表信息
id | username | password |
---|---|---|
1 | u3 | p3 |
id | username | password |
---|---|---|
1 | u1 | p1 |
2 | u2 | p2 |
這個只是簡單的使用map來維護了我們多個數據源,你完全可以把它改造為自己想要的那種方式,比如主從結構的數據庫…,當然了我們這里這么做并不是非要自己維護這些數據源,只是讓你知道多數據源維護的原理,而真正多數據源我們是使用相應的框架來實現的
package com.bsx.test; import lombok.Data; import org.junit.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; import java.util.HashMap; import java.util.Map; /** * @Description: 模擬多數據源管理 * @author: ztd * @date 2019/7/8 下午4:41 */ public class MultiConnTest { /** * 多數據源處理 * 1.insert使用一個數據源 * 2.query使用另一個數據源 * * @throws Exception */ @Test public void testMultiDB() throws Exception { DBConf test = new DBConf("root", "12345678", "jdbc:mysql://localhost:3306/test?characterEncoding=utf-8"); DBConf demo = new DBConf("root", "12345678", "jdbc:mysql://localhost:3306/demo?characterEncoding=utf-8"); MapdbConfMap = new HashMap<>(); dbConfMap.put("test", test); dbConfMap.put("demo", demo); Connection connection = getConn(dbConfMap.get("test")); System.out.println("======print test user info======"); printUserInfo(connection); connection = getConn(dbConfMap.get("demo")); System.out.println("======print demo user info======"); printUserInfo(connection); } public static void printUserInfo(Connection connection) throws Exception { Statement statement = connection.createStatement(); ResultSet resultSet = statement.executeQuery("SELECT * FROM user"); while (resultSet.next()) { System.out.println("id:" +resultSet.getInt(1) + " name: " + resultSet.getString(2) + " password: " + resultSet.getString(3)); } resultSet.close(); statement.close(); connection.close(); } public static Connection getConn(DBConf dbConf) { return initMysql(dbConf.getUrl(), dbConf.getUser(), dbConf.getPassword()); } /** * @description 連接mysql * @author ztd * @date 2019/7/8 下午5:06 */ public static Connection initMysql(String url, String user, String password) { Connection conn = null; try{ //jdbc:數據庫類型://主機IP:端口/數據庫名?characterEncoding=編碼 Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, user, password); }catch(Exception e){ System.out.println("數據庫連接異常!"); e.printStackTrace(); } return conn; } @Data class DBConf { private String user; private String password; private String url; public DBConf(String user, String password, String url) { this.user = user; this.password = password; this.url = url; } } }
運行結果:
======print test user info====== id:1 name: u3 password: p3 ======print demo user info====== id:1 name: u1 password: p1 id:2 name: u2 password: p2
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/76212.html
摘要:阻塞當進行讀寫時,線程是阻塞的狀態。當任何一個收到數據后,中斷程序將喚起進程。接收數據當收到數據后,中斷程序會給的就緒列表添加引用。當接收到數據,中斷程序一方面修改,另一方面喚醒等待隊列中的進程,進程再次進入運行狀態如下圖。 本篇文章目的在于基本概念和原理的解釋,不會貼過多的使用代碼。 什么是NIO Java NIO (New IO)是 Java 的另一個 IO API (來自 jav...
摘要:上篇說了最基礎的五種模型,相信大家對相關的概念應該有了一定的了解,這篇文章主要講講基于多路復用的。 上篇說了最基礎的五種IO模型,相信大家對IO相關的概念應該有了一定的了解,這篇文章主要講講基于多路復用IO的Java NIO。 背景 Java誕生至今,有好多種IO模型,從最早的Java IO到后來的Java NIO以及最新的Java AIO,每種IO模型都有它自己的特點,詳情請看我的上...
摘要:但你是否知道分庫分表需要哪些要素拆分過程是復雜的,提前計劃,不要等真正開工,各種意外的工作接踵而至,以至失控。在實施分庫分表策略時,這些個性會造成策略過大不好維護。 更多文章關注微信公眾號《小姐姐味道》 https://mp.weixin.qq.com/s?__... 數據庫中間件之分庫分表 恭喜你,貴公司終于成長到一定規模,需要考慮高可用,甚至分庫分表了。但你是否知道分庫分表需要哪...
摘要:的重連機制會嘗試重連至其他伺服器并重新建立起對應關系。使用進行中文分詞曹操在操場操美女對分詞后的名詞和動詞轉換為簡體中文并查詢命中則替換。返回替換后的字符串得到曹操在操場美女打包部署本身是單線程的雖然本身提供模塊但需要修改代碼。 本篇是一個Node新手做完實際項目后的心得總結。Node高手完全可以略過本文。 摘要 如果BOSS要求你在短期內快速實現一套聊天云服務平臺, 你的第一反應是什...
閱讀 864·2021-11-19 11:29
閱讀 3356·2021-09-26 10:15
閱讀 2866·2021-09-22 10:02
閱讀 2442·2021-09-02 15:15
閱讀 1979·2019-08-30 15:56
閱讀 2415·2019-08-30 15:54
閱讀 2914·2019-08-29 16:59
閱讀 641·2019-08-29 16:20