public Statement getStatement() {
Class[] interfaces = statement.getClass().getInterfaces();
if(interfaces==null||interfaces.length==0){
interfaces = new Class[1];
interfaces[0] = Statement.class;
}
Statement stmt = (Statement)Proxy.newProxyInstance(
statement.getClass().getClassLoader(),
interfaces,this);
return stmt;
}
/**
* 方法接管
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String method = m.getName(); //接管setString方法
if(decode && SETSTRING.equals(method)) {
try{
String param = (String)args[1];
if(param!=null)
param = new String(param.getBytes(),"8859_1");
return m.invoke(statement,new Object[]{args[0],param});
} catch(InvocationTargetException e){
throw e.getTargetException(); }
}
//接管executeQuery方法
if(decode && EXECUTEQUERY.equals(method)){
try{
ResultSet rs = (ResultSet)m.invoke(statement,args);
return new _ResultSet(rs,decode).getResultSet();
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
try{
return m.invoke(statement, args);
} catch(InvocationTargetException e) {
throw e.getTargetException();
}
}
//两个要接管的方法名
private final static String SETSTRING = "setString";
private final static String EXECUTEQUERY = "executeQuery";
}
_ResultSet.java
/*
* Created on 2003-10-23 by Liudong
*/
package lius.pool;
import java.sql.ResultSet;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 数据库结果集的代理类
* @author Liudong
*/
class _ResultSet implements InvocationHandler{
private ResultSet rs = null;
private boolean decode = false;
public _ResultSet(ResultSet rs,boolean decode) {
this.rs = rs;
this.decode = decode;
}
public ResultSet getResultSet(){
Class[] interfaces = rs.getClass().getInterfaces();
if(interfaces==null||interfaces.length==0){
interfaces = new Class[1];
interfaces[0] = ResultSet.class;
}
ResultSet rs2 = (ResultSet)Proxy.newProxyInstance(rs.getClass().getClassLoader(),
interfaces,this);
return rs2;
}
/**
* 结果getString方法
*/
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
String method = m.getName();
if(decode && GETSTRING.equals(method)){
try{
String result = (String)m.invoke(rs,args);
if(result!=null)
return new String(result.getBytes("8859_1"));
return null;
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
try{
return m.invoke(rs, args);
}catch(InvocationTargetException e){
throw e.getTargetException();
}
}
private final static String GETSTRING = "getString";
}
现在我们已经把三个接口的代理类做好了,下一步就是怎么来使用这三个类。其实对于使用者来讲并不需要关心三个类,只需要了解_Connection就可以了,因为另外两个是_Connection直接调用的。为了使用_Connection我们必须传入两个参数,第一个是数据库实际的数据库连接实例,另外一个是布尔值代表是否进行转码处理。我们必须先通过实际的情况获取到数据库连接后再传入_Connection的构造函数作为参数,下面例子告诉你如何来使用_Connection这个类:
Connection conn = getConnection(); //获取数据库连接
boolean coding = false; //从配置或者其他地方读取是否进行转码的配置
//接管数据库连接实例
_Connection _conn = new _Connection(conn,coding);
//获得接管后的数据库连接实例,以后直接使用conn2而不是conn
Connection conn2 = _conn.getConnection(); |
因为对一个应用系统来讲,数据库连接的获取必然有统一的方法,在这个方法中加入对连接的接管就可以一劳永逸的解决数据库的编码问题。
性能比较
功能没有问题了,开发者接下来就会关心性能的问题,因为在进行一些对响应速度要求很高或者大数据量的处理情况下性能就成为一个非常突出的问题。由于JAVA中的动态接口代理采用的是反射(Reflection)机制,同时又加入我们自己的一些代码例如方法名判断,字符串转码等操作因此在性能上肯定比不上直接使用没有经过接管的数据库连接。但是这点性能上的差别是不是我们可以忍受的呢,为此我做了一个试验对二者进行了比较:
测试环境简单描述:
使用ACCESS数据库,建两张结构一样的表,计算从获取连接后到插入数据完毕后的时间差,两个程序(直连数据库和使用连接接管)都进行的字符串的转码操作。
测试结果:
插入记录数 直连数据库程序耗时 单位:ms 使用连接接管程序耗时 性能比较
1000 2063 2250 9.0%
5000 8594 8359 -2.7%
10000 16750 17219 2.8%
15000 22187 23000 3.6%
20000 27031 27813 2.9% |
从上面这张测试结果表中来看,二者的性能的差别非常小,尽管在两万条数据的批量插入的时候时间差别也不会多于一秒钟,这样的结果应该说还是令人满意的,毕竟为了程序良好的结构有时候牺牲一点点性能还是值得的。
本文算是我之前文章《使用JAVA动态代理实现数据库连接池》中提出的数据库连接池实现的进一步完善,同样使用动态接口代理的技术来解决数据库编码的问题。JAVA的这个高级技术可以用来解决许多实际中非常棘手的问题,就像本文提到的编码问题的处理以及数据库连接池的实现,同时在WEB开发框架的实现上也有非常大的作为。欢迎对这方面感兴趣的朋友来信共同来研究。