问题概述:
之前本系列博客写的,全部都是一个connection对象,不知道大家发现没有,我们既然做了一个Connection工具类,那么大家肯定都是从那里面拿Connection对象的,之前的如果是多线程运行,很容易出问题的,你想想事务处理就知道了,同时用事务处理操作同一个Connection,肯定会出问题的。
例如:
一方的事务在提交的时候,你正好运行了一个事务中的一个操作,那么你这个操作也会被提交,而且你后面的提交或回滚失效的,如果对方把Connection关闭了,你的程序还会挂。
等等问题…
那么,我们就需要一个Connection对象池,谁来了,就去池中拿对象,不会再是同一个了,而且可以很好的控制池中Connection对象的数目,不可能一直new Connection对象的,如果池中的对象用完了,我们就让那个客户端等,等别人用完Connection对象还回到池中。
配置文件:jdbc.propertise
##MySQL driver=com.mysql.jdbc.Driver url=jdbc:mysql:///hncu?useUnicode=true&characterEncoding=utf-8 username=root password=1234 ##Oracle #driver=oracle.jdbc.driver.OracleDriver #url=jdbc:oracle:thin:@localhost:1521:orcl #username=scott #password=tiger
ConnsUtil:Connection池
package cn.hncu.pool; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.List; import java.util.Properties; public class ConnsUtil { //声明一个池 private static List<Connection> pool = new ArrayList<Connection>(); //声明池中的Connection对象个数 private static final int NUM = 3; static{ try { //读取配置文件 Properties p = new Properties(); p.load(ConnsUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")); String driver = p.getProperty("driver"); String url = p.getProperty("url"); String user = p.getProperty("username"); String password = p.getProperty("password"); Class.forName(driver); for(int i=0;i<NUM;i++){ Connection con = DriverManager.getConnection(url, user, password); //加入池中 pool.add(con); } } catch (Exception e) { e.printStackTrace(); } } public static synchronized Connection getConnection() throws InterruptedException{ if(pool.size()<=0){ Thread.sleep(100); return getConnection(); } return pool.remove(0); } public static void back(Connection con){ System.out.println("还回来一个Connection连接..."); pool.add(con); } }
测试类:
package cn.hncu.pool; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class TestPool { public static void main(String[] args) { Connection con = null; try{ con = ConnsUtil.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); String sql ="insert into stud values('P201','关羽',30) "; st.execute(sql); sql ="insert into stud values('P202','张飞',25) "; st.execute(sql); new OneThread(1).start(); new OneThread(2).start(); new OneThread(3).start(); new OneThread(4).start(); new OneThread(5).start(); System.out.println("主线程准备提交..."); con.commit(); System.out.println("主线程提交完毕..."); }catch (Exception e) { try { con.rollback(); System.out.println("主线程回滚了..."); } catch (SQLException e1) { throw new RuntimeException("主线程事务回滚失败!", e1); } }finally{ try { if(con!=null){ con.setAutoCommit(true); ConnsUtil.back(con); //con.close();//如果要把close内部的功能换成还连接,就需要我们以后的技术来实现 } } catch (SQLException e) { throw new RuntimeException("主线程连接关闭失败!", e); } } } } class OneThread extends Thread{ private int n; public OneThread(int n) { this.n = n; } @Override public void run() { Connection con = null; try{ con = ConnsUtil.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); String sql ="insert into stud values('P30"+n+"','刘备',30) "; st.execute(sql); sql ="insert into stud values('P31"+n+"','曹操',25) "; st.execute(sql); System.out.println("第"+n+"个线程准备提交..."); con.commit(); System.out.println("第"+n+"个线程提交完毕..."); }catch (Exception e) { try { con.rollback(); System.out.println("第"+n+"个线程回滚了..."); } catch (SQLException e1) { throw new RuntimeException("第"+n+"事务回滚失败!", e1); } }finally{ try { if(con!=null){ con.setAutoCommit(true); //con.close();//这里就是我们需要改进的地方 ConnsUtil.back(con); } } catch (SQLException e) { throw new RuntimeException("第"+n+"连接关闭失败!", e); } } } }
在调用返回Connection对象回到池中的时候,用普通的调用,不可避免的要去调用自己写的一个返回方法,如何在不改变程序员变成习惯(一般都习惯用close()去关闭那个对象)的情况下去实现Connection对象的回收呢,那就需要用到装饰模式,或者代理模式了。
装饰模式:
装饰模式又名包装(Wrapper)模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰模式以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。
其实我们要做的就是写一个MyConnection类实现Connection接口,实现所有方法,再进行扩展!
public class ConnsUtil { private static List<Connection> pool = new ArrayList<Connection>(); private static final int NUM=3; static{ try { Properties p = new Properties(); p.load(ConnsUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")); String drive = p.getProperty("driver"); String url = p.getProperty("url"); String user = p.getProperty("username"); String password = p.getProperty("password"); Class.forName(drive); for(int i=0;i<NUM;i++){ Connection conn = DriverManager.getConnection(url, user, password); } } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (SQLException e) { e.printStackTrace(); } } public static synchronized Connection getConnection() throws InterruptedException{ if(pool.size()<=0){ Thread.sleep(50); return getConnection(); } return pool.remove(0); } static class MyConnection implements Connection{ //封装一个Connection private Connection con; public MyConnection(Connection con){ this.con=con; } @Override public <T> T unwrap(Class<T> iface) throws SQLException { return con.unwrap(iface); } @Override public void rollback() throws SQLException { con.rollback(); } @Override public Statement createStatement() throws SQLException { return con.createStatement(); } @Override public void setAutoCommit(boolean autoCommit) throws SQLException { con.setAutoCommit(autoCommit); } @Override public void commit() throws SQLException { con.commit(); } ...... //...需要把接口的所有方法都实现了,按照上面的样式去写就行,如果不用增加功能或修改的,直接返回con.方法(参数);就可以了。如果需要修改,就自己写。比如:这里我们需要修改close();方法. @Override public void close() throws SQLException { pool.add(this); //直接把自己这个对象加进去 } } }