资讯专栏INFORMATION COLUMN

java维护多个数据库连接

My_Oh_My / 1592人阅读

摘要:到此我们发现其实维护的只是驱动而已,我们要获取那种类型数据库的连接,以及获取那个数据库连接还是取决于我们自己,因为获取数据库连接的时候,连接信息是我们自己指定的。

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 CopyOnWriteArrayList registeredDrivers = 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=utf8
3.数据信息 3.1.test库的user表信息
id username password
1 u3 p3
3.2.demo库的user表信息
id username password
1 u1 p1
2 u2 p2
4.管理多数据源的示例代码

这个只是简单的使用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");
        Map dbConfMap = 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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76212.html

相关文章

  • 一篇文章带你彻底搞懂NIO

    摘要:阻塞当进行读写时,线程是阻塞的状态。当任何一个收到数据后,中断程序将唤起进程。接收数据当收到数据后,中断程序会给的就绪列表添加引用。当接收到数据,中断程序一方面修改,另一方面唤醒等待队列中的进程,进程再次进入运行状态如下图。 本篇文章目的在于基本概念和原理的解释,不会贴过多的使用代码。 什么是NIO Java NIO (New IO)是 Java 的另一个 IO API (来自 jav...

    ziwenxie 评论0 收藏0
  • Java IO之NIO

    摘要:上篇说了最基础的五种模型,相信大家对相关的概念应该有了一定的了解,这篇文章主要讲讲基于多路复用的。 上篇说了最基础的五种IO模型,相信大家对IO相关的概念应该有了一定的了解,这篇文章主要讲讲基于多路复用IO的Java NIO。 背景 Java诞生至今,有好多种IO模型,从最早的Java IO到后来的Java NIO以及最新的Java AIO,每种IO模型都有它自己的特点,详情请看我的上...

    pingink 评论0 收藏0
  • “分库分表" ?选型和流程要慎重,否则会失控

    摘要:但你是否知道分库分表需要哪些要素拆分过程是复杂的,提前计划,不要等真正开工,各种意外的工作接踵而至,以至失控。在实施分库分表策略时,这些个性会造成策略过大不好维护。 更多文章关注微信公众号《小姐姐味道》 https://mp.weixin.qq.com/s?__... 数据库中间件之分库分表 恭喜你,贵公司终于成长到一定规模,需要考虑高可用,甚至分库分表了。但你是否知道分库分表需要哪...

    archieyang 评论0 收藏0
  • 一个JAVA码农的Node之旅

    摘要:的重连机制会尝试重连至其他伺服器并重新建立起对应关系。使用进行中文分词曹操在操场操美女对分词后的名词和动词转换为简体中文并查询命中则替换。返回替换后的字符串得到曹操在操场美女打包部署本身是单线程的虽然本身提供模块但需要修改代码。 本篇是一个Node新手做完实际项目后的心得总结。Node高手完全可以略过本文。 摘要 如果BOSS要求你在短期内快速实现一套聊天云服务平台, 你的第一反应是什...

    rollback 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<