网站建设知识
MySQL第七天
2025-07-22 10:01  点击:0

在上次写的连接模板中,Connection是单例,只适用于单线程也就是一个用户操作,一旦多线程同时运行,就会挂掉,这里将探讨原因和解决办法。

单例工厂类的漏洞:

这里写了一个类,专门对此做了测试:

TxDemo.java

package cn.hncu.tx;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import cn.hncu.pubs.ConnFactory;//使用的是单例连接工厂ConnFactorypublic class TxDemo {public static void main(String[] args) {Connection con = ConnFactory.getConn();try {Statement st = con.createStatement();con.setAutoCommit(false);// 开启一个事务st.executeUpdate("insert into person2 values('P07','赵子龙','1')");MyThread th1 = new MyThread(1);MyThread th2 = new MyThread(2);MyThread th2 = new MyThread(3);MyThread th4 = new MyThread(4);th1.start();th2.start();th2.start();th4.start();Thread.sleep(1000);conmit();System.out.println("main线程提交一个事务");} catch (Exception e) {try {con.rollback();System.out.println("main线程回滚一个事务");} catch (SQLException e1) {e1.printStackTrace();}} finally {try {//con.setAutoCommit(true);//con.close();} catch (Exception e) {e.printStackTrace();}}}}class MyThread extends Thread {private int num;public MyThread(int num) {this.num = num;}@Overridepublic void run() {Connection con = ConnFactory.getConn();try {Statement st = con.createStatement();con.setAutoCommit(false);// 开启一个事务String sql = "insert into person2 values('P07" + num + "','赵子龙"+ num + "','1')";if(num==3){sql = "insert into person2 values('P07" + num + ",'赵子龙"+ num + "','1')";}else{Thread.sleep(100);}//System.out.println("sql-----" + sql);st.executeUpdate(sql);conmit();System.out.println(num + "线程提交一个事务");} catch (Exception e) {try {con.rollback();System.out.println(num + "线程回滚一个事务");} catch (SQLException e1) {e1.printStackTrace();} finally {try {//con.setAutoCommit(true);//con.close();} catch (Exception e1) {e1.printStackTrace();}}}}}

串数据:虽然main线程提交了事务,但是被3线程给回滚了,所以main线程做的插入动作也被回滚了,再提交也没用

解决:要开一个连接池,池中放连接,对池做维护

ConnUtils.java工具类

package cn.hncu.pool;import java.sql.Connection;import java.sql.DriverManager;import java.util.ArrayList;import java.util.List;import java.util.Properties;//new出多个连接,放进池中,连接不再是单例,用池当单例public class ConnUtils {private static List pool=new ArrayList();private final static int NUM=3;private ConnUtils(){}static{Properties p=new Properties();try {p.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));//可从本类ConnUtils.Class.getClassLoader().getResourceAsStream("jdbc.properties")拿String dirver=p.getProperty("driver");String url=p.getProperty("url");String user=p.getProperty("username");String password=p.getProperty("password");Class.forName(dirver);for(int i=0;innection="" exception="" interruptedexception="" public="" return="" span="" static="" synchronized="" try="" void="">


TxDemo2.java测试类

package cn.hncu.tx;import java.sql.Connection;import java.sql.SQLException;import java.sql.Statement;import cn.hncu.pool.ConnUtils;import cn.hncu.pubs.ConnFactory;//使用的是多例连接工具ConnUtilspublic class TxDemo2 {public static void main(String[] args) {Connection con = ConnUtils.getConn();try {Statement st = con.createStatement();con.setAutoCommit(false);// 开启一个事务st.executeUpdate("insert into person2 values('P07','赵子龙','1')");MyThread2 th1 = new MyThread2(1);MyThread2 th2 = new MyThread2(2);MyThread2 th2 = new MyThread2(3);MyThread2 th4 = new MyThread2(4);th1.start();th2.start();th2.start();th4.start();Thread.sleep(1000);conmit();System.out.println("main线程提交一个事务");} catch (Exception e) {try {con.rollback();System.out.println("main线程回滚一个事务");} catch (SQLException e1) {e1.printStackTrace();}} finally {try {con.setAutoCommit(true);//con.close();ConnUtils.back(con);//这里是回收Connection连接,应做成con.close(),池在背后回收资源,调用back方法} catch (Exception e) {e.printStackTrace();}}}}class MyThread2 extends Thread {private int num;public MyThread2(int num) {this.num = num;}@Overridepublic void run() {Connection con = ConnUtils.getConn();try {Statement st = con.createStatement();con.setAutoCommit(false);// 开启一个事务String sql = "insert into person2 values('P07" + num + "','赵子龙"+ num + "','1')";if(num==2){sql = "insert into person2 values('P07" + num + ",'赵子龙"+ num + "','1')";}else{Thread.sleep(100);}//System.out.println("sql-----" + sql);st.executeUpdate(sql);conmit();System.out.println(num + "线程提交一个事务");} catch (Exception e) {try {con.rollback();System.out.println(num + "线程回滚一个事务");} catch (SQLException e1) {e1.printStackTrace();} finally {try {con.setAutoCommit(true);//con.close();ConnUtils.back(con);} catch (Exception e1) {e1.printStackTrace();}}}}}


