自己动手写JDBC驱动来监视SQL语句(2)
Author 正正 Date 2011.01.28 13:22:00 转载请注明出处 正正博客 http://www.2009fly.com
还从没有写过连载的文章,但是上次说的不是很完整,那天晚上把人困的不行,今天补过来。晚上就要回家了,一星期不能上网,这应该是春节前的最后一篇吧。
前面我们已经有了自己的JDBC的驱动MyDriver和与数据库的连接对象MyConnection。因为连接数据库是要和底层打交道的,必须主 动的去关闭与数据库的连接,因此千万认真的实现这几个资源的close方 法:Connection,Statenment,PreparedStatement,ResultSet等。好了,切入正题,我们今天讨论一下 PreparedStatement的写法,因为其是Statement的子对象,会了PreparedStatement的书写,那么 Statement也就不在话下了。
首先,和前面介绍的思想一下,要有一个真正干活的家伙:realPreparedStatement来作为我们自己的 MyPreparedStatement的“奴隶”。真正干活的时候我们叫realPreparedStatement去做,我们自己的 MyPreparedStatement只在前后做一点自己的事情就好了,比如统计sql语句执行的时间,记录sql语句执行的内容,甚至在真正的sql 执行前进行修改来优化它。更深入点,我们甚至可以对不同的数据库的方言(dialect)进行相应的解释来转化成当前 realPreparedStatement对应的数据库的本地方言,当然你需要自己解析(parse)SQL语法。下面我们只谈简单的sql语句打印实 现。由于PreparedStatement执行sql时,比如execute()方法,并没有传入的sql语句,我们需要自己构造其真正要执行的sql 语句的原型,因此除了realPreparedStatement变量外,我们还需要几个辅助构造sql语句的成员变量。那么需要什么呢?我们可以想象, 一条简单的DML的sql语句的构成及其格式。主要讨论的就是PreparedStatement的’?’怎么可以替换成我们真正传入的数据。我们可以构 造两个列表或者数组,无论是什么,我们可以叫容器,一个存放带“‘’”的类型数据,一个存放不带“‘’”的类型的数据。比如:
CREATE TABLE T1(C1 INT PRIMARY KEY NOT NULL,C2 VARCHAR(20)) ;
我们传入给PreparedStatement的INSERT语句是:
INSERT INTO T1(C1,C2) VALUES(?,?);
如果真正数据时:1,’2009FLY’;的话,我们希望构成的是:
INSERT INTO T1(1,’2009FLY’);
当然,你也可以用其他的算法,比如一个存放容器value,另一个存放flag来标记是否该加”’’”,下面的实现就用的第二种算法。
那么到底什么类型该加”’’”,什么时候不该加”’’”;呢?聪明的你一定知道。但是要注意区分不同的数据库和同一种数据库的不同JDBC驱动,比 如SQLite的JDBC驱动,在SQLite里面就没有通常DBMS的数据类型。还有就是对于setObject()这样的方法的处理,这是一定要注意 的。好了,开始构造我们的MyPreparedStatement吧:
package com.2009fly;
import … …//请自行添加
public class MyPreparedStatement implements PreparedStatement{
private Connection myConnection;
private PreparedStatement realPreparedStatement;
private String sql;//欲处理的sql语句,带有’?’
… …//其它变量
public MyPreparedStatement (Connection conn,PreparedStatement ps,String sql){
this.myConnection =conn;//这个传入我们自己的Connection
this.realPreparedStatement=ps;//传入真正的realPreparedStatement
this.sql=sql;
}
… …
}
现在看看在MyConnection里面是怎么做的:
public PreparedStatement prepareStatement(String sql)
throws SQLException{
PreparedStatement ps = null;
ps=new MyPreparedStatement(this, realConnection.prepareStatement(sql),sql);
return ps;
}
Statement和PreparedStatement是类似的,但是后者的sql语句还要自己去构造,前面已经告诉了你的方法,可以在MyPreparedStatement里面再声明几个变量:
public final static int MAX_FIELDS = 32;//初始化数组大小
public static int GROW_MAX = 16;//超出MAX_FIELDS后自增大小
private Object values[];//set方法中的值
private boolean flag[]; //对应的values是否是含有引号的数据类型
在构造函数中添加:
values = new Object[MAX_FIELDS+1];
flag = new boolean[MAX_FIELDS+1];
这里values和flag是一一对应的。
我们这样子实现setTime()方法:
public void setTime(int p0, Time p1) throws SQLException {
setObjectAsString(p0, p1);//value[p0]=p1,flag[p0]=true;注意p0>MAX_FIELDS的处理
prepStmtPassthru.setTime(p0,p1);
}
在执行execute时候,根据sql,value和flag来构造真正的标准sql语句。这个就不写出来了,你会的!
目前你可以写自己的JDBC驱动工具来打印sql日志了,再加上个时间的函数,就可以检查每条sql执行所用的时间。再深一点,你加入sql解析,就可以转换不同DBMS的方言了,希望你能走的更远。
呵呵… …今天就到这里了,如果你还有不明白的地方,可以email给我:zzcwfp@gmail.com
====================================================================
版权所有,欢迎转载,请在转载前注明原文出处:正正博客 http://www.2009fly.com
尊重别人的劳动成果也就是尊重自己!
====================================================================