数据库连接池
传统方式获取 Connection 问题
1、每次向数据库建立连接时,都将 Connection 加载到内存,再验证 IP 地址、用户名、密码,频繁连接占用系统资源,服务器易崩溃
2、每次连接断开,若程序异常未能关闭,将导致数据库内存泄漏,导致数据库重启
3、不能控制创建连接数量
数据库连接池
1、预先在缓冲池放入一定数量连接,需要建立数据库连接时,从缓冲池中取出一个连接,使用完再放回
2、数据库连接池负责分配、管理、释放数据库连接,允许应用程序复用一个现有的数据库连接,而不是重建一个连接
3、当应用程序向连接池请求的连接数,超过最大连接数量时,这些请求将被加入到等待队列
4、JDBC 数据库连接池使用 javax.sql.DataSource 表示,该接口由第三方实现
5、种类
名称 | 说明 |
C3P0 | 速度相对慢,稳定性高 |
DBCP | 速度相对 CP30 快,不稳定 |
Proxool | 稳定性相对 C3P0 差,有监控连接池状态的功能 |
BoneCP | 速度快 |
Druid | 集 CP30、DBCP、Proxool 优点 |
Druid
1、DruidDataSource 配置
(1).xml 配置文件,通用配置
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<property name="url" value="${jdbc_url}" />
<property name="username" value="${jdbc_user}" />
<property name="password" value="${jdbc_password}" />
<property name="filters" value="stat" />
<property name="maxActive" value="20" />
<property name="initialSize" value="1" />
<property name="maxWait" value="6000" />
<property name="minIdle" value="1" />
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<property name="poolPreparedStatements" value="true" />
<property name="maxOpenPreparedStatements" value="20" />
<property name="asyncInit" value="true" />
</bean>
(2)配置属性列表,DruidDataSource 配置兼容DBCP,但个别配置的语意有所区别
配置 | 缺省值 | 说明 |
name | 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。如果没有配置,将会生成一个名字,格式是:"DataSource-" + System.identityHashCode(this). 另外配置此属性至少在1.0.5版本中是不起作用的,强行设置name会出错 | |
url | 连接数据库的url,不同数据库不一样。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
|
username | 连接数据库的用户名 | |
password | 连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter | |
driverClassName | 根据url自动识别 | 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName |
initialSize | 0 | 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 |
maxActive | 8 | 最大连接池数量 |
maxIdle | 8 | 已经不再使用,配置了也没效果 |
minIdle | 最小连接池数量 | |
maxWait | 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁 | |
poolPreparedStatements | false | 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭 |
maxPoolPreparedStatementPerConnectionSize | -1 | 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100 |
validationQuery | 用来检测连接是否有效的sql,要求是一个查询语句,常用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用 | |
validationQueryTimeout | 单位:秒,检测连接是否有效的超时时间。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)方法 | |
testOnBorrow | true | 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testOnReturn | false | 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能 |
testWhileIdle | false | 建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效 |
keepAlive | false(1.0.28) | 连接池中的minIdle数量以内的连接,空闲时间超过minEvictableIdleTimeMillis,则会执行keepAlive操作 |
timeBetweenEvictionRunsMillis | 1分钟(1.0.14) | 有两个含义: 1) Destroy线程会检测连接的间隔时间,如果连接空闲时间大于等于minEvictableIdleTimeMillis则关闭物理连接。 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明 |
numTestsPerEvictionRun | 不再使用,一个DruidDataSource只支持一个EvictionRun | |
minEvictableIdleTimeMillis | 连接保持空闲而不被驱逐的最小时间 | |
connectionInitSqls | 物理连接初始化的时候执行的sql | |
exceptionSorter | 根据dbType自动识别 | 当数据库抛出一些不可恢复的异常时,抛弃连接 |
filters | 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall |
|
proxyFilters | 类型是List<com.alibaba.druid.filter.Filter>,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系 |
(3)导入 .properties 配置文件,获取连接
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.util.Properties;
public class Program {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
//假设配置文件路径 src:\druid.properties
properties.load(new FileInputStream("src:\\druid.properties"));
DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
Connection connection = dataSource.getConnection();
}
}
2、基于 Druid 的 JDBCUtils
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
public class JDBCUtilsByDruid {
public static DataSource dataSource;
static {
Properties properties = new Properties();
try {
properties.load(new FileInputStream("src\\druid.properties"));
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
//close把使用的Connection对象放回连接池
public static void close(ResultSet resultSet, PreparedStatement preparedStatement, Connection connection) {
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
传统方式使用 ResultSet 的问题
1、ResultSet 与 Connection 关联,关闭连接后,不能继续使用 ResultSet
2、ResultSet 不利于数据管理,返回信息不方便
3、解决:DBUtils,JavaBean
JavaBean / POJO / Domain
1、解决 ResultSet 问题
2、表中的列与 JavaBean 中的属性存在映射(关联)关系
(1)数值型对应其包装类,因为 MySQL 所有类型都允许 NULL,而 Java 只有引用类型允许 NULL
(2)MySQL <-> Java:CHAR、VARCHAR <-> String,DATE <-> String / Date
3、DAO(Data Access Object):数据访问对象,DAO 操作 JavaBean,即具体表
DBUtils
1、commons-dbutils 是 Apache 组织提供的开源 JDBC 工具类库,对 JDBC 封装,极大简化 JDBC 编码的工作量
2、QueryRunner 类:封装 SQL 执行,线程安全,实现增删改查、批处理、查询
3、ResultSetHandler 接口:处理 java.sql.ResultSet,将数据按要求转换为另一种形式
4、实现 ResultSetHandler 接口的 JavaBean
(1)ArrayHandler:把结果集中的第一行数据转成对象数组
(2)ArrayListHandler:把结果集中的每一行数据都转成一个数组,再存放到List中
(3)BeanHandler:将结果集中的第一行数据封装到一个对应 JavaBean 实例中
(4)BeanListHandler:将结果集中的每一行数据都封装到一个对应 JavaBean 实例中,存放到List里
(5)ColumnListHandler:将结果集中某一列的数据存放到 List 中
(6)KeyedHandler:将结果集中的每行数据都封装到 Map 里,再把这些 Map 再存到另一个 Map 里,其 key 为指定的 key
(7)MapHandler:将结果集中的第一行数据封装到一个 Map 里,key 是列名,value 是对应的值
(8)MapListHandler:将结果集中的每一行数据都封装到一个 Map 里,然后再存放到 List
DBUtils + Druid 的缺点
1、SQL 语句固定,不能通过参数传递,通用性低
2、对于 SELECT 操作,若有返回值,返回类型不确定,需要使用泛型
3、表的数量较多时,DAO 代码冗余复杂
BasicDAO
1、抽象各个 DAO 的共同操作
2、简化代码,提高维护性、可读性
3、通用类,专门与数据库交互,完成对数据表的 CRUD 操作
4、实现一张表对应一个具体 DAO
Service:业务层,不同 Service 调用单个 / 多个 DAO 完成综合操作
View:界面层,调用 Service,得到结果,显示数据
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步