1、 概述
数据库连接是很“宝贵的”,如果每次获取Connection
都去创建数据库连接,使用之后就断开,再次使用又重新创建,程序效率是很低的。因为Socket
连接的建立很消耗资源。
所以需要数据库连接池,数据库连接池也被称为数据源即DataSource
,JAVA
中为了使用者更加规范的编写数据源类,定义了java.sql.DataSource
接口,如果我们要编写自己的连接池,就要实现这个接口。
程序通过连接池预先同数据库建立一些连接,放在内存中,需要数据库连接时直接到连接池中取一个就行,用完后再放回去。
该接口声明了两个获取数据库连接的方法
1 public interface DataSource extends CommonDataSource, Wrapper { 2 3 Connection getConnection() throws SQLException; 4 5 Connection getConnection(String username, String password) throws SQLException; 6 }
2、DBCP 数据源概述
DBCP(DataBase Connection Pool)
由Apache
开发,可以让程序自动管理数据库连接的释放和断开。
需要使用的jar包:commons-dbcp-1.4.jar、commons-pool-1.5.4.jar、commons-logging-1.0.4.jar、commons-collections-3.1.jar
重要的配置参数:
driverClassName | 数据库驱动 |
url | 数据库连接url |
username | 数据库连接用户名 |
password | 数据库连接密码 |
initialSize | 初始连接数量 |
maxActive | 最大活跃连接数量 |
maxIdle | 最大空闲连接数量 |
minIdle | 最小空闲连接数量 |
maxWaitMillis | 在抛出异常之前等待的最大毫秒数(当没有可用的连接时),默认-1无限期地等待 |
validationQuery | 验证连接时使用的SQL |
testOnCreate | 连接创建时验证 |
testOnBorrow | 连接获取时验证 |
testOnReturn | 连接归还时验证 |
testWhileIdle | 是否验证空闲连接,默认false不验证。 如果一个对象无法验证,将从池中删除 |
timeBetweenEvictionRunsMillis | 运行空闲对象删除线程的时间间隔,单位为毫秒,默认为-1即不执行任务 |
numTestsPerEvictionRun | 空闲对象删除线程每次运行时检查的对象数量(如果有的话),默认3个 |
minEvictableIdleTimeMillis | 在空闲对象删除线程可以删除连接之前,连接可以在池中空闲的最少时间(如果有的话),默认1000 * 60 * 30 |
maxConnLifetimeMillis | 连接的最长寿命(以毫秒为单位)。超过此时间后,连接将在下一次激活、钝化或验证测试时失败。0或更小意味着连接具有无限寿命 |
removeAbandonedTimeout | Timeout in seconds before an abandoned connection can be removed |
3、 设计思路
1) 在jdbc.properties
文件配置数据源实现类类型,以及该数据源的属性信息;
2) 在DBUtil
工具类中解析配置文件,获取到使用的数据源类型,并通过反射实例化;
3) 使用beanutils
框架给实例化的数据源对象注入属性,需要使用到commons-beanutils-1.9.3.jar
框架;
4) 修改getConnection
方法从数据源获取连接
之所以这样做,是为了程序代码和具体数据源实现类的解耦,当不再使用DBCP
改用C3P0
时,我们可以通过修改jdbc.properties
配置文件而不再需要修改程序代码。
4、 配置 jdbc.properties
##### DBCP Configuration ##### dataSourceClassName=org.apache.commons.dbcp.BasicDataSource driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false username=system password=123456 initialSize=1 maxActive=10 maxIdle=5 minIdle=2 maxWaitMillis=-1 validationQuery=select 1 testWhileIdle=true timeBetweenEvictionRunsMillis=60000 numTestsPerEvictionRun=3 minEvictableIdleTimeMillis=60000 softMinEvictableIdleTimeMillis=60000 maxConnLifetimeMillis=300000 removeAbandonedTimeout=300
5、DBUtil 工具类
1 public class DBUtil { 2 3 /** 4 * 数据源对象 5 */ 6 private static DataSource ds; 7 8 private static Properties prop = new Properties(); 9 10 static { 11 try { 12 // 读取加载jdbc配置文件 13 prop.load(DBUtil.class.getClassLoader().getResourceAsStream( 14 "jdbc.properties")); 15 16 // 获取数据源实现类类型 17 String dataSourceClassName = prop 18 .getProperty("dataSourceClassName"); 19 20 // 实例化数据源实现类 21 Class<?> clazz = Class.forName(dataSourceClassName); 22 ds = (DataSource) clazz.newInstance(); 23 24 prop.remove("dataSourceClassName"); 25 26 // 为数据源注入属性 27 Map<String, Object> p = new HashMap<String, Object>(); 28 for (Object key : prop.keySet()) { 29 p.put(key.toString(), prop.get(key)); 30 } 31 BeanUtils.populate(ds, p); 32 33 } catch (IOException e) { 34 throw new RuntimeException("加载JDBC配置失败", e); 35 } catch (ClassNotFoundException e) { 36 throw new RuntimeException("数据源实现类未找到", e); 37 } catch (InstantiationException e) { 38 throw new RuntimeException("创建数据源实现类对象失败", e); 39 } catch (IllegalAccessException e) { 40 throw new RuntimeException("创建数据源实现类对象失败", e); 41 } catch (InvocationTargetException e) { 42 throw new RuntimeException("数据源属性注入时失败", e); 43 } 44 } 45 46 /** 47 * 从数据源中获取一个数据库连接 48 * 49 * @return 数据库连接 50 * @throws SQLException 51 */ 52 public static Connection getConnection() throws SQLException { 53 return ds.getConnection(); 54 } 55 }