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关闭,不能在层之间传递的问题。

逻辑例如以下图:

 


 

 

【推广】 免费学中医,健康全家人
原文地址:https://www.cnblogs.com/zhchoutai/p/8305069.html