每个线程都独立开了,各不影响:

装修模式:

对Connection功能进行增强

ConnUtils2.java

由于本代码实现了Connection接口,代码较多,但大部分都是 不重要的,这里我只对改动的代码贴出来:

在静态块for循环中

for(int i=0;innection="" myconnection="" pre="">

这里用到了MyConnection内部类

//采用包装模式(装修模式)对Connection con功能进行增强//因为是静态块中new  MyConnection 同时还要访问静态变量pool,所以要声明成静态,不然new 的时候会出现错误 static class MyConnection implements Connection {private Connection con;public MyConnection(Connection con){this.con=con;}

下面都是实现Connection抽象方法,我们只对close方法进行更改,其余还是用原来的

//功能增强就是这个地方,把close方法,改成放回池中@Overridepublic void close() throws SQLException {System.out.println("往池中还回一个连接------"+this);pool.add(this);//注意这里要放this}


TxDemo3.java中也只是把原先的ConnUtils.back(con)方法,改成con.close()方法,其余与TxDemo2.java相同,这里不再重叙。

结果也是正确的:

代理模式:

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

代理模式的初期引入:

房东与租客:

Renter.java

package cn.hncu.proxy.rent;public class Renter implements IRenter {@Overridepublic void rent(){System.out.println("房东:租出房子,收取房租");}@Overridepublic String toString() {return "好好保护我的房子....";}}

IRenter.java

package cn.hncu.proxy.rent;public interface IRenter {public abstract void rent();}


Client.java

package cn.hncu.proxy.rent;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 Renter r=new Renter();//被代理对象-----原型对象Object newObj=Proxy.newProxyInstance(//ClassLoader loader, Class[] interfaces, InvocationHandler hClient.class.getClassLoader(),new Class[]{IRenter.class}, new InvocationHandler() {@Override//参数:proxy是代理后的对象(和外面的newObj一样的),method是当前被调用的method对象,args是当前method对象的参数public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {if(method.getName().equals("rent")){System.out.println("给中介交点费用...");return method.invoke(r, args);}return method.invoke(r, args);//这里是放行,不做任何拦截,只是从这里过了一下}});//代理后的newObj通常都是强转换成接口类型来使用IRenter ir=(IRenter)newObj;ir.rent();String str=ir.toString();System.out.println(str);}}

运行结果图:


连接池用代理模式:

ConnnUtils3.java

package cn.hncu.pool;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;//new出多个连接,放进池中,连接不再是单例,用池当单例public class ConnUtils3 {private static List pool=new ArrayList();private final static int NUM=3;private ConnUtils3(){}static{Properties p=new Properties();try {p.load(ClassLoader.getSystemResourceAsStream("jdbc.properties"));//可从本类ConnUtils.Class.getClassLoader().getResourceAsStream("jdbc.properties")拿String dirver=p.getProperty("driver");String url=p.getProperty("url");String user=p.getProperty("username");String password=p.getProperty("password");Class.forName(dirver);for(int i=0;innection="" exception="" final="" interruptedexception="" method="" new="" newobj="Proxy.newProxyInstance(" object="" override="" pre="" public="" return="" static="" synchronized="" throwable="" throws="" try="">

测试类与测试结果和上次的一样,这里不再贴出。

通用模板:

ProxyUtils.java

package cn.hncu.proxy.util;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;public class ProxyUtils implements InvocationHandler {private Object obj;private ProxyUtils(Object obj){this.obj=obj;}public static Object getProxy(Object obj){Object newObj=Proxy.newProxyInstance(ProxyUtils.class.getClassLoader(), obj.getClass().getInterfaces(), new ProxyUtils(obj));//构造传参return newObj;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("前面拦拦:......");Object resObj=method.invoke(obj, args);System.out.println("后面拦拦:......");return resObj;}}

测试模板:

人和狗

dog.java

package cn.hncu.proxy.util;public class Dog implements IDog {private String name;public Dog(){this.name="小黑";}@Overridepublic void bark(){System.out.println(name+": 汪``汪``汪``");}}


IDog.java

package cn.hncu.proxy.util;public interface IDog {public abstract void bark();}

Person.java

package cn.hncu.proxy.util;public class Person implements IPerson {private String name;public Person(String name){this.name=name;}@Overridepublic void sayHi(){System.out.println("Hello,I am "+name);}}


IPerson.java

package cn.hncu.proxy.util;public interface IPerson {public abstract void sayHi();}


Client.java

package cn.hncu.proxy.util;public class Client {public static void main(String[] args) {Dog dog=new Dog();IDog dog2=(IDog) ProxyUtils.getProxy(dog);Person p=new Person("二牛");IPerson p2=(IPerson) ProxyUtils.getProxy(p);p2.sayHi();System.out.println("------------------------");dog2.bark();}}


运行结果: