您的位置:首页 > 编程语言 > Java开发

mysql笔记五——数据库连接池(原理、构建)和java动态代理的使用

2016-08-10 02:00 519 查看

数据库连接池

1、什么是数据库连接池?

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

2、为什么要引入数据库连接池?出现的原因

用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。在多线程的环境下容易浪费资源

数据库连接池的出现就是为了减少服务器的负担,提高资源的利用效率。

3、原理:数据库连接池的原理就是回收机制,也就是关闭的连接能及时在利用,避免浪费。

下面是简单版的数据库连接池

ConnsUtils.javal类

package cn.hncu.pool;

import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class ConnsUtils {
private static List<Connection> list=new ArrayList<Connection>();
static{
Properties p=new Properties();
try {
p.load(ConnsUtils.class.getClassLoader().getResourceAsStream("jdbc.properities"));
String url = p.getProperty("url");
String user=p.getProperty("user");String driver=p.getProperty("driver");
Class.forName(driver);//如果是tomcat中,加载驱动不能少
String password=p.getProperty("password");
for(int i=0;i<3;i++){
Connection con=DriverManager.getConnection(url, user, password);
list.add(con);
}
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getCon(){
synchronized (list) {
if (list.size()<=0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

return list.remove(0);
}
public static void back(Connection con){
synchronized (list) {
System.out.println("还回来了一个连接....");
list.notify();
list.add(con);
}
}
}


测试:PoolTest .java

package cn.hncu.pool;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;

import cn.hncu.utils.ConnectFactory;

public class PoolTest {
public static void main(String[] args) {
Connection con=null;
try {
con = ConnsUtils.getCon();
} catch (Exception e) {
System.out.println("正在等待资源");
}
try {
con.setAutoCommit(false);
Statement st = con.createStatement();
String sql = "insert into book(name) values('bb')";
st.execute(sql);
new OneThread(1).start();
new OneThread(2).start();
new OneThread(3).start();
new OneThread(4).start();
new OneThread(5).start();
sql = "insert into book(name) values('bb2') ";
st.execute(sql);
System.out.println("主线程准备提交");
con.commit();
System.out.println("主线程提交完毕");
} catch (Exception e) {
try {
con.rollback();
System.out.println("主线程回滚了...");
} catch (SQLException e1) {
System.out.println("主线程回滚失败...");
}

}finally{
if(con!=null){
try {
con.setAutoCommit(true);
ConnsUtils.back(con);//还资源
//                  con.close();
} catch (SQLException e) {
throw new RuntimeException("连接关闭失败!", e);
}
}

}
}
}

class OneThread extends Thread{
private int i;

public OneThread(int i) {
this.i = i;
}

@Override
public void run() {
Connection con=ConnsUtils.getCon();
try {
con.setAutoCommit(false);
Statement st = con.createStatement();
String sql = "insert into book(name) values('aa="+i+"')";
st.execute(sql);
sql = "insert into book(name) values('aaa="+i+"') ";
st.execute(sql);
System.out.println("第"+i+"线程准备提交");
con.commit();
System.out.println("第"+i+"个线程提交完毕");
} catch (Exception e) {
try {
con.rollback();
System.out.println("第"+i+"个线程回滚了...");
} catch (SQLException e1) {
System.out.println("第"+i+"个线程回滚失败...");
}

}finally{
if(con!=null){
try {
con.setAutoCommit(true);
ConnsUtils.back(con);//还资源
//                  con.close();
} catch (SQLException e) {
throw new RuntimeException("连接关闭失败!", e);
}
}

}
}

}


我们发现,con对象不能关闭,否则就会出现,拿不到Connection对象的情况,导致查询无法完成,所有必须引入动态代理来解决这个弱点。


动态代理

1、简介:Java动态代理类位于Java.lang.reflect包下,一般主要涉及到以下两个类:

Interface InvocationHandler:该接口中仅定义了一个方法Object:invoke(Object obj,Method method,Object[]

args)。在实际使用时,第一个参数obj一般是指代理类(要小心使用,不小心就是死循环),method是被代理的方法,args为该方法的参数数组。这个抽象方法在代理类中动态实现。

Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容: Protected

Proxy(InvocationHandler h):构造函数,估计用于给内部的h赋值。 Static Class getProxyClass (ClassLoader loader,Class[]

interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组。

2、代理机制及特点

通过实现 InvocationHandler 接口创建自己的调用处理器; 通过为 Proxy 类指定 ClassLoader 对象和一组

interface 来创建动态代理类;

通过反射机制获得动态代理类的构造函数,其唯一参数类型是调用处理器接口类型;

通过构造函数创建动态代理类实例,构造时调用处理器对象作为参数被传入

3、核心代码:我通过图片来进行注解



下面演示详细代码,与数据库连接池联合使用

ConnsUtils.javal类

package cn.hncu.proxy;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

public class ConnsUtils {
private static List<Connection> pool=new ArrayList<Connection>();//创建一个pool池对象
private static final int NUM=3;//池中最大数
static{
Properties p=new Properties();//从配置文件读取
try {
//          p.load(ClassLoader.getSystemClassLoader().getResourceAsStream("jdbc.properities"));
p.load(ConnsUtils.class.getClassLoader().getResourceAsStream("jdbc.properities"));
String url = p.getProperty("url");
String user=p.getProperty("user");
String password=p.getProperty("password");
for(int i=0;i<NUM;i++){
final Connection con=DriverManager.getConnection(url, user, password);//final必须设置,因为内部类访问
//使用动态代理,代理conn对象,实现对close方法的拦截
Object obj=Proxy.newProxyInstance(ConnsUtils.class.getClassLoader(),
new Class[]{Connection.class},new InvocationHandler() {
//proxy是代理后的对象(等价于返回的obj), method就是类反射中的方法对象, args是执行method方法所需的参数
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {

if(method.getName().equalsIgnoreCase("close")&&(args==null||args.length==0)){
synchronized (pool) {
pool.add((Connection) proxy);//需要强转
pool.notify();//唤醒等待的资源
}
return null;
}
return method.invoke(con, args);//其他方法就行放行
}
});
pool.add((Connection)obj);//把代理后的conn对象即obj
}
} catch (IOException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static Connection getCon(){
synchronized (pool) {//同步锁,一次只能一个进来拿,避免同时拿
if(pool.size()<=0){
try {
pool.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return pool.remove(0);
}

}
}


我们会发现,就算我们在PoolTest下调用了con.close()函数,我们也没有关闭Connection对象,因为我们在ConnsUtils中通过动态代理把con.close()进行了拦截并更改!

有关DPCP和C3P0数据库连接池的使用,请参考开源数据库连接池DBCP和C3P0的使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: