JDBC连接池目的:主要解决数据库一直申请链接,释放连接所产生的资源浪费,提升代码性能。
获得连接和释放资源非常耗系统资源,通常使用连接池技术,共享Connection。通过池来管理Connection,重复利用。归还连接时调用close(),但是不是销毁连接,而是归还连接,将连接放在连接池中。
java为数据库连接提供的公共结构:java.sql.DataSoure。
常见的连接池:DBCP(占市场百分之十几)、C3P0(占80%以上)。
自定义简单连接池:
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.LinkedList;
import javax.sql.DataSource;
//自定义的工具类,有和数据库建立连接的方法。
import cn.jdbc.utils.CopyOfCopyOfJDBCUtils;
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 = CopyOfCopyOfJDBCUtils.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 = CopyOfCopyOfJDBCUtils.getConnection();
pool.add(conn);
}
}
// 5.从池子中获取一个连接对象Connection
conn=pool.remove(0);
// 移除的也是conn对象,将移除的对象返回,相当于拿到Connection对象。
return conn;
}
// 归还连接对象到连接池中去
public void backConnection(Connection conn){
pool.add(conn);
}
注意:在创建类接口时会提示出现错误,也就是在写语句MyDataSource implements DataSource时,会报错。
此时选择添加没有实现的 方法选项,也就是Add unimplement methods。
自定义测试类:
import java.sql.Connection;
import java.sql.PreparedStatement;
import org.junit.Test;
import cn.jdbc.DataSource.MyDataSource;
public class TestMyDataSource {
//测试添加用户
@Test
public void testAddUser(){
Connection conn =null;
PreparedStatement pr=null;
// 1.创建自定义连接池对象
MyDataSource dataSource=new MyDataSource();
try {
// 2.从池子中获取连接
conn =dataSource.getConnection();
String sql="insert into tbl_user values(null,?,?)";
pr=conn.prepareStatement(sql);
pr.setString(1, "燕小六");
pr.setString(2, "燕小六");
int rows=pr.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (Exception e) {
throw new RuntimeException();
}finally{
dataSource.backConnection(conn);
}
}
}
代码运行结果为:添加成功。
查看数据库用户燕小六的信息也出现在数据中。
修改重写close()方法。
需求:仍然调用close()方法,实现将资源归还到连接池,避免资源被真正的释放。
4种方法:
1.继承,子类继承父类,将父类的方法进行复写,从而进行增强。
适用前提:必须有父类,且存在继承关系。
2.【装饰者设计模式】,此设计模式专门用于增强方法。
使用前提:必须有接口,且需要将接口的所有方法都实现(缺点)
3.动态代理:在运行时动态的创建代理类,完成增强操作,与装饰者类似适用前提,必须有接口。
难点:需要反射技术。
4.字节码增强,运行时创建目标子类,从而增强。
常见第三方框架:cglib、javassist等。
这次使用第二种方法,装饰者设计模式。
自定义MyConnection,这是其中一部分代码,需要对close() 和prepareStatement(String sql)进行重写。
//1.实现同一个接口
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 clearWarnings() throws SQLException {
// TODO Auto-generated method stub
}
//4.书写需要增强的方法
@Override
public void close() throws SQLException {
pool.add(conn);
}
//此方法必须覆盖,否则会出现空指针异常
@Override
public PreparedStatement prepareStatement(String sql) throws SQLException {
// TODO Auto-generated method stub
return conn.prepareStatement(sql);
}
之后测试类
//使用改造过得方法,测试添加用户
@Test
public void testAddUser1(){
Connection conn =null;
PreparedStatement pr=null;
// 1.创建自定义连接池对象
DataSource dataSource=new CopyOfMyDataSource();
try {
// 2.从池子中获取连接
conn =dataSource.getConnection();
String sql="insert into tbl_user values(null,?,?)";
// 必须在自定义的Connection类中重写prepareStatement(sql)方法
pr=conn.prepareStatement(sql);
pr.setString(1, "燕小七");
pr.setString(2, "燕小七");
int rows=pr.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (Exception e) {
throw new RuntimeException();
}finally{
CopyOfCopyOfJDBCUtils.release(conn, pr, null);
}
}
自定义的连接池,只用修改部分代码,调用MyConnection的方法即可`
//2.创建5个链接放到容器中
static {
for (int i = 0; i < 5; i++) {
Connection conn = CopyOfCopyOfJDBCUtils.getConnection();
// 放入池子中的connection对象已经经过改造了
MyConnection myconn=new MyConnection(conn,pool);
pool.add(myconn);
}
}
//重写获取连接的方法
@Override
public Connection getConnection() throws SQLException {
Connection conn=null;
// 3.使用前先判断
if(pool.size()==0){
// 4.如果池子里面没有,则在创建一些
for (int i = 0; i < 5; i++) {
conn = CopyOfCopyOfJDBCUtils.getConnection();
// 放入池子中的connection对象已经经过改造了
MyConnection myconn=new MyConnection(conn,pool);
pool.add(myconn);
}
}
// 5.从池子中获取一个连接对象Connection
conn=pool.remove(0);
// 移除的也是conn对象,将移除的对象返回,相当于拿到Connection对象。
return conn;
C3P0连接池:开源免费的连接池,目前使用的开源项目有:Spring、Hibernate等等,C3P0属于第三方工具,在使用是需要导入jar包。使用时还需要加载c3p0-config.xml配置文件(两种方法,一种加,一种不用加),一般都加。也支持.properties文件。
首先导包,方法步骤和导入jdbc包一样
其次在java工程的src文件中创建或者复制一份c3p0-config.xml配置文件。
测试c3p0连接池:只需要改动DataSource dataSource=new ComboPooledDataSource();即可。
import java.sql.Connection;
import java.sql.PreparedStatement;
import javax.sql.DataSource;
import org.junit.Test;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import cn.jdbc.utils.CopyOfCopyOfJDBCUtils;
public class TestC3P0 {
//使用C3P0连接池,测试添加用户
@Test
public void testAddUser1(){
Connection conn =null;
PreparedStatement pr=null;
// 1.创建自定义连接池对象
DataSource dataSource=new ComboPooledDataSource();
// DataSource dataSource=new ComboPooledDataSource(oracle);
// 括号里面不加参数,定义默认的连接池对象,加参数则定义名字为参数的连接池对象
try {
// 2.从池子中获取连接
conn =dataSource.getConnection();
String sql="insert into category values(?,?)";
// 必须在自定义的Connection类中重写prepareStatement(sql)方法
pr=conn.prepareStatement(sql);
pr.setString(1, "c005");
pr.setString(2, "燕小五");
int rows=pr.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (Exception e) {
throw new RuntimeException();
}finally{
CopyOfCopyOfJDBCUtils.release(conn, pr, null);
}
}
}
C3P0工具类的抽取:
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class C3P0Utils {
// c3p0工具类
private static ComboPooledDataSource dataSource=new ComboPooledDataSource();
public static DataSource getDataSource(){
return dataSource;
}
public static Connection getConnection(){
try {
return dataSource.getConnection();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
再测试:
@Test
public void testAddUser2(){
Connection conn =null;
PreparedStatement pr=null;
try {
// 2.从池子中获取连接
conn =C3P0Utils.getConnection();
String sql="insert into category values(?,?)";
// 必须在自定义的Connection类中重写prepareStatement(sql)方法
pr=conn.prepareStatement(sql);
pr.setString(1, "c006");
pr.setString(2, "燕小六");
int rows=pr.executeUpdate();
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (Exception e) {
throw new RuntimeException();
}finally{
CopyOfCopyOfJDBCUtils.release(conn, pr, null);
}
}
DBCP连接池:配置文件名称.properties,不能编写中文。
JDBC简化工具包DBUtils目的:节省代码。
import java.sql.SQLException;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.Test;
import cn.jdbc.utils.C3P0Utils;
//测试DUBtils工具类的增删改查
public class TestDBUtils {
//添加所有用户方法
@Test
public void testAddUser(){
try {
// 1.创建核心类QuerryRunner
QueryRunner qr=new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql ="insert into category values(?,?)";
// 3.为占位符设置值
Object[] params={"余淮","耿耿"};
// 4.执行添加操作
int rows= qr.update(sql,params);
if(rows>0){
System.out.println("添加成功");
}else{
System.out.println("添加失败");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void testDeleteUser(){
try {
// 1.创建核心类QuerryRunner
QueryRunner qr=new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql ="delete from category where cid=?";
// 3.为占位符设置值
Object[] params={"余淮"};
// 4.执行添加操作
int rows= qr.update(sql,params);
if(rows>0){
System.out.println("删除成功");
}else{
System.out.println("删除失败");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Test
public void testUpdateUserById(){
try {
// 1.创建核心类QuerryRunner
QueryRunner qr=new QueryRunner(C3P0Utils.getDataSource());
// 2.编写sql语句
String sql ="update category set cname=? where cid=?";
// 3.为占位符设置值
Object[] params={"燕子","c005"};
// 4.执行添加操作
int rows= qr.update(sql,params);
if(rows>0){
System.out.println("修改成功");
}else{
System.out.println("修改失败");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}