问题概述:

之前本系列博客写的,全部都是一个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;}@Overridepublic 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{//封装一个Connectionprivate Connection con;public MyConnection(Connection con){this.con=con;}@Overridepublic <T> T unwrap(Class<T> iface) throws SQLException {return con.unwrap(iface);}@Overridepublic void rollback() throws SQLException {con.rollback();}@Overridepublic Statement createStatement() throws SQLException {return con.createStatement();}@Overridepublic void setAutoCommit(boolean autoCommit) throws SQLException {con.setAutoCommit(autoCommit);}@Overridepublic void commit() throws SQLException {con.commit();}......//...需要把接口的所有方法都实现了,按照上面的样式去写就行,如果不用增加功能或修改的,直接返回con.方法(参数);就可以了。如果需要修改,就自己写。比如:这里我们需要修改close();方法.@Overridepublic void close() throws SQLException {pool.add(this);//直接把自己这个对象加进去}}
}

测试类:

package cn.hncu.pool2;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('P203','关羽',30) ";st.execute(sql);sql ="insert into stud values('P204','张飞',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);con.close();//这样,我们直接调用close方法就可以了!!!}} catch (SQLException e) {throw new RuntimeException("主线程连接关闭失败!", e);}}}
}class OneThread extends Thread{private int n;public OneThread(int n) {this.n = n;}@Overridepublic void run() {Connection con = null;try{con = ConnsUtil.getConnection();con.setAutoCommit(false);Statement st = con.createStatement();String sql ="insert into stud values('P40"+n+"','刘备"+n+"',30) ";st.execute(sql);sql ="insert into stud values('P41"+n+"','曹操"+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();//这样,我们直接调用close方法就可以了!!!}} catch (SQLException e) {throw new RuntimeException("第"+n+"连接关闭失败!", e);}}}}

测试结果:

代理模式:

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。

好处就是:
在某些情况下,如果不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

可以为原来的方法增加或者修改动作!

代理模式一般需要3个角色:

抽象角色:声明真实对象和代理对象的共同接口(必须是接口;

代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装与包装。

真实角色(被代理的角色):代理角色所代表的真实对象,是我们最终要引用的对象。

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。 代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后 续处理。

接口:

package cn.hncu.proxy.demo1;public interface IRenter {public void rent(int i);public String say();
}

实际对象:

package cn.hncu.proxy.demo1;public class Renter implements IRenter{@Overridepublic void rent(int i) {System.out.println("给你"+i+"个房间,需要交500元!");}public String say() {System.out.println("Renter:你好,我是房东,房子实际只要300元");return "房东的返回结果...";}
}

代理模式的核心部分:

package cn.hncu.proxy.demo1;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class Client {public static void main(String[] args) {//被代理的对象final IRenter r = new Renter();//这个obj是代理后的对象Object obj = Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{IRenter.class},new InvocationHandler() {@Override//proxy是代理后的对象(等价于返回的obj),method就是类反射中的方法对象, args是执行method方法所需的参数public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("我是中介...");//在这里,我们把房东说的话和返回值给拦截了。if(method.getName().equals("say")){System.out.println("我是中介,你还没交中介费,无法联系房东...");return "";}Object obj = method.invoke(r, args);System.out.println("欢迎再来找我中介...");//r就是原来的对象return obj;//这个返回结果是调用r中的方法method.invoke(r, args);返回的结果}});IRenter ir = (IRenter) obj;ir.rent(3);System.out.println(ir.say());}
}

可以看出来InvocationHandler这个接口中的invoke方法是最核心的部分!!!
在里面我们利用类加载器和类反射,直接可以来调用原来的类,而且可以加上自己需要的功能,或者实现拦截!!!

上面的例子只能实现对Renter 类的代理。

通用的代理模板类:

public interface IAnimal {public void run();
}public class Dog implements IAnimal{private String name;public Dog(String name) {this.name = name;}@Overridepublic void run() {System.out.println("Doge:"+name+"is running...");}
}
public interface IPerson {public void sayHi();
}public class Person implements IPerson {private String name;public Person(String name){this.name = name;}@Overridepublic void sayHi() {System.out.println(name+"说:祝你快乐...");}}

ProxyUtil.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class ProxyUtil implements InvocationHandler{private Object srcObj=null;public ProxyUtil(Object srcObj) {this.srcObj=srcObj;}public static Object getProxy(Object srcObj){Object obj = Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),srcObj.getClass().getInterfaces(),new ProxyUtil(srcObj)); //obj是代理后的返回结果其实就是method.invoke(srcObj, args)的返回结果return obj;}//这个就是代理的方法了@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("进入了代理区域~~");//这个srcObj就是原来的对象return method.invoke(srcObj, args);}
}

client

package cn.hncu.proxy.demo2;import cn.hncu.proxy.demo2.domain.Dog;
import cn.hncu.proxy.demo2.domain.IAnimal;
import cn.hncu.proxy.demo2.domain.IPerson;
import cn.hncu.proxy.demo2.domain.Person;public class Client {public static void main(String[] args) {Dog dag = new Dog("旺财");IAnimal iDag = (IAnimal) ProxyUtil.getProxy(dag);iDag.run();Person p = new Person("小明");IPerson ip =(IPerson)  ProxyUtil.getProxy(p);ip.sayHi(); }
}

输出结果:

我们现在再来把之前用装饰模式写的ConnsUtil类改写成代理模式:

ConnsUtil类的代理模式:

package cn.hncu.pool3;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.util.ArrayList;
import java.util.List;
import java.util.Properties;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 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++){final Connection conn = DriverManager.getConnection(url, user, password);//只需要改这里就行了!//使用动态代理代理conn对象,实现对close方法的拦截Object obj = Proxy.newProxyInstance(ConnsUtil.class.getClassLoader(),conn.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {if(method.getName().equalsIgnoreCase("close") && (args==null || args.length==0)){pool.add((Connection)proxy);return null;}else{return method.invoke(conn, args);}}});pool.add((Connection)obj);}} catch (Exception e) {e.printStackTrace();}}public static synchronized Connection getConn() throws Exception{if(pool.size()<=0){Thread.sleep(100);return getConn();}return pool.remove(0);}}

大家可以自己动手写一写,加深印象, Proxy.newProxyInstance(类加载器,class数组(被代理对象的接口的class数组),new InvocationHandler());
记住这个就可以了!
被代理对象的接口的class数组可以用conn.getClass().getInterfaces(),来获得。
conn为被代理的对象!

动态代理比装饰模式方便很多!

MySQL---数据库从入门走向大神系列(十)-Connection对象池、装饰模式与动态代理模式相关推荐

  1. MySQL---数据库从入门走向大神系列(十二)-构建MVC项目

    这个是对前面技术的一个小总结吧,用到的大概技术有: MVC框架,加注解,Struts框架的思想,动态代理,线程管理对象ThreadLocal,Connection对象池,Properties文件读取, ...

  2. MySQL---数据库从入门走向大神系列(十四)-ComboPooledDataSource(C3P0连接池配置)

    需要准备的jar包: MySQL的jar包mysql-connector-java-5.1.39-bin目前是5.1.39版本: http://dev.mysql.com/downloads/conn ...

  3. MySQL---数据库从入门走向大神系列(十五)-Apache的DBUtils框架使用

    DBUtils简介: commons-dbutils 是 Apache 组织提供的一个开源 JDBC工具类库,它是对JDBC的简单封装,学习成本极低,并且使用dbutils能极大简化jdbc编码的工作 ...

  4. MySQL---数据库从入门走向大神系列(二)-用Java对MySQL进行增删改查

    上节已经学会对MySQL进行简单的增删改查了,那么,我们如何实现用Java来对数据库操作增删改呢. 本节将用Java演示对MySQL进行增删改查. 简单的来说,分为4个步骤: 1.加载连接器(驱动) ...

  5. MySQL---数据库从入门走向大神系列(七)-Java访问数据库配置及简单使用方法execute

    从操作配置文件properties中读取连接字符串,通过该字符串进行数据连接,需要写三个文件其中,两个是java类,一个是后缀名为.properties的文件,该文件放在src工作目录下. 需要准备的 ...

  6. python从入门到大神系列手机_python从入门到大神---2、和Python编程相遇的日子

    python从入门到大神---2.和Python编程相遇的日子 一.总结 一句话总结: python2和python3是很不同的,连语法都不同,比如 print 函数打印结果 1.python中pip ...

  7. 【python 图像识别】图像识别从菜鸟走向大神系列1

    无意中发现了一个巨牛的人工智能教程,忍不住分享一下给大家.教程不仅是零基础,通俗易懂,而且非常风趣幽默,像看小说一样!觉得太牛了,所以分享给大家.点这里可以跳转到教程.人工智能教程 一.安装配置(py ...

  8. oracle入门到大神(备mysql、java基础、javaee必经之路)-任亮-专题视频课程

    oracle入门到大神(备mysql.java基础.javaee必经之路)-19178人已学习 课程介绍         Oracle10g的安装.orcale的基本概念介绍.命令行常用操作.Scot ...

  9. c,c++小白到大神系列教程之一:C语言入门-王健伟-专题视频课程

    c,c++小白到大神系列教程之一:C语言入门-1127人已学习 课程介绍         本课程针对 有一点计算机基础比如知道二进制.八进制.十六进制数据的含义,对内存.堆.栈等有基本概念的计算机初学 ...

最新文章

  1. java的不足_Java不足之我见
  2. Tomcat启动过程源码分析六
  3. Redis 内存压缩实战
  4. LaTeX Test
  5. 数字语音信号处理学习笔记——绪论(2)
  6. 遥控开关在云智能物联网领域:智能养殖高效、生态、安全!
  7. code iban 是有什么组成_深入浅出Zookeeper(四):客户端的请求在服务器中经历了什么...
  8. Docker : Docker 安装ES
  9. 分享:苹果APP更新上架被拒的另一种理由(Safety - Objectionable Content)
  10. servlet过滤器 实例_Java Servlet过滤器示例教程
  11. 管理感悟:出了事故,关键是想想自己哪里能改进
  12. IPFS 相关开发资源汇总
  13. 正交相机和透视相机的区别
  14. VS 0x80041FEB
  15. nginx-proxy-manager在线申请证书
  16. 数据库在软件开发中的作用是什么?
  17. 苹果主题商店_小米怕被苹果找茬,开始下架相关IOS主题了!
  18. python气泡图的地图_Python数据可视化:香港地图、房价可视化,绘制气泡图
  19. P6560 [SBCOI2020] 时光的流逝 (博弈,反向建图,拓扑排序
  20. Verilog HDL 语法学习笔记

热门文章

  1. Linux shell计算两个文件的交集,并集和差集
  2. linux两个文件取交集、并集、差集
  3. 关于scrollHeight和scrollTop取值为0的问题
  4. 网站被百度降权后快速恢复权重和收录的几个方法
  5. python编写凯撒密码的加密函数_python实现 古典密码 凯撒密码的加密与解密
  6. IEEE754标准原文,及浮点数在线转换
  7. 【致青春】致我终将逝去的22年的青春
  8. VScode神仙插件,程序员必备
  9. 程序员3~5年后如何规划自己
  10. ESD防静电门禁系统详解