Java数据库连接池c3p0、dbcp和dbutils工具类的使用详解

一、数据库连接池的简单介绍

1.数据库连接池简介

在系统初始化时,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新连接,而是从连接池中取出一个已建立的空闲连接对象。

使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。

而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况。
数据库连接池工作原理



2.数据库连接池原理简介

数据库连接池的基本原理就是为数据库建立一个缓冲池。在缓冲池中先创建指定数量的数据库连接,当有连接请求时就从缓冲池中取出 处于空闲状态的连接,并将此连接标记为忙碌。知道该请求进程结束后,它所使用的连接才会重新回到空闲状态,并等待下一次请求调用。

所以综上所述,数据库连接池的主要作用就是负责分配、管理和释放数据库连接,它允许程序重复使用同一个现有的数据库连接,大大缩短了运行时间,提高了执行效率。



3.使用数据库连接池的必要性

当我们在进行基于数据库的web程序开发时,我们可以现在主程序中通过JDBC的DriverManager建立数据库连接,然后将要对数据库进行操作的sql语句封装到Statement中,最后在返回结果集后断开数据库连接。这种较为传统的开发模式很容易造成安全隐患。

因为每一个数据库连接使用完后都需要断开连接,但如果程序出现异常导致 连接未能及时关闭,这样就可能导致内存泄漏;另外使用传统JDBC模式开发不能控制需要创建的连接数,系统一般会将资源大量分出给连接以防止资源不够用,但是如果连接数超出一定数量也会有极大可能造成内存泄漏问题

普通的JDBC数据库连接使用DriverManager来获取,每次向数据库简历连接的时候都要将Connection加载到内存中,再根据JDBC代码(或配置文件)中用户名和密码进行验证。由此可见,如果同一个数据库在同一时间数十人甚至上百人请求连接势必会占用大量系统资源,严重会导致服务器崩溃




二、c3p0数据库连接池的使用

1.定义

c3p0是一个开源的JDBC连接池,它实现了数据源与JDNDI绑定,支持JDBC3规范和实现了JDBC2的扩展 说明的Connection和Statement池的DataSource对象。

即将用于连接数据库的连接整合在一起行程一个随取随用的数据库连接池(Connection pool)



2.使用

- 导入jar包:c3p0的jar包和musq驱动jar包


- 配置配置文件(两种方式取一,放在src路径下)

c3p0-config.xml

<c3p0-config>   
 <default-config>   
 <!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 -->   
 <property name="acquireIncrement">3</property>   

 <!--定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 -->   
 <property name="acquireRetryAttempts">30</property>   
    
 <!--两次连接中间隔时间,单位毫秒。Default: 1000 -->   
 <property name="acquireRetryDelay">1000</property>   
    
 <!--连接关闭时默认将所有未提交的操作回滚。Default: false -->   
 <property name="autoCommitOnClose">false</property>   
    
 <!--c3p0将建一张名为Test的空表,并使用其自带的查询语句进行测试。如果定义了这个参数那么   
 属性preferredTestQuery将被忽略。你不能在这张Test表上进行任何操作,它将只供c3p0测试   
 使用。Default: null-->   
 <property name="automaticTestTable">Test</property>   
    
 <!--获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效   
 保留,并在下次调用getConnection()的时候继续尝试获取连接。如果设为true,那么在尝试   
 获取连接失败后该数据源将申明已断开并永久关闭。Default: false-->   
 <property name="breakAfterAcquireFailure">false</property>   
    
 <!--当连接池用完时客户端调用getConnection()后等待获取新连接的时间,超时后将抛出   
 SQLException,如设为0则无限期等待。单位毫秒。Default: 0 -->   
 <property name="checkoutTimeout">100</property>   
    
 <!--通过实现ConnectionTester或QueryConnectionTester的类来测试连接。类名需制定全路径。   
 Default: com.mchange.v2.c3p0.impl.DefaultConnectionTester-->   
 <property name="connectionTesterClassName"></property>   
    
 <!--指定c3p0 libraries的路径,如果(通常都是这样)在本地即可获得那么无需设置,默认null即可   
 Default: null-->   
 <property name="factoryClassLocation">null</property>   
    
 <!--强烈不建议使用该方法,将这个设置为true可能会导致一些微妙而奇怪的bug-->   
 <property name="forceIgnoreUnresolvedTransactions">false</property>   
    
 <!--每60秒检查所有连接池中的空闲连接。Default: 0 -->   
 <property name="idleConnectionTestPeriod">60</property>   
    
 <!--初始化时获取三个连接,取值应在minPoolSize与maxPoolSize之间。Default: 3 -->   
 <property name="initialPoolSize">3</property>   
    
 <!--最大空闲时间,60秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->   
 <property name="maxIdleTime">60</property>   
    
 <!--连接池中保留的最大连接数。Default: 15 -->   
 <property name="maxPoolSize">15</property>   
    
 <!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements   
 属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。   
 如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default: 0-->   
 <property name="maxStatements">100</property>   
    
 <!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->   
 <property name="maxStatementsPerConnection"></property>   
    
 <!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能   
 通过多线程实现多个操作同时被执行。Default: 3-->   
 <property name="numHelperThreads">3</property>   
    
 <!--当用户调用getConnection()时使root用户成为去获取连接的用户。主要用于连接池连接非c3p0   
 的数据源时。Default: null-->   
 <property name="overrideDefaultUser">root</property>   
    
 <!--与overrideDefaultUser参数对应使用的一个参数。Default: null-->   
 <property name="overrideDefaultPassword">password</property>   
    
 <!--密码。Default: null-->   
 <property name="password"></property>   
    
 <!--定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意:   
 测试的表必须在初始数据源的时候就存在。Default: null-->   
 <property name="preferredTestQuery">select id from test where id=1</property>   
    
 <!--用户修改系统配置参数执行前最多等待300秒。Default: 300 -->   
 <property name="propertyCycle">300</property>   
    
 <!--因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的   
 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable   
 等方法来提升连接测试的性能。Default: false -->   
 <property name="testConnectionOnCheckout">false</property>   
    
 <!--如果设为true那么在取得连接的同时将校验连接的有效性。Default: false -->   
 <property name="testConnectionOnCheckin">true</property>   
    
 <!--用户名。Default: null-->   
 <property name="user">root</property>   
    
 <!--早期的c3p0版本对JDBC接口采用动态反射代理。在早期版本用途广泛的情况下这个参数   
 允许用户恢复到动态反射代理以解决不稳定的故障。最新的非反射代理更快并且已经开始   
 广泛的被使用,所以这个参数未必有用。现在原先的动态反射与新的非反射代理同时受到   
 支持,但今后可能的版本可能不支持动态反射代理。Default: false-->   
 <property name="usesTraditionalReflectiveProxies">false</property> 
 </default-config>      
