JDBC 连接池 & DBUtils
使用 DBCP C3P0 连接池完成基本数据库的操作
使用DBUtils 完成 CRRD 的操作
目标:
学会 DBCP 配置文件的编写,以及数据源 DataSource 创建。
学会 C3P0 配置文件的编写,以及数据源 DataSource 创建。
能够使用 JDBC 简化工具包 DBUtis 完成单表的增删改查操作。
会描述 DBUtils 底层原理。
使用连接池重写工具类:
实际开发中,“获得连接”和“释放资源”是两个特别消耗资源的过程,为了解决这样的性能问题,通常我们采取池连接技术,来共享 Connection 。
连接池概念:
用池来管理 Connnection 这样可以重复使用 Connection 。有了池,所以我们就不需要自己创建 Connection,而是通过池来获取 Connection对象,当使用完后调用 Connection 的close() 方法也不会真的关闭Connection,而是把 Connection ”归还“给连接池,池就可以再利用 Connectionn 对象了。
规范:
Java 为数据库连接池提供了公共的接口:Javax.sql.DataSource,各个厂商需要让自己的连接池实现这个接口,这样应用程序可以方便的切换不同厂商的连接池。
常见的连接池:
DBCP、C3P0
自定义连接池:
1、创建连接池实现(数据源),并实现接口(Javax.sql.DataSource)。因为我们只使用该接口中的 getConnection() 方法。
2、提供一个集合用于存放连接,因为移除/添加的操作太多,所以我们选择 LinkedList
3、初始化三个连接。
4、之后程序需要连接,调用实现类的 getConnection() 方法,本方法可从连接池(容器 List)获得连接。为了保障当前连接只能提供给一个线程使用,所以我们需要将连接先从连接池中移除。
5、当用户使用完连接时候,释放资源,不执行 close(), 而是将连接存放至连接池中。
案例实现:
数据库连接的工具类:
package com.ma.jdbc.utils;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ResourceBundle;
/**
* 工具类
* 提供获取连接及释放资源的方法
* @author lenovo
*
*/
public class JDBCUtils {
/**
* 获取连接方法
* @return
*/
public static Connection getConnection(){
Connection conn = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/user","root","123456");
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
/**
* 释放资源方法
* @param conn
* @param pstmt
* @param rs
*/
public static void release(Connection conn, PreparedStatement pstmt, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pstmt != null){
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
自己编写的连接池:
package com.ma.jdbc.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.LinkedList;
import java.util.logging.Logger;
import javax.sql.DataSource;
import com.ma.jdbc.utils.JDBCUtils;
public class MyDataSource implements DataSource{
//1、创建一个容器存储 Connection 对象
private static LinkedList<Connection> pool = new LinkedList<Connection>();
//2、创建5个连接放到池子中去
static{
for (int i = 0; i < 5; i++) {
Connection conn = JDBCUtils.getConnection();
pool.add(conn);
}
}
/**
* 获取连接的方法
*/
@Override
public Connection getConnection() throws SQLException {
Connection conn = null;
//3、使用前先判断
if(pool.size() == 0){
//4、池子里面没有我们再创建一些
for (int i = 0; i < 5; i++) {
conn = JDBCUtils.getConnection();
pool.add(conn);
}
}
//5、从池子里面获取连接对象
conn = pool.remove(0);
return conn;
}
/**
* 归还连接对象的方法
*/
public void backConnection(Connection conn){
pool.add(conn);
}
@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;
}
@Override
public Connection getConnection(String username, String password)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
}
使用数据库连接池完成数据的添加操作:
package com.ma.jdbc.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import com.ma.jdbc.DataSource.MyDataSource;
public class TestMyDataSource {
@Test
public void testAddUser(){
Connection conn = null;
PreparedStatement pstmt = null;
//1、创建自定义连接池对象
MyDataSource dataSourse = new MyDataSource();
try {
//2、从池子中获取连接对象
conn = dataSourse.getConnection();
//3、编写 sql 语句
String sql = "insert into t_user values(null, ?, ?, ?)";
//4、执行 sql
pstmt = conn.prepareStatement(sql);
//5、设置参数
pstmt.setString(1, "Young");
pstmt.setString(2, "555555");
pstmt.setString(3, "Tong");
//6、处理结果
int rows = pstmt.executeUpdate();
if(rows > 0){
System.out.println("添加成功 !");
}else{
System.out.println("添加失败 !");
}
} catch (Exception e) {
}finally{
dataSourse.backConnection(conn);
}
}
}
此时调用 close() 方法会销毁连接,实现调用 close() 方法将连接对象返回到数据库连接池中的功能。
利用装饰者模式完成功能的增强
装饰者固定格式:接口 A, 已知实现类 C,需要装饰者创建代理类 B。
1、创建类 B 并实现接口 A
2、提供类 B 的构造方法参数类型为 A,用于接收 A 接口的其他实现类(C)
3、给类 B 添加类型为 A 的成员变量,用于存放 A 接口的其他实现类
4、增强需要的方法
5、实现不需要的方法,方法体重调用成员变量存放的其他实现类对应的方法
package com.ma.jdbc.DataSource;
import java.sql.Array;
import java.sql.Blob;
import java.sql.CallableStatement;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.NClob;
import java.sql.PreparedStatement;
import java.sql.SQLClientInfoException;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.SQLXML;
import java.sql.Savepoint;
import java.sql.Statement;
import java.sql.Struct;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
//1、实现同一个接口 Connection
public class MyConnection implements Connection{
//3、定义变量
private Connection conn;
private LinkedList<Connection> pool;
//2、实现一个构造方法(使用了多态)
public MyConnection(Connection conn, LinkedList<Connection> pool){
this.conn = conn;
this.pool = pool;
}
@Override
public void close() throws SQLException {
pool.add(conn);
}
@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;
}
@Override
public Statement createStatement() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public String nativeSQL(String sql) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setAutoCommit(boolean autoCommit) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean getAutoCommit() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void commit() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void rollback() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isClosed() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public DatabaseMetaData getMetaData() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setReadOnly(boolean readOnly) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public boolean isReadOnly() throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void setCatalog(String catalog) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public String getCatalog() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setTransactionIsolation(int level) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getTransactionIsolation() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public SQLWarning getWarnings() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void clearWarnings() throws SQLException {
// TODO Auto-generated method stub
}
@Override
public Statement createStatement(int resultSetType, int resultSetConcurrency)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Map<String, Class<?>> getTypeMap() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setHoldability(int holdability) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getHoldability() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Savepoint setSavepoint() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Savepoint setSavepoint(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void rollback(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void releaseSavepoint(Savepoint savepoint) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public Statement createStatement(int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public CallableStatement prepareCall(String sql, int resultSetType,
int resultSetConcurrency, int resultSetHoldability)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, int[] columnIndexes)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public PreparedStatement prepareStatement(String sql, String[] columnNames)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Clob createClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Blob createBlob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public NClob createNClob() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public SQLXML createSQLXML() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean isValid(int timeout) throws SQLException {
// TODO Auto-generated method stub
return false;
}
@Override
public void setClientInfo(String name, String value)
throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public void setClientInfo(Properties properties)
throws SQLClientInfoException {
// TODO Auto-generated method stub
}
@Override
public String getClientInfo(String name) throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Properties getClientInfo() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Array createArrayOf(String typeName, Object[] elements)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public Struct createStruct(String typeName, Object[] attributes)
throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void setSchema(String schema) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public String getSchema() throws SQLException {
// TODO Auto-generated method stub
return null;
}
@Override
public void abort(Executor executor) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public void setNetworkTimeout(Executor executor, int milliseconds)
throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getNetworkTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
}
C3P0连接池:
C3P0 开源免费的连接池,目前使用它的开源项目有 Spring、Hibernate 等。使用第三方工具需要导入 jar 包 ,C3P0 使用时还需要添加配置文件 c3p0-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///user</property>
<property name="user">root</property>
<property name="password">123456</property>
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">20</property>
</default-config>
<named-config name="oracle">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql:///web_07</property>
<property name="user">root</property>
<property name="password">123</property>
</named-config>
</c3p0-config>
测试代码:
package com.ma.jdbc.test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import com.ma.jdbc.DataSource.MyDataSource;
import com.ma.jdbc.utils.JDBCUtils;
importcom.mchange.v2.c3p0.ComboPooledDataSource;
public class TestC3p0 {
@Test
public void testAddUser(){
Connection conn = null;
PreparedStatement pstmt = null;
//1、创建自定义连接池对象
ComboPooledDataSource dataSource = new ComboPooledDataSource();
//ComboPooledDataSource dataSource = new ComboPooledDataSource("name");//加载有名称的datasource
try {
//2、从池子中获取连接对象
conn = dataSource.getConnection();
//3、编写 sql 语句
String sql = "insert into t_user values(null, ?, ?, ?)";
//4、执行 sql
pstmt = conn.prepareStatement(sql);
//5、设置参数
pstmt.setString(1, "Toung");
pstmt.setString(2, "444444");
pstmt.setString(3, "LIN");
//6、处理结果
int rows = pstmt.executeUpdate();
if(rows > 0){
System.out.println("添加成功 !");
}else{
System.out.println("添加失败 !");
}
} catch (Exception e) {
}finally{
JDBCUtils.release(conn, pstmt, null);
}
}
}
运行结果:
DBCP 数据库连接池:
配置文件名称:*.propreties
配置文件位置:任意,建议 src 目录下面
在增删改查代码中有很多相同的代码,只有少量的地方不同,对于不同的地方我们采用传参的方式解决。
DBUtils 就是 JDBC 的简化开发工具包。
相关知识:
JavaBean 组件,JavaBean 就是一个类,在开发中常用于封装数据,具有以下特征:
1、需要实现接口:java.io.Serializable,通常偷懒省略了。
2、提供私有字段:private 类型 字段名。
3、提供 getter/setter 方法。
4、提供无参构造。
DBUtils 完成增删改查:
DbUtils 是 Java 编程中的数据库操作实用工具,小巧简单实用。
DBUtils 简化了 JDBC 的操作,可以少写代码。
DBUtils 的三个核心功能简介:
1、QueryRunner 中提供对sql 语句操作的 api。
2、ResultSetHandler 接口,用于定义 select 操作后,怎么封装结果集。
3、DbUtils 类,它就是一个工具类,定义了关闭资源与处理事务的方法。
QueryRunner 核心类:
1、QueryRunner(DataSource ds),提供数据源(连接池),DBUtils 底层自动维护连接 Connection 。
2、update(String sql, Object... params),执行更新数据。
3、query(String sql, ResultSetHandler<T> rst, Object... params),执行查询。
ResultSethandler 结果集处理类:
ArrayListHandler | 将结果集中的每一条记录都封装到一个 object[]数组中,将这些数组封装到 List 集合中。 |
BeanHandler | 将结果集中的每一条数据封装到指定的 JavaBean 中。 |
BeanListHandler | 将结果集中的每一条记录封装到指定 JavaBean 中,将这些 JavaBean 再封装到 List 集合中。 |
ColumnListHandler | 将结果集中指定列的字段值,封装到一个 List 集合中。 |
KeyedHandler | 将结果集中每一条记录封装到 Map<String, Object>,再将这个 Map 集合作为令一个Map 的 value ,另一个 Map 的 key 是指定的字段的值。 |
MapHandler | 将结果集中每一条记录封装到 Map<String, Object>集合中, key 就是字段名称, value 就是字段值。 |
MapListHandler | 将结果集中每一条记录封装到 Map<String, Object>集合中, key 就是字段名称, value 就是字段值,再将这些 Map 封装到 List 集合中。 |
ScalarHandler | 它适用于单数据,例如 select count(*) from 表 。 |
测试:
1、添加用户信息
工具类:
package com.ma.jdbc.utils;
import java.sql.Connection;
import java.sql.SQLException;
import javax.management.RuntimeErrorException;
import javax.sql.DataSource;
importcom.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Utils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
public static DataSource getDataSouce(){
return dataSource;
}
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
测试类:
package com.ma.jdbc.test;
import java.sql.SQLException;
importorg.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import com.ma.jdbc.utils.C3P0Utils;
import com.mchange.v2.c3p0.cfg.C3P0ConfigUtils;
public class TestDBUtils {
@Test
public void testAddUser(){
try {
//1、创建核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、编写 sql 语句
String sql = "insert into t_user values(null, ?, ?, ?)";
//3、为占位符设置值
Object[] params = {"Long", "333333", "Tong"};
//4、执行添加操作
int rows = qr.update(sql, params);
if(rows > 0){
System.out.println("插入成功 !");
}else{
System.out.println("插入失败 !");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
2、更新用户信息
@Test
public void testUpdateUser(){
try {
//1、创建核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、编写 sql 语句
String sql = "update t_user set password = ? where uid = ?";
//3、为占位符设置值
Object[] params = {"666666", 14};
//4、执行添加操作
int rows = qr.update(sql, params);
if(rows > 0){
System.out.println("更新成功 !");
}else{
System.out.println("更新失败 !");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
3、删除用户信息
@Test
public void testDeleteUser(){
try {
//1、创建核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、编写 sql 语句
String sql = "delete from t_user where uid = ?";
//3、为占位符设置值
Object[] params = {14};
//4、执行添加操作
int rows = qr.update(sql, params);
if(rows > 0){
System.out.println("删除成功 !");
}else{
System.out.println("删除失败 !");
}
} catch (SQLException e) {
e.printStackTrace();
}
}
结果:
查询所有用户信息
package com.ma.jdbc.test;
import java.sql.SQLException;
import java.util.List;
importorg.apache.commons.dbutils.QueryRunner;
importorg.apache.commons.dbutils.handlers.BeanListHandler;
import org.junit.Test;
import com.ma.domain.User;
import com.ma.jdbc.utils.C3P0Utils;
public class TestDBUtils1 {
@Test
public void queryAll(){
try {//1、获取核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、创建 sql 语句
String sql = "select * from t_user";
//3、执行查询操作
List<User> users = qr.query(sql, new BeanListHandler<User>(User.class));
//4、遍历结果
for(User user : users){
System.out.println("uid =" + user.getUid() + "username =" + user.getUsername() + "password =" + user.getPassword() + "address =" + user.getAddress());
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
结果:
打印结果:
根据 uid 查询用户信息
/**
* 根据 uid 查询信息
*/
@Test
public void queryUserById(){
try {//1、获取核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、创建 sql 语句
String sql = "select * from t_user where uid = ?";
//3、设置参数
Object[] param = {13};
//4、执行查询
User user = qr.query(sql, new BeanHandler<User>(User.class), param);
System.out.println("uid =" + user.getUid()+" " + "username =" + user.getUsername()+" " + "password =" + user.getPassword()+" " + "address =" + user.getAddress());
} catch (SQLException e) {
e.printStackTrace();
}
}
结果:
查询用户总数
/**
* 查询用户总数
*/
@Test
public void queryCount(){
try {//1、获取核心类
QueryRunner qr = new QueryRunner(C3P0Utils.getDataSouce());
//2、创建 sql 语句
String sql = "select count(*) from t_user";
//3、设置参数
Object[] param = {13};
//4、执行查询
Long count = (Long) qr.query(sql, new ScalarHandler());
System.out.println(count);
} catch (SQLException e) {
e.printStackTrace();
}
}
打印结果: