数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的应用中体现得尤为突出。对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标。数据库连接池正是针对这个问题提出来的在进行JDBC操作过程中,几乎一半以上的时间是用来获取数据库连接。没有办法减少一次使用数据库连接的代价,所以考虑使用池化资源的方式降低连接的平均使用成本数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。
德鲁伊连接池
常见配置参数
配置 | 缺省 | 说明 |
Name | 配置这个属性的意义在于,如果存在多个数据源,监 控的时候可以通过名字来区分开来。 如果没有配 置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this) | |
jdbcUrl | 连接数据库的url,不同数据库不一样。如mysql数 据库为 jdbc:mysql://10.20.153.104:3306/druid2, 针对oracle数据库 dbc:oracle:thin:@10.20.149.85:1521:ocnauto | |
username | 连接数据库的用户名 | |
Password | 连接数据库的密码。如果不希望密码直接写在配置文件中,可以使用ConfigFilter。 | |
driverClassName | 根据url自动识别,可配可不配,如果不配置druid会 根据url自动识别dbType,然后选择相应的driverClassName | |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调 用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。 | |
poolPreparedStatements | False | 是否缓存preparedStatement,PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭 |
maxOpenPreparedStatements | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语 句。如果validationQuery为null,testOnBorrow、 testOnReturn、testWhileIdle都不会其作用。 | |
testOnBorrow | True | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 |
testOnReturn | False | 归还连接时执行validationQuery检测连接是否有 效,做了这个配置会降低性能 |
testWhileIdle | False | 建议配置为true,不影响性能,并且保证安全性。申 请连接的时候检测,如果空闲时间大于 timeBetweenEvictionRunsMillis,执行 validationQuery检测连接是否有效 |
timeBetweenEvictionRunsMillis | 有两个含义: 1)Destroy线程会检测连接的间隔时间 2)testWhileIdle的判断依据 | |
minEvictableIdleTimeMillis | 一个链接的最小存活时间,单位为毫秒 | |
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 当数据库抛出一些不可恢复的 异常时,抛弃连接 | |
Filters | 属性类型是字符串,通过别名的方式配置扩展插件, 常用的插件有: 监控统计用的filter:stat,日志用的 filter:log4j,防御sql注入的filter:wall | |
proxyFilters | 类型是List,如果同时配置了filters和proxyFilters, 是组合关系,并非替换关系 |
连接池的使用
在一个线程的处理过程中可能会出现多次访问数据库,线程使用连接对象时没有必要频繁的从池中获取,再归还,再获取。可以在一个线程开始执行时获取对应的连接,处理完毕后再归还连接
配置文件:src/jdbc.properties
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC
username=root
password=123456
initialSize=5
maxActive=10
maxWait=1000
JDBCTools工具类:
public class JdbcUtils {
private JdbcUtils() {
}
private static DataSource ds;
private static final ThreadLocal<Connection> ts = new ThreadLocal<>();
static {
try {
InputStream is = JdbcUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
Properties ps = new Properties();
ps.load(is);
ds = DruidDataSourceFactory.createDataSource(ps);
} catch (Exception e) {
e.printStackTrace();
}
}
// 获取连接的方法
public static Connection getConnection() {
Connection res = ts.get();
if (res == null) {
try {
res = ds.getConnection();
ts.set(res);
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
return res;
}
// 释放连接的方法
public static void releaseConnention() {
Connection conn = ts.get();
if (conn != null) {
try {
conn.close();
ts.remove();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}
public static int executeUpdate(String sql, Object... params) {
try {
System.out.println(sql);
return createPreparedStatement(sql, params).executeUpdate();
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
public static ResultSet executeQuery(String sql, Object... params) {
try {
return createPreparedStatement(sql, params).executeQuery();//执行后返回代表查询结果的ResultSet对象。
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
public static PreparedStatement createPreparedStatement(String sql, Object... params) {
try {
Connection conn = getConnection();
PreparedStatement ps = conn.prepareStatement(sql);
if (params != null && params.length > 0)
for (int i = 0; i < params.length; i++)
ps.setObject(i + 1, params[i]);
System.out.println(ps);
return ps;
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}
}
}