</c3p0-config>

c3p0.properties

c3p0.driverClass = com.mysql.jdbc.Driver
c3p0.jdbcUrl = jdbc:mysql://localhost:3306/webexam
c3p0.user = root
c3p0.password =
c3p0.maxPoolSize = 20
c3p0.minPoolSize = 3
c3p0.maxStatements = 30
c3p0.maxIdleTime = 150

注:c3p0读取配置优先级如下(优先级权重值从上到下)

  • Configuration values programmatically set. 【以编程方式设置配置值。】
  • System property setting of configuration value. 【配置值的系统属性设置。】
  • Configuration values taken from the default configuration of a c3p0-config.xml file. 【配置值取自c3p0-config.xml文件的默认配置。】
  • Configuration values specified in a c3p0.properties file 【在c3p0.properties文件中指定的配置值】
  • c3p0’s hard-coded default values. 【c3p0的硬编码默认值。】
    由此可见,配置文件c3p0-config.xml是比c3p0.properties配置文件优先级高的,也是c3p0官方推荐的一种方式。

-c3p0工具类

public class C3P0Util {
	private static ComboPooledDataSource dataSource = new ComboPooledDataSource("poolConnection");
	

	public static ComboPooledDataSource getDataSource(){
		return dataSource;
	}
	
	//从连接池中获取连接
	public static Connection getConnection(){
		try {
			return (Connection) dataSource.getConnection();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return null;
	}

	//将连接归还连接池
	public static void release(Connection conn,Statement stmt,ResultSet rs){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs=null;
		}
		
		if(stmt!=null){
			try {
				stmt.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			stmt=null;
		}
		
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
	
	
	public static void release(Connection conn,PreparedStatement ps,ResultSet rs){
		if(rs!=null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			rs=null;
		}
		
		if(ps!=null){
			try {
				ps.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			ps=null;
		}
		
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
	
	public static void release(Connection conn){
		if(conn!=null){
			try {
				conn.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			conn=null;
		}
	}
}



三、dbcp数据库连接池的使用

1.定义

DBCP(database connection pool)是Apache软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool



2.使用

- 导入jar包

commons-dbutils.jar
commons-pool.jar

- 配置文件

dbcp.properties

#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/student
username=root
password=666666
initialSize=10
maxActive=50
maxIdle=20
minIdle=5
maxWait=60000
connectionProperties=useUnicode=true;characterEncoding=utf8
defaultAutoCommit=true
defaultReadOnly=
defaultTransactionIsolation=REPEATABLE_READ

DBCPUtils工具类

public class DBCPUtils {
    static DataSource ds = null;
    static {
        try {
            // 得到配置文件
            Properties prop = new Properties();
            prop.load(DBCPUtils.class.getClassLoader().getResourceAsStream(
                    "dbcpconfig.properties"));
            // 根据配置文件创建数据库连接池(数据源)
            ds = BasicDataSourceFactory.createDataSource(prop);
        } catch (Exception e) {
            throw new ExceptionInInitializerError("DBCP初始化异常,请检查配置文件!!!");
        }
    }

    /**
     * 得到数据库连接对象
     * 
     * @return
     */
    public static Connection getConnection() {
        try {
            return ds.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException("服务器忙。。。");
        }
    }

    /**
     * 关闭所有资源连接
     * 
     * @param conn
     * @param ps
     * @param rs
     */
    public static void releaseAll(Connection conn, Statement ps, ResultSet rs) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            conn = null;
        }

        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            ps = null;
        }

        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            rs = null;
        }
    }
}



四、dbutils工具类的简单应用

1.DBUtil核心功能

QueryRunner中提供对sql语句操作的API

ResultSetHandler接口,用于定义select操作后,怎样封装结果集

DBUtils类是一个工具类,定义了关闭资源与事务处理的方法



2.QueryRunner核心类

QueryRunner(DataSource ds) 传入参数为连接池

update(String sql,Objec…params) 执行insert update delete操作

query(String sql ,ResultSetHandler rsh,Object…params) 执行 select操作

3.ResultSetHandler结果集处理类

Java数据库连接池c3p0、dbcp和dbutils工具类的使用详解



## **4.实例代码** ### -关于增删改查
/*
 *  使用QueryRunner类,实现对数据表的
 *  insert delete update
 *  调用QueryRunner类的方法 update (Connection con,String sql,Object...param)
 *  Object...param 可变参数,Object类型,SQL语句会出现?占位符
 *  数据库连接对象,自定义的工具类传递
*/
public class QueryRunnerDemo {
	private static Connection con = JDBCUtilConfig.getConnection();
	
