数据库连接池
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 Listlist=new ArrayList (); 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 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("主线程准备提交"); conmit(); 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+"线程准备提交"); conmit(); 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.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 Listlist=new ArrayList (); 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 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("主线程准备提交"); conmit(); 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+"线程准备提交"); conmit(); 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); } } } }}
我们会发现,就算我们在PoolTest下调用了con.close()函数,我们也没有关闭Connection对象,因为我们在ConnsUtils中通过动态代理把con.close()进行了拦截并更改!