自己动手写JDBC驱动来监视SQL语句(2)

来源:互联网 发布:科比65分全场数据 编辑:程序博客网 时间:2024/05/22 00:37

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

尊重别人的劳动成果也就是尊重自己!

推荐:2009FLY文摘|正正博客