	public static void main(String[] args)throws SQLException {
		//insert();
		update();
		//delete();
	}
	/*
	 *  定义方法,使用QueryRunner类的方法delete将数据表的数据删除
	 */
	public static void delete()throws SQLException{
		//创建QueryRunner类对象
		QueryRunner qr = new QueryRunner();	
		//写删除的SQL语句
		String sql = "DELETE FROM sort WHERE sid=?";
		//调用QueryRunner方法update
		int row = qr.update(con, sql, 8);
		System.out.println(row);
		/*
		 *  判断insert,update,delete执行是否成功
		 *  对返回值row判断
		 *  if(row>0) 执行成功
		 */
		DbUtils.closeQuietly(con);
	}
	
	/*
	 *  定义方法,使用QueryRunner类的方法update将数据表的数据修改
	 */
	public static void update()throws SQLException{
		//创建QueryRunner类对象
		QueryRunner qr = new QueryRunner();	
		//写修改数据的SQL语句
		String sql = "UPDATE sort SET sname=?,sprice=?,sdesc=? WHERE sid=?";
		//定义Object数组,存储?中的参数
		Object[] params = {"花卉",100.88,"情人节玫瑰花",4};
		//调用QueryRunner方法update
		int row = qr.update(con, sql, params);
		System.out.println(row);
		DbUtils.closeQuietly(con);
	}
	
	
	/*
	 * 定义方法,使用QueryRunner类的方法update向数据表中,添加数据
	 */
	public static void insert()throws SQLException{
		//创建QueryRunner类对象
		QueryRunner qr = new QueryRunner();
		String sql = "INSERT INTO sort (sname,sprice,sdesc)VALUES(?,?,?)";
		//将三个?占位符的实际参数,写在数组中
		Object[] params = {"体育用品",289.32,"购买体育用品"};
		//调用QueryRunner类的方法update执行SQL语句
		int row = qr.update(con, sql, params);
		System.out.println(row);
		DbUtils.closeQuietly(con);
	}
}

-关于查询

category.java

// JavaBean
public class Category {
	private String cid;
	private String cname;
	
	public String getCid() {
		return cid;
	}
	public void setCid(String cid) {
		this.cid = cid;
	}
	
