JDBC
1. Driver接口
注册数据库驱动程序,分别为:
DriverManager.registerDriver(new Driver());
Class.forName("com.mysql.jdbc.Driver");
推荐采用第二种方式,因为它本身采用静态代码块
如果采用第一种的方式,会注册两次
2. url:即连接协议
jdbc:mysql:///127.0.0.1:3306/db9?useUnicode=true&characterEncoding=UTF8
3. Connection
Statement及其子类
ResulSet
select * from someTable --N+2 查询结果永远是N+2条,因为包括行首,行尾
连接池的引入
创建Connection的过程是非常耗时的,为保证Connection可以重用,应该对其进行池管理.
传统的连接只有一个,我们把它比作公路,我们连接创建后进行sql操作后再关闭连接,就相当于你从高速公路到达某个地方后,把走过的那条高速公路拆了,过河拆桥,别人就不能访问了,别人只好重修修建公路(自己再重新new 一个连接)
于是,传统解决办法:创建一个静态工厂方法(多次使用,防止重复new),但是只有一个连接
所以还是不行,于是引入了连接池,我们可以把它比作抽奖箱里面的小球,具体自己可以琢磨一下
连接池的基本实现
为什么要有连接池
1:维护多个连接。必须要保证每一个线程获取到的是不同的connection对象。
2:提供一个方法可以回收连接。
而程序员,总是调用close方法,所以为了回收连接。我们应该重写close方法。对close方法增强。一下是三种实现方式:
子类。(继承的体现)
包装。
代理。
动态代理实现连接池
动态代理:
作用:1.对某个方法增强.
2.在不污染原类的情况下,修改原类的行为.
代理类,与被代理类,两个不同的实体
要求:
所有被代理的类,都必须要拥有一个接口.
本质上是对方法的修改,但其实它是通过反射执行的某个方法.
代理是代理接口
代理的任务
1.在内存中创建某个接口的子类.
2.拦截所有在代理上知心的方法(出了getClass方法)
用动态代理书写连接池
设计:
代理的目标:原生的Connection
代理的目的:修改close方法,让close方法不可以关闭连接,且主动回收连接
小技巧:在myeclipse中,资源文件创建时默认是iso 8859-1编码(不管你项目创建的是以什么编码方式的)
具体代码实现:
1 public class ConnUtils{ 2 //第一步:声明连接池维护所有的连接 3 private static List<Connection> pool = new ArrayList<Connection>(); 4 //第二步:静态代码块中创建多个连接 5 static{ 6 try{ 7 Class.forName("com.mysql.jdbc.Driver"); 8 String url = "jdbc:mysql:///db909?characterEncoding=UTF8"; 9 for(int i=0;i<3;i++){ 10 final Connection con = DriverManager.getConnection(url,"root","1234");//com.mysql.jdbc.Jdbc4Connection@ 11 //对con对象进行动态代理 12 Object proxyedCon = 13 Proxy.newProxyInstance( 14 ConnUtils4.class.getClassLoader(), 15 new Class[]{Connection.class}, 16 //声明执行句柄,只对close方法设置拦截 17 new InvocationHandler() { 18 public Object invoke(Object proxy, Method method, Object[] args) 19 throws Throwable { 20 if(method.getName().equals("close")){ 21 System.err.println("有人想关闭连接,不能关,还连接"); 22 //将proxy再加到pool中,这个proxy就是proxyedCon 23 synchronized (pool) { 24 pool.add((Connection) proxy); 25 pool.notify(); 26 } 27 return null; 28 }else{ 29 System.err.println("放行"+method.getName()); 30 return method.invoke(con, args); 31 } 32 } 33 }); 34 //一定要将代理对象添加到池中去。 35 pool.add((Connection) proxyedCon); 36 } 37 }catch(Exception e){ 38 throw new RuntimeException(e.getMessage(),e); 39 } 40 } 41 /** 42 * 提供一个静态工厂方法返回一个连接 43 */ 44 public static Connection getCon(){ 45 synchronized (pool) { 46 if(pool.size()==0){ 47 try { 48 pool.wait(); 49 } catch (InterruptedException e) { 50 e.printStackTrace(); 51 } 52 return getCon(); 53 } 54 Connection con = pool.remove(0);//返回一个代理的connection对象 55 System.err.println("还有几个:"+pool.size()); 56 return con; 57 } 58 } 59 }
以上代码可以优化
通过类加载器读取一个资源文件:
SomeClass.class.getResourcre(xxx) --获取与SomeClass字节码同一个目录下的xxx文件.
SomeClass.class.getClassLoader().getResource(xxxxx) --获取classpath根上下的xxxxx文件
1.将url,driver,name,pwd写到一个配置文件中去
2.通过LinkedList来维护池.
优化后的代码:
1 public class ConnUtils3 { 2 private static LinkedList<Connection> pool = new LinkedList<Connection>(); 3 static{ 4 try { 5 //声明资源器类 - 6 Properties prop = new Properties(); 7 //获取这个文件的路径 8 URL url = ConnUtils3.class.getClassLoader().getResource("jdbc.properties"); 9 String path = url.getPath(); 10 //为了防止有中文或是空格 11 path = URLDecoder.decode(path,"UTf-8"); 12 File file = new File(path); 13 //加载jdbc.properties这个文件 14 prop.load(new FileInputStream(file)); 15 //获取信息 16 String driver = prop.getProperty("driver"); 17 Class.forName(driver); 18 String jdbcurl = prop.getProperty("url"); 19 String nm = prop.getProperty("name"); 20 String pwd = prop.getProperty("pwd"); 21 //创建三个原生的连接,都将它们代理 22 String poolSize = prop.getProperty("poolSize"); 23 int size = Integer.parseInt(poolSize); 24 for(int i=0;i<size;i++){ 25 final Connection con = DriverManager.getConnection(jdbcurl,nm,pwd); 26 //对con进行动态代理 27 Object proxyedObj = 28 Proxy.newProxyInstance(ConnUtils3.class.getClassLoader(), 29 new Class[]{Connection.class}, 30 new InvocationHandler() { 31 public Object invoke(Object proxy, Method method, Object[] args) 32 throws Throwable { 33 //是否是close方法 34 if(method.getName().equals("close")){ 35 synchronized (pool) { 36 pool.addLast((Connection) proxy); 37 pool.notify(); 38 } 39 return null;//如果调用的是close则不会调用被代理类的方法。 40 } 41 return method.invoke(con, args); 42 } 43 }); 44 //将代理对象放到pool中 45 pool.add((Connection) proxyedObj); 46 } 47 } catch (Exception e) { 48 e.printStackTrace(); 49 } 50 } 51 52 public static Connection getConn(){ 53 synchronized (pool) { 54 if(pool.size()==0){ 55 try { 56 pool.wait(); 57 } catch (InterruptedException e) { 58 e.printStackTrace(); 59 } 60 return getConn(); 61 }else{ 62 Connection con = pool.removeFirst(); 63 System.err.println("还有几个:"+pool.size()); 64 return con; 65 } 66 } 67 } 68 69 70 }
什么时候可以使用statement而不使用Perpstatement呢?
在用户不需要输入的时候,可以采用statement,其他建议全部使用Perpstatement
最后,附上开发开发分层图