数据库连接池(Druid、DBCP、C3P0)和Apache组织DBUtils工具类使用
第一节:自定义连接池
使用JDBC操作数据库,需要建立Connection,使用传统的JDBC操作需要每次创建Connection,创建Connection是一个非常性能和消耗时间的过程,我们需要在提高程序性能,那么就需要减少每次创建创建连接带来的负面影响,解决这个问题我们将利用池子概念,预先创建一些链接放入池子中,如果需要操作数据,不用创建新的Connection,只需要在池子中获取即可,使用完毕放入池子!这样就形成了复用!
1.1自定义连接池
我们可以通过自定义的方式实现连接池!分析连接池类应该包含特定的属性和方法!
属性: 集合 放置Connection
方法: 获取连接方法
回收连接方法
具体实现代码:
public class Pool{
static LinkedList<Connection> list = new LinkedList<Connection>();
//初始化10个连接对象
static{
for (int i = 0; i < 10; i++) {
Connection connection = JDBCUtils.newInstance().getConnection();
list.add(connection);
}
}
/**
* 从连接池子中获取连接的方式
* @return
*/
public static Connection getConnection(){
//如果10个连接对象都在使用中,则list为空,自己再创建一个
if (list.isEmpty()) {
Connection connection = JDBCUtils.newInstance().getConnection();
list.addLast(connection);
}
//使用移除
Connection conn = list.removeFirst();
return conn;
}
/**
* 返回到连接池子中
*/
public static void addBack(Connection conn){
if (list.size() >= 10) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}else{
list.addLast(conn);
//10
}
}
/**
* 获取连接池子中连接数量的方法
*/
public static int getSize(){
return list.size();
}
}
1.2通过实现java规范实现连接池
Java为连接池实现提供了一个规范(接口),规范的写法,我们需要实现DataSource接口!
public class MyDbPool implements DataSource{
//创建集合 并且线程安全Collections.synchronizedList
public static List<Connection> connctions=Collections.synchronizedList(new LinkedList<Connection>()) ;
static{
try {
//读取properties文件
InputStream is=MyDbPool.class.getClassLoader().getResourceAsStream("database.properties");
Properties properties=new Properties();
properties.load(is);
String driver=properties.getProperty("driver");
String url=properties.getProperty("url");
String user=properties.getProperty("user");
String password=properties.getProperty("password");
//1加载驱动
Class.forName(driver);
for(int i=0;i<5;i++){
Connection connection=DriverManager.getConnection(url, user, password);
connctions.add(connection);
System.out.println(i+"....."+connection.hashCode());
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public Connection getConnection() throws SQLException {
// TODO Auto-generated method stub
Connection connection=connctions.remove(0);
System.out.println("获取一个连接.."+connection.hashCode());
System.out.println("池中还剩"+connctions.size());
return connection;
}
/**
* 把连接再放入池中
* @param conn
*/
public void release(Connection conn){
connctions.add(conn);
System.out.println("放入了一个连接"+conn.hashCode());
System.out.println("池中还剩"+connctions.size());
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PrintWriter getLogWriter() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
// TODO Auto-generated method stub
return false;
}
}
第二节:使用DBCP连接池
DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。
2.1 DBCP连接池的使用
2.1.1 创建项目
创建JavaWeb项目
2.1.2 导入相应jar包
mysql-jdbc.jar
commons-dbcp.jar
commons-pool.jar
2.1.3 硬编码方式使用DBCP连接池
所谓的硬编码方式就是在代码中添加配置
@Test
public void testHard() throws SQLException{
//TODO 硬编码 使用DBCP连接池子
BasicDataSource source = new BasicDataSource();
//设置连接的信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/day2");
source.setUsername("root");
source.setPassword("111");
Connection connection = source.getConnection();
String sql = "select * from student";
Statement createStatement = connection.createStatement();
ResultSet executeQuery = createStatement.executeQuery(sql);
while (executeQuery.next()) {
System.out.println(executeQuery.getString(2));
}
connection.close();
//回收
}
2.1.4 软编码方式使用DBCP连接池
所谓的软编码,就是在项目中添加配置文件,这样就不需要每次代码中添加配合
- 项目中添加配置
文件名称: info.properties,(只要是.properties结尾的)
文件位置: src下
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/day2
username=root
password=111
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最大空闲连接 -->
maxIdle=20
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=6000
- 代码实现
@Test
public void testSoft() throws Exception{
//TODO DBCP软编码连接池子使用
BasicDataSourceFactory factory = new BasicDataSourceFactory();
Properties properties = new Properties();
//配置文件添加到properties对象中 javase
properties.load(new FileInputStream("src/info.properties"));
//生成连接池子 需要配置文件
DataSource source = factory.createDataSource(properties);
Connection connection = source.getConnection();
String sql = "select * from student";
Statement createStatement = connection.createStatement();
ResultSet executeQuery = createStatement.executeQuery(sql);
while (executeQuery.next()) {
System.out.println(executeQuery.getString(2));
}
connection.close();
//回收
}
第三节:使用C3P0连接池
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
c3p0与dbcp区别
1. dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
2. dbcp需要手动设置配置文件
c3p0不需要手动设置
3.1使用步骤
3.1.1创建项目
3.1.2导入jar包
c3p0-0.9.1.2.jar
mysql-connector-java-5.0.8.jar
3.1.3添加配置文件
c3p0是在外部添加配置文件,工具直接进行应用,因为直接引用,所以要求固定的命名和文件位置
文件位置: src
文件命名:c3p0-confifig.xml/c3p0-confifig.properties
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<!-- 基本配置 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day2</property>
<property name="user">root</property>
<property name="password">111</property>
<!--扩展配置-->
<!-- 连接超过30秒报错-->
<property name="checkoutTimeout">30000</property>
<!--30秒检查空闲连接 -->
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<!-- 30秒不适用丢弃-->
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!-- 命名的配置 -->
<named-config name="zhaowf">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day</property>
<property name="user">root</property>
<property name="password">111</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">20</property>
<property name="minPoolSize">10</property>
<property name="maxPoolSize">40</property>
<property name="maxStatements">20</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
注意: c3p0的配置文件内部可以包含命名配置文件和默认配置文件!默认是选择默认配置!如果需要切换命名配置可以在创建c3p0连接池的时候填入命名即可!
3.2使用C3P0进行数据库操作
public class TestC3p0 {
public static void main(String[] args) throws Exception {
//1.创建C3P0连接池子
Connection connection = DataSourceUtils.getConnection();
Statement createStatement = connection.createStatement();
String sql = "select * from student;";
ResultSet resultSet = createStatement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
DataSourceUtils.close(connection, createStatement, resultSet);
}
}
3.3 使用C3P0连接池编写工具类
/*** 从连接池子中获取连接!
** C3P0的连接池子
* 0.获取连接池子对象 DBUtils
* 1.获取连接
* 2.关闭资源
*/
public class DataSourceUtils {
private static ComboPooledDataSource dataSource = new
ComboPooledDataSource();
/**
* 返回连接池对象方法
* @return c3p0连接池子
*/
public static ComboPooledDataSource getDataSource(){
return dataSource;
}
/**
* 连接池中获取连接的方法
* @return 连接
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
//关闭资源
public static void close(Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Statement st){
if (st != null) {
try { st.close(); }
catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(ResultSet set){
if (set != null) {
try { set.close(); }
catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn,Statement st){
close(conn);
close(st);
}
public static void close(Connection conn,Statement st,ResultSet rt){
close(conn);
close(st);
close(rt);
}
}
第四节:使用Druid连接池
druid连接池的原理
数据库连接池在初始化的时候会创建initialSize个连接,当有数据库操作时,
会从池中取出一个连接。如果当前池中正在使用的连接数等于maxActive,
则会等待一段时间,等待其他操作释放掉某一个连接,如果这个等待时间超
过了maxWait,则会报错;如果当前正在使用的连接数没有达到maxActive,
则判断当前是否空闲连接,如果有则直接使用空闲连接,如果没有则新建立
一个连接。在连接使用完毕后,不是将其物理连接关闭,而是将其放入池中
等待其他操作复用。 同时连接池内部有机制判断,如果当前的总的连接数少
于miniIdle,则会建立新的空闲连接,以保证连接数得到miniIdle。如果当前
连接池中某个连接在空闲了timeBetweenEvictionRunsMillis时间后仍然没有
使用,则被物理性的关闭掉。有些数据库连接的时候有超时限制(mysql连接
在8小时后断开),或者由于网络中断等原因,连接池的连接会出现失效的
情况,这时候设置一个testWhileIdle参数为true,可以保证连接池内部定时
检测连接的可用性,不可用的连接会被抛弃或者重建,最大情况的保证从
连接池中得到的Connection对象是可用的。当然,为了保证绝对的可用性,
你也可以使用testOnBorrow为true(即在获取Connection对象时检测其可用性),
不过这样会影响性能。
数据库连接池的原理思想:
连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户
需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接
对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求
访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的
参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大
空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况。
Druid的优点:
Druid 是目前比较流行的高性能的,分布式列存储的OLAP框架(具体来说是MOLAP)。
它有如下几个特点:
一. 亚秒级查询
druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式。
二.实时数据注入
druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性
三.可扩展的PB级存储
druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其有效性
四.多环境部署
druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark, kafka,storm和samza等
五.丰富的社区
druid拥有丰富的社区,供大家学习
4.1使用步骤
4.1.1导入jar包
4.1.2编写工具类
/**
* 阿里的数据库连接池
* 性能最好的
* Druid
* */
public class DbUtils {
//声明连接池对象
private static DruidDataSource ds;
static{
//实例化配置对象
Properties properties=new Properties();
try {//加载配置文件
properties.load(DbUtils.class.getClassLoader().getResourceAsStream("database.properties"));
ds = (DruidDataSource)DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
数据库配置文件:
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/account
username=root
password=1234
#<!-- 初始化连接 -->
initialSize=10
#最大连接数量
maxActive=50
#<!-- 最小空闲连接 -->
minIdle=5
#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=5000
4.1.3测试
import java.sql.Connection;
public class Test {
public static void main(String[] args) throws Exception {
for(int i=0;i<100;i++) {
Connection connection=DbUtils.getConnection();
if(connection!=null) {
System.out.println("连接成功"+i+"..."+connection.hashCode()+connection.toString());
}
connection.close();
}
}
}
第五节:Apache组织DBUtils工具类的使用
Commons DbUtils是Apache组织提供的一个对JDBC进行简单封装的开源工具类库,使用它能够简化JDBC应用程序的开发,同时也不会影响程序的性能。
5.1 DBUtils简介
DBUtils是java编程中的数据库操作实用工具,小巧简单实用,
1.对于数据表的读操作,可以把结果转换成List,Array,Set等java集合,便于程序员操作。
2.对于数据表的写操作,也变得很简单(只需写sql语句)。
DBUtils包括主要类
DbUtils类:启动类
ResultSetHandler接口:转换类型接口
--ArrayHandler类:实现类,把记录转化成数组
--ArrayListHandler类:把记录转化成数组,并放入集合中
--ColumnListHandler类:取某一列的数据。封装到List中。
–ScalarHandler类:适合获取一行一列数据。
–BeanHandler类:实现类,把记录转成对象。
–BeanListHandler类:实现类,把记录转化成List,使记录为JavaBean类型的对象
QueryRunner类:执行SQL语句的类
5.2 DBUtils工具类+Druid连接池封装
5.2.1 项目准备
-
创建项目
-
导入jar包 工具类 配置文件
commons-dbutils-1.6.jar
druid-1.1.5.jar数据库驱动包
DruidUtils.java工具类
database.properties配置文件
5.2.2 实现代码
public class ResultHanlder {
@Test
public void testArrayHander() throws SQLException {
// ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
//1234为参数为sql语句的参数,可以多写
Object[] query = runner.query("select * from school where empno = ?", new ArrayHandler(), 1234);
for (Object object : query) {
System.out.println(object);
}
}
@Test
public void testArrayListHander() throws SQLException {
// ArrayHandler:适合取1条记录。把该条记录的每列值封装到一个数组中Object[]
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
List<Object[]> query = runner.query("select * from emp ", new ArrayListHandler());
for (Object[] objects : query) {
for (Object object : objects) {
System.out.println(object);
}
}
}
@Test
public void testColumnListHander() throws SQLException {
// ColumnListHandler:取某一列的数据。封装到List中。
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
List<Object> query = runner.query("select * from emp ", new ColumnListHandler<Object>(2));
for (Object objects : query) {
System.out.println(objects);
}
}
@Test
public void testScalarHandler() throws SQLException {
// ScalarHandler:适合取单行单列数据
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
Object query = runner.query("select count(*) from emp ", new ScalarHandler());
System.out.println(query);
}
@Test
public void testBeanHandler() throws SQLException {
// BeanHandler:适合取单行单列数据
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
Employee query = runner.query("select * from emp where empno=1234 ", new BeanHandler<Employee>(Employee.class));
System.out.println(query.toString());
}
@Test
public void testBeanListHandler() throws SQLException {
// BeanHandler:适合取多行多列数据
QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
List<Employee> query2 = runner.query("select * from emp", new BeanListHandler<Employee>(Employee.class));
for (Employee employee : query2) {
System.out.println(employee);
}
}
}
两个问题
1,为什么要使用数据库连接池
数据库连接池是初始化一定数量的数据库连接放在池中,因为初始化连接池的时候就创建
了若干连接,所以响应系统的时候更快。数据库连接池可以实现连接的复用,避免频繁的
建立连接和关闭连接,降低开销。统一管理资源,限制某一应用的最大连 接数量,避免
某一个应用独占所有数据库资源。
2.数据库连接池实现原理
连接池的工作原理主要由三部分组成,分别为连接池的建立、连接池中连接的使用管理、
连接池的关闭。
第一、连接池的建立。 一般在系统初始化时,连接池会根据系统配置建立,并在池中创建
了几个连接对象,以便使用 时能从连接池中获取。连接池中的连接不能随意创建和关闭,
这样避免了连接随意建立和关闭造成的系统开销。Java中提供了 很多容器类可以方便的
构建连接池,例如Vector、Stack等。
第二、连接池的管理。连接池管理策略是连接池机制的核心,连接池内连接的分配和释放
对系统的性能有很大的影响。
其管理策略是:
当客户请求数据库连接时,首先查看连接池中是否有空闲连接,如果存在
空闲连接,则将连接分配给客户使用;
如果没有空闲连接,则查看当前所开的连接数是否已经达到最大连接数,如果没达到
就重新创建一个连接给请求的客户;如果达到就按设定的最大等待时间进行等待,如果
超出最大等待时间,则抛出异常给客户。
当客户释放数据库连接时,先判断该连接的引用次数是否超过了规定值,如果超过
就从连接池中删除该连接,否则保留为其他客户服务。
该策略保证了数据库连接的有效复用,避免频繁的建立、释放连接所带来的系统资源
开销。
第三、连接池的关闭。当应用程序退出时,关闭连接池中所有的连接,释放连接池相关
的资源,该过程正好与创建相反