	public String getCname() {
		return cname;
	}
	public void setCname(String cname) {
		this.cname = cname;
	}
	@Override
	public String toString() {
		return "Category [cid=" + cid + ", cname=" + cname + "]";
	}
	public Category() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Category(String cid, String cname) {
		super();
		this.cid = cid;
		this.cname = cname;
	}
}

C3P0Utils.java
/**
 * 在C3P0连接池中 遵循了javax.sql.DataSource接口的实现类:
 * 		ComboPooledDataSource
*/
import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import javax.sql.DataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class C3P0Utils {

	private static ComboPooledDataSource ds = new ComboPooledDataSource();	
	// 自动加载C3P0-config.xml文件

	public static DataSource getDataSource(){
		return ds;
	}
	
	public static Connection getConnection() throws SQLException{
		// 获取连接,从C3P0连接池获取
		return ds.getConnection();
	}
	
	// 关闭所有资源的统一代码
	public static void closeAll(Connection conn, Statement st, ResultSet rs){
		//负责关闭
		if(conn != null){
			try {
				conn.close();		// 使用代理,放回到连接池中
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(st != null){
			try {
				st.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		if(rs != null){
			try {
				rs.close();
			} catch (SQLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}	
}

DBUtils.java
package cn.simon.jdbc.demo03_DBUtils的使用;

import java.sql.SQLException;
import java.util.List;
import java.util.Map;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ColumnListHandler;
import org.apache.commons.dbutils.handlers.MapHandler;
import org.apache.commons.dbutils.handlers.MapListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;

import cn.simon.jdbc.domain.Category;

/**
 *  DBUtils执行数据库查询操作
 *  	1.QueryRunner(DataSource)
 *  	2.query(String sql, ResultSetHandler<T> rsh, Object... params); // 主要执行查询
*/

public class DBUtilsDemo {
	public static void main(String[] args) throws SQLException {
		// TODO Auto-generated method stub

//		queryDemo01();
//		queryDemo02();
//		queryDemo03();
//		queryDemo04();
//		queryDemo05();
		queryDemo06();
//		queryDemo07();
//		queryDemo08();
	}
	
	// ArrayHandler处理类的使用
	public static void queryDemo01() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		Object[] objs = qr.query("select * from category where cid = ?", new ArrayHandler(), 1);
		for(Object o: objs){		// object[]中保存了object对象
			System.out.println(o);
		}
	}
	
	// ArrayListHandler
	public static void queryDemo02() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		List<Object[]> objs = qr.query("select * from category ", new ArrayListHandler());
		for (Object[] objects : objs) {
			System.out.println(objects[0]+"\t"+objects[1]);
		}
	}
	
	// BeanHandler处理类的使用
	public static void queryDemo03() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select * from category";
		Category c = qr.query(sql, new BeanHandler<Category>(Category.class));
		System.out.println(c);
	}
	
	// BeanListHandler
	public static void queryDemo04() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select * from category";
		List<Category> c = qr.query(sql, new BeanListHandler<Category>(Category.class));
		for (Category category : c) {
			System.out.println(category);
		}
	}
	
	// ColumnListHandler处理类的使用
	public static void queryDemo05() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select * from category";
		List<Object> c = qr.query(sql, new ColumnListHandler<Object>("cname"));
		System.out.println(c);
	}
	
	// MapHandler处理类的使用
	public static void queryDemo06() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select * from category";
		Map<String, Object> map = qr.query(sql, new MapHandler());
		// 3.
		System.out.println(map);
	}
	
	// MapListHandler处理类的使用
	public static void queryDemo07() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select * from category";
		List<Map<String, Object>> maps = qr.query(sql, new MapListHandler());
		// 3.List
		System.out.println(maps);
	}
	
	// MapListHandler处理类的使用
	public static void queryDemo08() throws SQLException{
		// 1.创建QueryRunner对象
		QueryRunner qr = new QueryRunner(C3P0Utils.getDataSource());
		// 2.执行查询
		String sql = "select count(*) from category";
		Long count = qr.query(sql, new ScalarHandler<Long>());
		// 3.List
		System.out.println(count);
	}
}



五、文章引用/参考

https://www.mchange.com/projects/c3p0/index.html
https://www.cnblogs.com/SUN99bk/p/12181493.html
https://www.cnblogs.com/ygj0930/p/6405861.html
https://www.cnblogs.com/xww0826/p/10359479.html#1%E3%80%81DBCP%E8%BF%9E%E6%8E%A5%E6%B1%A0
https://www.cnblogs.com/lztkdr/p/DBCPUtils.html
http://commons.apache.org/proper/commons-dbcp/configuration.html
https://www.jb51.net/article/101072.htm
https://www.cnblogs.com/sunseine/p/5947448.html
https://blog.csdn.net/simonforfuture/article/details/90480147
https://blog.csdn.net/a911711054/article/details/77719685?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-3.nonecase
https://blog.csdn.net/qq_25106373/article/details/80955184



欢迎关注微信公众号【Java编程扉页】
更多精彩,且让我慢慢与你分享
Java数据库连接池c3p0、dbcp和dbutils工具类的使用详解

上一篇:连接池原理介绍+常用连接池介绍


下一篇:pep9