转载请注明出处:http://blog.csdn.net/xiaojimanman/article/details/43272993
通过java程序去连接数据库时,使用的协议是TCP/IP协议,TCP/IP协议需要进行3次握手。如果每一次数据库操作都需要创建一个新的连接,都要进行3次握手,这是十分浪费资源的,程序的效率也不是很高。为了解决这个问题,我们想可不可以自己维护一些数据库连接,需要数据库操作的时候,直接使用这其中的一个连接,用完了,在还给它,这样的话就不需要每次数据库操作都创建一个新的连接了。这种思维模式就是今天的博客主题数据库连接池。
基本原理
连接池技术的核心思想是:连接复用,通过建立一个数据库连接池以及一套连接使用、分配、管理策略,使得该连接池中的连接可以得到高效、安全的复用,避免了数据库连接频繁建立、关闭的开销。另外,由于对JDBC中的原始连接进行了封装,从而方便了数据库应用对于连接的使用(特别是对于事务处理),提高了开发效率,也正是因为这个封装层的存在,隔离了应用的本身的处理逻辑和具体数据库访问逻辑,使应用本身的复用成为可能。连接池主要由三部分组成(如下图所示):连接池的建立、连接池中连接的使用管理、连接池的关闭。
连接池实现
这里的介绍的连接池是proxool,这里对其做了进一步的封装,使数据库操作更加简单。
DBPool
DBPool类用于指定数据库连接池的配置文件,源程序如下:
/** *@Description: 数据库连接池配置 */ package com.lulei.db.manager; import org.apache.log4j.Logger; import com.lulei.util.ClassUtil; public class DBPool { private static DBPool dbPool = null; private String poolPath; private static Logger log = Logger.getLogger(DBPool.class); private static String path = ClassUtil.getClassRootPath(DBPool.class); public static DBPool getDBPool(){ if (dbPool == null){ synchronized(DBPool.class){ if (dbPool == null){ dbPool = new DBPool(); } } } return dbPool; } private DBPool(){ } /** * @param poolPath * @Author: lulei * @Description: 设置数据库连接池配置文件路径 */ public void setPoolPath(String poolPath){ this.poolPath = poolPath; } /** * @return * @Author: lulei * @Description: 返回数据库连接池配置文件路径 */ protected String getPoolPath(){ //如果没有指定配置文件,则使用默认配置文件 if (poolPath == null){ poolPath = path + "proxool.xml"; log.info("Database's poolpath is null, use default path:" + poolPath); } return poolPath; } }
在配置文件中,参数介绍如下:
fatal-sql-exception: 它是一个逗号分割的信息片段.当一个SQL异常发生时,他的异常信息将与这个信息片段进行比较.如果在片段中存在,那么这个异常将被认为是个致命错误(Fatal SQL Exception ).这种情况下,数据库连接将要被放弃.无论发生什么,这个异常将会被重掷以提供给消费者.用户最好自己配置一个不同的异常来抛出.
fatal-sql-exception-wrapper-class:正如上面所说,你最好配置一个不同的异常来重掷.利用这个属性,用户可以包装SQLException,使他变成另外一个异常.这个异常或者继承SQLException或者继承字RuntimeException.proxool自带了2个实现:‘org.logicalcobwebs.proxool.FatalSQLException‘ 和‘org.logicalcobwebs.proxool.FatalRuntimeException‘ .后者更合适.
house-keeping-sleep-time: house keeper 保留线程处于睡眠状态的最长时间,house keeper 的职责就是检查各个连接的状态,并判断是否需要销毁或者创建.
house-keeping-test-sql: 如果发现了空闲的数据库连接.house keeper 将会用这个语句来测试.这个语句最好非常快的被执行.如果没有定义,测试过程将会被忽略。
injectable-connection-interface: 允许proxool实现被代理的connection对象的方法.
injectable-statement-interface: 允许proxool实现被代理的Statement 对象方法.
injectable-prepared-statement-interface: 允许proxool实现被代理的PreparedStatement 对象方法.
injectable-callable-statement-interface: 允许proxool实现被代理的CallableStatement 对象方法.
jmx: 略
jmx-agent-id: 略
jndi-name: 数据源的名称
maximum-active-time: 如果housekeeper 检测到某个线程的活动时间大于这个数值.它将会杀掉这个线程.所以确认一下你的服务器的带宽.然后定一个合适的值.默认是5分钟.
maximum-connection-count: 最大的数据库连接数.
maximum-connection-lifetime: 一个线程的最大寿命.
minimum-connection-count: 最小的数据库连接数
overload-without-refusal-lifetime: 略
prototype-count: 连接池中可用的连接数量.如果当前的连接池中的连接少于这个数值.新的连接将被建立(假设没有超过最大可用数).例如.我们有3个活动连接2个可用连接,而我们的prototype-count是4,那么数据库连接池将试图建立另外2个连接.这和 minimum-connection-count不同. minimum-connection-count把活动的连接也计算在内.prototype-count 是spare connections 的数量.
recently-started-threshold: 略
simultaneous-build-throttle: 略
statistics: 连接池使用状况统计。 参数“10s,1m,1d”
statistics-log-level: 日志统计跟踪类型。 参数“ERROR”或 “INFO”
test-before-use: 略
test-after-use: 略
trace: 如果为true,那么每个被执行的SQL语句将会在执行期被log记录(DEBUG LEVEL).你也可以注册一个ConnectionListener (参看ProxoolFacade)得到这些信息.
verbose: 详细信息设置。 参数 bool 值
在本例中数据库连接池配置文件如下:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd"> <proxool-config> <proxool> <alias>novelSelect</alias> <driver-url><![CDATA[jdbc:mysql://172.20.37.73:3306/novel?characterEncoding=utf-8]]></driver-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <driver-properties> <property name="user" value="root"/> <property name="password" value="root"/> </driver-properties> <house-keeping-sleep-time>900000</house-keeping-sleep-time> <maximum-active-time>500000</maximum-active-time> <maximum-connection-count>40</maximum-connection-count> <minimum-connection-count>4</minimum-connection-count> <house-keeping-test-sql>select 1</house-keeping-test-sql> <prop key="hibernate.connection.release_mode">after_transaction</prop> </proxool> <proxool> <alias>novelEdit</alias> <driver-url><![CDATA[jdbc:mysql://172.20.37.73:3306/novel?characterEncoding=utf-8]]></driver-url> <driver-class>com.mysql.jdbc.Driver</driver-class> <driver-properties> <property name="user" value="root"/> <property name="password" value="root"/> </driver-properties> <house-keeping-sleep-time>900000</house-keeping-sleep-time> <maximum-active-time>500000</maximum-active-time> <maximum-connection-count>10</maximum-connection-count> <minimum-connection-count>4</minimum-connection-count> <house-keeping-test-sql>select 1</house-keeping-test-sql> <prop key="hibernate.connection.release_mode">after_transaction</prop> </proxool> </proxool-config>
DBManager
DBManager在系统中是单例模式,在初始化只需要简单的两句代码:
JAXPConfigurator.configure(DBPool.getDBPool().getPoolPath(), false); Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
获取数据库连接也只是简单的一句代码:
return DriverManager.getConnection(poolName);
DBManager源代码如下:
/** *@Description: 数据库连接池管理 */ package com.lulei.db.manager; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import org.logicalcobwebs.proxool.configuration.JAXPConfigurator; public class DBManager { private static DBManager dBManager = null; private DBManager(){ try { JAXPConfigurator.configure(DBPool.getDBPool().getPoolPath(), false); Class.forName("org.logicalcobwebs.proxool.ProxoolDriver"); } catch (Exception e){ e.printStackTrace(); } } /** * @return DBManager * @Author: lulei * @Description: 获取数据库连接池管理对象 */ protected static DBManager getDBManager(){ if (dBManager == null){ synchronized(DBManager.class){ if (dBManager == null){ dBManager = new DBManager(); } } } return dBManager; } /** * @param poolName * @return Connection * @throws SQLException * @Author: lulei * @Description: 获取数据库链接 */ protected Connection getConnection(String poolName) throws SQLException{ return DriverManager.getConnection(poolName); } }
DBOperation
为了简化数据库的操作,对数据库操作进行再一次封装成DBOperation类。在setPres方法中,这里只做了几种简单的数据类型,关于其他复杂的数据类型可以根据项目需要添加。源代码如下:
/** *@Description: 数据库操作 */ package com.lulei.db.manager; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.HashMap; import org.apache.log4j.Logger; public class DBOperation { private static Logger log = Logger.getLogger(DBOperation.class); private Connection conn = null; private String poolName; /** * @param poolName */ public DBOperation(String poolName){ this.poolName = poolName; } /** * @throws SQLException * @Author: lulei * @Description: 获取Connection */ private void open() throws SQLException{ this.conn = DBManager.getDBManager().getConnection(poolName); } /** * @Author: lulei * @Description: 关闭Connection */ public void close() { try { if (this.conn != null) { this.conn.close(); } } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /** * @param sql组装的sql字符串 * @param params传入的参数 * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: 组装PreparedStatement */ private PreparedStatement setPres(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ if (null != params) { if (0 < params.size()){ PreparedStatement pres = this.conn.prepareStatement(sql); for (int i = 1; i <= params.size(); i++){ if (params.get(i).getClass() == Class.forName("java.lang.String")){ pres.setString(i, params.get(i).toString()); } else if (params.get(i).getClass() == Class.forName("java.lang.Integer")){ pres.setInt(i, (Integer) params.get(i)); } else if (params.get(i).getClass() == Class.forName("java.lang.Boolean")){ pres.setBoolean(i, (Boolean) params.get(i)); } else if (params.get(i).getClass() == Class.forName("java.lang.Float")){ pres.setFloat(i, (Float) params.get(i)); } else if (params.get(i).getClass() == Class.forName("java.lang.Double")){ pres.setDouble(i, (Double) params.get(i)); } else if (params.get(i).getClass() == Class.forName("java.lang.Long")){ pres.setLong(i, (Long) params.get(i)); } else if (params.get(i).getClass() == Class.forName("java.sql.Date")){ pres.setDate(i, java.sql.Date.valueOf(params.get(i).toString())); } else { log.info("not found class : " + params.get(i).getClass().toString()); return null; } } return pres; } } return null; } /** * @param sql * @return int * @throws SQLException * @Author: lulei * @Description: executeUpdate */ protected int executeUpdate(String sql) throws SQLException{ this.open(); Statement state = this.conn.createStatement(); int re = state.executeUpdate(sql); return re; } /** * executeUpdate * @param sql * @param params * @return int * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: */ protected int executeUpdate(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ this.open(); PreparedStatement pres = setPres(sql, params); int re = 0; if (null != pres) { re = pres.executeUpdate(); } return re; } /** * getGeneratedKeys * @param sql * @return ResultSet * @throws SQLException * @Author: lulei * @Description: */ protected ResultSet getGeneratedKeys(String sql) throws SQLException{ this.open(); Statement state = this.conn.createStatement(); state.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); ResultSet re = state.getGeneratedKeys(); return re; } /** * getGeneratedKeys * @param sql * @param params * @return ResultSet * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: */ protected ResultSet getGeneratedKeys(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ this.open(); PreparedStatement pres = setPres(sql, params); if (null != pres) { pres.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); ResultSet re = pres.getGeneratedKeys(); return re; } return null; } /** * @param sql * @return ResultSet * @throws SQLException * @Author: lulei * @Description: executeQuery */ protected ResultSet executeQuery(String sql) throws SQLException{ this.open(); Statement state = this.conn.createStatement(); ResultSet re = state.executeQuery(sql); return re; } /** * @param sql * @param params * @return ResultSet * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: executeQuery */ protected ResultSet executeQuery(String sql, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ this.open(); PreparedStatement pres = setPres(sql, params); if (null != pres) { ResultSet re = pres.executeQuery(); return re; } return null; } }
DBServer
DBServer对数据库的增删该查操作进行进一步的细化,源代码如下:
/** *@Description: 增删改查四个数据库操作接口 */ package com.lulei.db.manager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; public class DBServer { private DBOperation dBOperation; /** * @param poolName * @Description: 在使用该类之前,请保证函数DBPool.getDBPool().setPoolPath()已经运行 */ public DBServer(String poolName){ dBOperation = new DBOperation(poolName); } /** * @Author: lulei * @Description: 释放链接,在执行完数据库操作,必须执行此命令 */ public void close(){ dBOperation.close(); } /** * @param table * @param columns * @param params * @return int * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: insert 执行完此命令后,执行close()操作 */ public int insert(String table, String columns, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ String sql = insertSql(columns, table); return dBOperation.executeUpdate(sql, params); } /** * @param sql * @return int * @throws SQLException * @Author: lulei * @Description: insert 执行完此命令后,执行close()操作 */ public int insert(String sql) throws SQLException { return dBOperation.executeUpdate(sql); } /** * @param table * @param columns * @param params * @return ResultSet * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: insertGetGeneratedKeys 执行完此命令后,执行close()操作 */ public ResultSet insertGetGeneratedKeys(String table, String columns, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ String sql = insertSql(columns, table); return dBOperation.getGeneratedKeys(sql, params); } /** * @param sql * @return ResultSet * @throws SQLException * @Author: lulei * @Description: insertGetGeneratedKeys 执行完此命令后,执行close()操作 */ public ResultSet insertGetGeneratedKeys(String sql) throws SQLException{ return dBOperation.getGeneratedKeys(sql); } /** * @param table * @param condition * @return int * @throws SQLException * @Author: lulei * @Description: delete 执行完此命令后,执行close()操作 */ public int delete(String table, String condition) throws SQLException{ if(null == table){ return 0; } String sql = "delete from " + table + " " + condition; return dBOperation.executeUpdate(sql); } /** * @param sql * @return int * @throws SQLException * @Author: lulei * @Description: delete 执行完此命令后,执行close()操作 */ public int delete(String sql) throws SQLException{ return dBOperation.executeUpdate(sql); } /** * @param columns * @param table * @param condition * @return ResultSet * @throws SQLException * @Author: lulei * @Description: select 执行完此命令后,执行close()操作 */ public ResultSet select(String columns, String table, String condition) throws SQLException { String sql = "select " + columns + " from " + table + " " + condition; return dBOperation.executeQuery(sql); } /** * @param sql * @return ResultSet * @throws SQLException * @Author: lulei * @Description: select 执行完此命令后,执行close()操作 */ public ResultSet select(String sql) throws SQLException{ return dBOperation.executeQuery(sql); } /** * @param table * @param columns * @param condition * @param params * @return int * @throws SQLException * @throws ClassNotFoundException * @Author: lulei * @Description: update 执行完此命令后,执行close()操作 */ public int update(String table, String columns, String condition, HashMap<Integer, Object> params) throws SQLException, ClassNotFoundException{ String sql = updateString(table, columns, condition); return dBOperation.executeUpdate(sql, params); } /** * @param sql * @return int * @throws SQLException * @Author: lulei * @Description: update 执行完此命令后,执行close()操作 */ public int update(String sql) throws SQLException{ return dBOperation.executeUpdate(sql); } /** * @param table * @param columns * @param condition * @return String * @Author: lulei * @Description: 组装updateString */ private String updateString(String table, String columns, String condition) { if (null == columns || null == table) { return ""; } String[] column = columns.split(","); StringBuilder stringBuilder = new StringBuilder("update "); stringBuilder.append(table); stringBuilder.append(" set "); stringBuilder.append(column[0]); stringBuilder.append("=?"); for (int i = 1; i < column.length; i++){ stringBuilder.append(", "); stringBuilder.append(column[i]); stringBuilder.append("=?"); } stringBuilder.append(" "); stringBuilder.append(condition); return stringBuilder.toString(); } /** * @param columns * @param table * @return String * @Author: lulei * @Description: 组装insertSql */ private String insertSql(String columns, String table){ if (null == columns || null == table) { return ""; } int colNum = columns.split(",").length; StringBuilder stringBuilder = new StringBuilder("insert into "); stringBuilder.append(table); stringBuilder.append(" ("); stringBuilder.append(columns); stringBuilder.append(") values (?"); for (int i = 1; i < colNum; i++) { stringBuilder.append(",?"); } stringBuilder.append(")"); return stringBuilder.toString(); } }
下面是的使用事例是其他项目中的一个例子,这里可以简单的看下,代码如下:
public boolean support(String docNo){ DBServer dbServer = new DBServer(dbPoolName); String editTime = System.currentTimeMillis() + ""; String sql = "update " + SolutionTable.tableName + " set " + SolutionTable.support + "=" + SolutionTable.support + "+1, " + SolutionTable.editTime + "='"+ editTime+"' where " +SolutionTable.docNo + "='" + docNo + "'"; try { return dbServer.update(sql) > 0; } catch (SQLException e) { e.printStackTrace(); } finally { dbServer.close(); } return false; }
ps:最近发现其他网站可能会对博客转载,上面并没有源链接,如想查看更多关于 基于lucene的案例开发 请点击这里。或访问网址http://blog.csdn.net/xiaojimanman/article/category/2841877