摘要:到此我們發(fā)現(xiàn)其實(shí)維護(hù)的只是驅(qū)動(dòng)而已,我們要獲取那種類型數(shù)據(jù)庫的連接,以及獲取那個(gè)數(shù)據(jù)庫連接還是取決于我們自己,因?yàn)楂@取數(shù)據(jù)庫連接的時(shí)候,連接信息是我們自己指定的。
1.DriverManager維護(hù)了一個(gè)驅(qū)動(dòng)列表
以我們熟悉的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() } }
在我們執(zhí)行如下語句的時(shí)候,static塊的內(nèi)容會(huì)被執(zhí)行,于是com.mysql.jdbc.Driver就成功的把自己給注冊到DriverManager的驅(qū)動(dòng)列表里面去了。
Class.forName("com.mysql.jdbc.Driver");
來看看DriverManager的注冊實(shí)現(xiàn):
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); }
代碼的意思就是如果當(dāng)前的Driver不存在就添加,否則就啥也不執(zhí)行。
于是在DriverManager這個(gè)類里面就有了我們Mysql的驅(qū)動(dòng)類了。
對于Oracle也是一樣的,被加載的驅(qū)動(dòng)都需要在被加載的時(shí)候,在static塊中,自動(dòng)把自己給注冊到DriverManager中。
于是我們明白DriverManager就是維護(hù)了一個(gè)數(shù)據(jù)庫的驅(qū)動(dòng)列表,而且這個(gè)列表中同類型的數(shù)據(jù)庫連接只有一份,比如我們系統(tǒng)里面即用到了mysql也用到了oracle那么我們的DriverManager里面只維護(hù)了2種類型的數(shù)據(jù)庫驅(qū)動(dòng),不論我們實(shí)際上用了多個(gè)mysql數(shù)據(jù)庫,驅(qū)動(dòng)都是一樣的。
2.獲取邏輯由具體驅(qū)動(dòng)自己實(shí)現(xiàn)看看DriverManager是如何獲取數(shù)據(jù)庫連接的:
第一步:構(gòu)造用戶信息
@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; // 線程同步,防止并發(fā)出問題 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; // 循環(huán)當(dāng)前的數(shù)據(jù)庫驅(qū)動(dòng)來獲取數(shù)據(jù)庫連接 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()); // 這個(gè)地方由具體的數(shù)據(jù)庫驅(qū)動(dòng)自己來實(shí)現(xiàn) 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"); }
對于上面的代碼,我們不需要全部關(guān)注,只需要知道,連接的獲取過程是通過循環(huán)已有的驅(qū)動(dòng),然后由每個(gè)驅(qū)動(dòng)自己來完成的。我們來看看mysql的驅(qū)動(dòng)實(shí)現(xiàn):
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); } // 首先判斷當(dāng)前的url是不是負(fù)載均衡的url,如果是走負(fù)載均衡的獲取邏輯 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; // 這個(gè)地方會(huì)判斷當(dāng)前url是不是屬于mysql連接的前綴,不是就return if ((props = parseURL(url, info)) == null) { return null; } if (!"1".equals(props.getProperty(NUM_HOSTS_PROPERTY_KEY))) { return connectFailover(url, info); } // 總之經(jīng)過了一系列的判斷我們的程序開始真正的去拿我們要的連接了 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方法實(shí)現(xiàn):
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; } // 判斷當(dāng)前的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; }
對于不同的數(shù)據(jù)庫,因?yàn)槭褂玫倪B接url不一樣,比如mysql的連接格式如下
jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
而oracle的連接字符串如下:
jdbc:oracle:thin:@127.0.0.1:1521:news
所以通過連接字符串的前綴不同可以區(qū)分出當(dāng)前的驅(qū)動(dòng)是不是目標(biāo)驅(qū)動(dòng),如果不是,DriverManager接著循環(huán)下一個(gè)驅(qū)動(dòng)來嘗試獲取連接。這樣就可以通過DriverManager通過url來獲取不同類型數(shù)據(jù)庫的連接了。到此我們發(fā)現(xiàn)其實(shí)DriverManager維護(hù)的只是驅(qū)動(dòng)而已,我們要獲取那種類型數(shù)據(jù)庫的連接,以及獲取那個(gè)數(shù)據(jù)庫連接還是取決于我們自己,因?yàn)楂@取數(shù)據(jù)庫連接的時(shí)候,連接信息是我們自己指定的。
3.如何維護(hù)多個(gè)數(shù)據(jù)庫連接從上面的分析我們知道了,我們獲取數(shù)據(jù)庫的連接就是提供連接的url,用戶名,密碼就可以獲取一個(gè)相應(yīng)數(shù)據(jù)庫的連接了,而如果要維護(hù)多個(gè)數(shù)據(jù)庫連接,不就是提供多套u(yù)rl,用戶名和密碼嗎?而如果你想手動(dòng)的來把這些連接管理起來也很簡單,其實(shí)就是如何管理多套數(shù)據(jù)庫連接信息而已。舉例如下:
1.數(shù)據(jù)庫信息有2個(gè)數(shù)據(jù)庫:jdbc:mysql://localhost:3306/test 和 jdbc:mysql://localhost:3306/demo
2.表結(jié)構(gòu)信息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.數(shù)據(jù)信息 3.1.test庫的user表信息
id | username | password |
---|---|---|
1 | u3 | p3 |
id | username | password |
---|---|---|
1 | u1 | p1 |
2 | u2 | p2 |
這個(gè)只是簡單的使用map來維護(hù)了我們多個(gè)數(shù)據(jù)源,你完全可以把它改造為自己想要的那種方式,比如主從結(jié)構(gòu)的數(shù)據(jù)庫…,當(dāng)然了我們這里這么做并不是非要自己維護(hù)這些數(shù)據(jù)源,只是讓你知道多數(shù)據(jù)源維護(hù)的原理,而真正多數(shù)據(jù)源我們是使用相應(yīng)的框架來實(shí)現(xiàn)的
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: 模擬多數(shù)據(jù)源管理 * @author: ztd * @date 2019/7/8 下午4:41 */ public class MultiConnTest { /** * 多數(shù)據(jù)源處理 * 1.insert使用一個(gè)數(shù)據(jù)源 * 2.query使用另一個(gè)數(shù)據(jù)源 * * @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:數(shù)據(jù)庫類型://主機(jī)IP:端口/數(shù)據(jù)庫名?characterEncoding=編碼 Class.forName("com.mysql.jdbc.Driver"); conn = DriverManager.getConnection(url, user, password); }catch(Exception e){ System.out.println("數(shù)據(jù)庫連接異常!"); 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; } } }
運(yùn)行結(jié)果:
======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
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/76212.html
摘要:阻塞當(dāng)進(jìn)行讀寫時(shí),線程是阻塞的狀態(tài)。當(dāng)任何一個(gè)收到數(shù)據(jù)后,中斷程序?qū)酒疬M(jìn)程。接收數(shù)據(jù)當(dāng)收到數(shù)據(jù)后,中斷程序會(huì)給的就緒列表添加引用。當(dāng)接收到數(shù)據(jù),中斷程序一方面修改,另一方面喚醒等待隊(duì)列中的進(jìn)程,進(jìn)程再次進(jìn)入運(yùn)行狀態(tài)如下圖。 本篇文章目的在于基本概念和原理的解釋,不會(huì)貼過多的使用代碼。 什么是NIO Java NIO (New IO)是 Java 的另一個(gè) IO API (來自 jav...
摘要:上篇說了最基礎(chǔ)的五種模型,相信大家對相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用的。 上篇說了最基礎(chǔ)的五種IO模型,相信大家對IO相關(guān)的概念應(yīng)該有了一定的了解,這篇文章主要講講基于多路復(fù)用IO的Java NIO。 背景 Java誕生至今,有好多種IO模型,從最早的Java IO到后來的Java NIO以及最新的Java AIO,每種IO模型都有它自己的特點(diǎn),詳情請看我的上...
摘要:但你是否知道分庫分表需要哪些要素拆分過程是復(fù)雜的,提前計(jì)劃,不要等真正開工,各種意外的工作接踵而至,以至失控。在實(shí)施分庫分表策略時(shí),這些個(gè)性會(huì)造成策略過大不好維護(hù)。 更多文章關(guān)注微信公眾號《小姐姐味道》 https://mp.weixin.qq.com/s?__... 數(shù)據(jù)庫中間件之分庫分表 恭喜你,貴公司終于成長到一定規(guī)模,需要考慮高可用,甚至分庫分表了。但你是否知道分庫分表需要哪...
摘要:的重連機(jī)制會(huì)嘗試重連至其他伺服器并重新建立起對應(yīng)關(guān)系。使用進(jìn)行中文分詞曹操在操場操美女對分詞后的名詞和動(dòng)詞轉(zhuǎn)換為簡體中文并查詢命中則替換。返回替換后的字符串得到曹操在操場美女打包部署本身是單線程的雖然本身提供模塊但需要修改代碼。 本篇是一個(gè)Node新手做完實(shí)際項(xiàng)目后的心得總結(jié)。Node高手完全可以略過本文。 摘要 如果BOSS要求你在短期內(nèi)快速實(shí)現(xiàn)一套聊天云服務(wù)平臺(tái), 你的第一反應(yīng)是什...
閱讀 854·2021-11-19 11:29
閱讀 3349·2021-09-26 10:15
閱讀 2854·2021-09-22 10:02
閱讀 2433·2021-09-02 15:15
閱讀 1970·2019-08-30 15:56
閱讀 2408·2019-08-30 15:54
閱讀 2903·2019-08-29 16:59
閱讀 635·2019-08-29 16:20