zoukankan      html  css  js  c++  java
  • mysql数据库连接池使用(二)实现自己的数据库连接池

    上一个章节,我们讲了xml文件的解析框架XMLConfiguration的使用,不懂的能够參考

    Apache Commons Configuration读取xml配置详细使用。

    这个章节主要实现自己的数据库连接池,封装自己的BasicDataSource类。

    实现自己业务的数据池。以下開始我们的项目构建。

    1.1.1. maven依赖。

    <dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-dbcp2</artifactId>
    <version>2.1.1</version>
    <exclusions>
    <exclusion>
    <artifactId>commons-logging</artifactId>
    <groupId>commons-logging</groupId>
    </exclusion>
    </exclusions>
    </dependency>
     <dependency>
            <groupId>commons-configuration</groupId>
            <artifactId>commons-configuration</artifactId>
            <version>1.8</version>
        </dependency>
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.8.0</version>
        </dependency>
        <dependency>
            <groupId>commons-jxpath</groupId>
            <artifactId>commons-jxpath</artifactId>
            <version>1.3</version>
    </dependency>


    1.1.2. 配置文件

    数据库採用读写分离,所以定义了2个数据源配置。配置文件da2s.xml存放在src根文件夹下详细的配置例如以下:

    <?xml version="1.0" encoding="UTF-8"?

    > <da2s-configuration> <DefaultConnectionPool>3000</DefaultConnectionPool> <connectionPool name="3000"> <dbtype>MYSQL</dbtype> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <url>jdbc:mysql://localhost:3306/springok</url> <username>root</username> <password></password> <datasourceProperty> <defaultAutoCommit>false</defaultAutoCommit> <initialSize>10</initialSize> <maxActive>10</maxActive> <maxIdle>5</maxIdle> <minIdle>5</minIdle> <maxWait>3000</maxWait> <validationQuery>select 1</validationQuery> <testOnBorrow>true</testOnBorrow> <removeAbandoned>true</removeAbandoned> <removeAbandonedTimeout>180</removeAbandonedTimeout> <logAbandoned>true</logAbandoned> </datasourceProperty> </connectionPool> <connectionPool name="5000"> <dbtype>MYSQL</dbtype> <driverClassName>com.mysql.jdbc.Driver</driverClassName> <url>jdbc:mysql://localhost:3306/springok</url> <username>root</username> <password></password> <datasourceProperty> <defaultAutoCommit>false</defaultAutoCommit> <initialSize>10</initialSize> <maxActive>10</maxActive> <maxIdle>5</maxIdle> <minIdle>5</minIdle> <maxWait>3000</maxWait> <validationQuery>select 1</validationQuery> <testOnBorrow>true</testOnBorrow> <removeAbandoned>true</removeAbandoned> <removeAbandonedTimeout>180</removeAbandonedTimeout> <logAbandoned>true</logAbandoned> </datasourceProperty> </connectionPool> </da2s-configuration>



    1.1.3. DataSourceManager的实现(核心部分)

    package cn.xhgg.test;
     
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLClientInfoException;
    import java.sql.SQLException;
    import java.util.Enumeration;
    import java.util.List;
    import java.util.Set;
    import java.util.concurrent.ConcurrentHashMap;
     
    import javax.sql.DataSource;
     
    import org.apache.commons.configuration.XMLConfiguration;
    import org.apache.commons.dbcp2.BasicDataSource;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
     
    import com.google.common.base.Preconditions;
     
    public class DataSourceManager {
    private final static Logger log = LoggerFactory.getLogger(DataSourceManager.class);
    static private ConcurrentHashMap<String, DataSource> pools = new ConcurrentHashMap<>();
    static private ConcurrentHashMap<String, String> dbTypes = new ConcurrentHashMap<>();
    static private String catalina_base = System.getProperty("catalina.base");
    static private String log_file_name = "dbcp2_exception.log";
     
    static private String DefaultConnectionPoolName = null;
    /**
     * 建构函数私有以防止其他对象创建本类实例
     */
    private DataSourceManager() {
    initDataSource();
    }
     
    /**
     * 返回唯一实例.假设是第一次调用此方法,则创建实例
     *
     * @return DBConnectionManager 唯一实例
     */
    static public DataSourceManager getInstance() {
    return DataSourceManager2Holder.instance;
    }
     
    /** 该类的一个对象。整个系统公用这一个对象。

    */ private static class DataSourceManager2Holder { private static DataSourceManager instance = new DataSourceManager(); } /** * 依据指定属性创建连接池实例. * * @param props * 连接池属性 */ private void initDataSource() { XMLConfiguration config; try { config = new XMLConfiguration("da2s.xml"); config.setThrowExceptionOnMissing(false); } catch (org.apache.commons.configuration.ConfigurationException exc) { log.error("GlobalConfigurationException", exc); throw new RuntimeException(exc); } DefaultConnectionPoolName = config.getString("DefaultConnectionPool"); // 该项未配置。则值为null log.debug("DefaultConnectionPoolName is " + DefaultConnectionPoolName + "..."); List<?

    > poolList = config.getList("connectionPool.dbtype"); String connPoolName = new String(); String dbtype = new String(); String driverClassName = new String(); String url = new String(); String username = new String(); String password = new String(); boolean defaultAutoCommit = false; boolean defaultReadOnly = false; int initialSize = 0; int maxActive = 0; int maxIdle = 0; int minIdle = 0; long maxWait = 0; String validationQuery = new String(); boolean testOnBorrow = true; boolean removeAbandoned = true; int removeAbandonedTimeout = 0; boolean logAbandoned = true; long maxConnLifetimeMillis = 0; try { for (int i = 0, j = poolList.size(); i < j; i++) { connPoolName = config.getString("connectionPool(" + i + ")[@name]"); dbtype = config.getString("connectionPool(" + i + ").dbtype"); driverClassName = config.getString("connectionPool(" + i + ").driverClassName"); url = config.getString("connectionPool(" + i + ").url"); username = config.getString("connectionPool(" + i + ").username"); password = config.getString("connectionPool(" + i + ").password"); defaultAutoCommit = config.getBoolean("connectionPool(" + i + ").datasourceProperty.defaultAutoCommit", false); defaultReadOnly = config.getBoolean("connectionPool(" + i + ").datasourceProperty.defaultReadOnly", false); initialSize = config.getInt("connectionPool(" + i + ").datasourceProperty.initialSize", 3); maxActive = config.getInt("connectionPool(" + i + ").datasourceProperty.maxActive", 50); maxIdle = config.getInt("connectionPool(" + i + ").datasourceProperty.maxIdle", 20); minIdle = config.getInt("connectionPool(" + i + ").datasourceProperty.minIdle", 5); maxWait = config.getLong("connectionPool(" + i + ").datasourceProperty.maxWait", 3000); maxConnLifetimeMillis = config.getLong("connectionPool(" + i + ").datasourceProperty.maxLifetime", 600000);// 10分钟 validationQuery = config.getString("connectionPool(" + i + ").datasourceProperty.validationQuery", "select 1"); testOnBorrow = config.getBoolean("connectionPool(" + i + ").datasourceProperty.testOnBorrow", true); removeAbandoned = config.getBoolean("connectionPool(" + i + ").datasourceProperty.removeAbandoned", true); removeAbandonedTimeout = config.getInt("connectionPool(" + i + ").datasourceProperty.removeAbandonedTimeout", 180); logAbandoned = config.getBoolean("connectionPool(" + i + ").datasourceProperty.logAbandoned", true); BasicDataSource bds2 = new BasicDataSource(); bds2.setDriverClassName(driverClassName); bds2.setUrl(url); bds2.setUsername(username); bds2.setPassword(password); bds2.setDefaultAutoCommit(defaultAutoCommit); bds2.setDefaultReadOnly(defaultReadOnly); bds2.setDefaultTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); // 初始化连接数 bds2.setInitialSize(initialSize); // 最小空暇连接 bds2.setMinIdle(minIdle); // 最大空暇连接 bds2.setMaxIdle(maxIdle); // 超时回收时间(以毫秒为单位) bds2.setMaxWaitMillis(maxWait); // 最大连接数 bds2.setMaxTotal(maxActive); bds2.setTestOnBorrow(testOnBorrow); bds2.setValidationQuery(validationQuery); // 一个连接的最大存活毫秒数。假设超过这个时间,则连接在下次激活、钝化、校验时都将会失败。

    假设设置为0或小于0的值,则连接的存活时间是无限的。 bds2.setMaxConnLifetimeMillis(maxConnLifetimeMillis); // 空暇对象驱赶线程执行时的休眠毫秒数,假设设置为非正数,则不执行空暇对象驱赶线程。 long timeBetweenEvictionRunsMillis = 1000; bds2.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); // 超时取回 bds2.setLogAbandoned(logAbandoned); bds2.setAbandonedUsageTracking(logAbandoned); bds2.setRemoveAbandonedOnMaintenance(removeAbandoned); bds2.setRemoveAbandonedOnBorrow(removeAbandoned); bds2.setRemoveAbandonedTimeout(removeAbandonedTimeout); pools.put(connPoolName, bds2); dbTypes.put(connPoolName, dbtype); log.debug("Init DataSource " + connPoolName + "..."); } } catch (Exception e) { log.error("Init DataSource " + connPoolName + "...ERROR", e); throw new RuntimeException(e); } } /** * 动态加入连接池 */ public boolean addDataSource(String key, DataSource datasource) { pools.put(key, datasource); return true; } /** * 动态删除连接池 */ public void removeDataSource(String key) { if (key == null) return; BasicDataSource cds = (BasicDataSource) pools.remove(key); try { cds.close(); } catch (SQLException e) { log.error("Close DS Error key={}", key, e); } cds = null; } /** * 获取一个默认的可用连接. * * @return DataSource */ public DataSource getDataSource() { if (DefaultConnectionPoolName == null || DefaultConnectionPoolName.trim().isEmpty()) return null; else return (DataSource) pools.get(DefaultConnectionPoolName); } /** * 获取一个可用连接. * * @param name * 连接池名字 * @return DataSource */ public DataSource getDataSource(String name) { return (DataSource) pools.get(name); } /** * close all connection. <br> * 关闭全部闲置连接. */ public synchronized void shutdown() { Enumeration<String> allkeys = pools.keys(); while (allkeys.hasMoreElements()) { String poolName = (String) allkeys.nextElement(); log.warn("DataSourceManager shutdown pool[{}]...", poolName); BasicDataSource cpds = (BasicDataSource) pools.remove(poolName); try { cpds.close(); } catch (SQLException e) { log.error("Close DS Error key={}", poolName, e); } cpds = null; dbTypes.remove(poolName); } } public String getDefaultDataSourceName() { return DefaultConnectionPoolName; } public String getDBType(String name) { return (String) dbTypes.get(name); } public Set<String> getPoolNames() { return pools.keySet(); } /** * 获取一个默认的可用连接. * * @return DataSource */ public Connection getConnection() { return getConnection(DefaultConnectionPoolName); } /** * 获取一个可用连接. * * @param name * 连接池名字 * @return DataSource */ public Connection getConnection(String name) { // Objects.requireNonNull(name, "PoolName should not be null"); Preconditions.checkNotNull(name, "PoolName should not be null"); Preconditions.checkArgument(!name.trim().isEmpty(), "PoolName should not be empty"); BasicDataSource thisDS = (BasicDataSource)pools.get(name); //String dbType = dbTypes.get(name); Preconditions.checkState(thisDS != null, "DataSource " + name + " is null"); Preconditions.checkState(!thisDS.isClosed(), "DataSource " + name + " has closed"); try { Connection conn = thisDS.getConnection(); //setCallerInfo(conn); return conn; } catch (SQLException e) { throw new RuntimeException(e); } } /** * 获取直接的数据库conn * * @param driverClassName * @param dbUrl * @param userName * @param password * @return * @throws InstantiationException * @throws IllegalAccessException * @throws ClassNotFoundException * @throws SQLException */ public static Connection getDirectJDBCConnection(String driverClassName, String dbUrl, String userName, String password) throws InstantiationException, IllegalAccessException, ClassNotFoundException, SQLException { Class.forName(driverClassName).newInstance(); Connection conn = DriverManager.getConnection(dbUrl, userName, password); return conn; } }



    1.1.4. JdbcUtils工具类实现

    package cn.xhgg.test;
    import java.sql.Connection;
    import java.sql.SQLException;
    import javax.sql.DataSource;
     
    public class JdbcUtils {
    // 使用ThreadLocal存储当前线程中的Connection对象
    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>();
     
    /**
     * 获取读数据源
     * @return
     * @throws SQLException
     */
    public static DataSource getReadDataSource(){
    return DataSourceManager.getInstance().getDataSource("3000");
    }
    /**
     * 获取写数据源
     * @return
     */
    public static DataSource getWriteDataSource(){
    return DataSourceManager.getInstance().getDataSource("3000");
    }
     
    public static Connection getConnection() throws SQLException {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn == null) {
    throw new SQLException("no connection init");
    }
    return conn;
    }
    public static Connection getConnection(boolean isCreate) throws SQLException {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn == null&&isCreate) {
     loadReadConnection();
    }
    return getConnection();
    }
     
    /**
     * @Method: startTransaction
     * @Description: 开启事务
     *
     */
    public static void loadReadConnection() {
    try {
    Connection conn = threadLocal.get();
    if (conn == null) {
    conn = getReadDataSource().getConnection();
    // 把 conn绑定到当前线程上
    threadLocal.set(conn);
    }
    // 开启事务
    conn.setAutoCommit(false);
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
    /**
     * @Method: startTransaction
     * @Description: 开启事务
     *
     */
    public static void startTransaction() {
    try {
    Connection conn = threadLocal.get();
    if (conn == null) {
    conn = getWriteDataSource().getConnection();
    // 把 conn绑定到当前线程上
    threadLocal.set(conn);
    }
    // 开启事务
    conn.setAutoCommit(false);
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
     
    /**
     * @Method: rollback
     * @Description:回滚事务
     *
     */
    public static void rollback() {
    try {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn != null) {
    // 回滚事务
    conn.rollback();
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
     
    /**
     * @Method: commit
     * @Description:提交事务
     *
     */
    public static void commit() {
    try {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn != null) {
    conn.commit();
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
     
    /**
     * @Method: close
     * @Description:关闭数据库连接(注意,并非真的关闭。而是把连接还给数据库连接池)
     *
     */
    public static void close() {
    try {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn != null) {
    conn.close();
    // 解除当前线程上绑定conn
    threadLocal.remove();
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    }
    public static boolean isClosed(){
    try {
    // 从当前线程中获取Connection
    Connection conn = threadLocal.get();
    if (conn != null) {
    return false;
    }
    } catch (Exception e) {
    throw new RuntimeException(e);
    }
    return true;
    }
     
    }


    1.1.5. 測试

    Connection connection = JdbcUtils.getConnection(true);
    System.out.println(connection);


    输出例如以下图:

     

    測试OK

    1.1.6. 思考获取数据库连接与ThreadLocal

    用户的请求处理过程: controller-->>service-->>dao层。

    所以先实例化controller层并初始化service实例对象。调用service方法,service实例化依赖的dao层。

    并进行数据库的操作。然后依次返回。

    OpenSessionInView模式不就是解决session关闭,不能在层之间传递的问题。

    逻辑例如以下图:

     


     

     

  • 相关阅读:
    python中get pass用法
    python中get pass用法
    python中get pass用法
    C#委托的介绍(delegate、Action、Func、predicate)
    数据库查询优化的一些总结
    正则表达式的一些基础语法
    w3school上系统过了一遍Jquery的总结
    JavaScript遍历XML总结
    2013学习总结----JavaScript
    xgqfrms™, xgqfrms® : xgqfrms's offical website of GitHub!
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8305069.html
Copyright © 2011-2022 走看看