一个简单实用的数据库操作框架

这个小小的数据库操作封装框架是参考IBM开发网上的两篇文章并在其基础上扩充了一些功能而得到的。所以首先要感谢两篇文章的作者。
学习JDBC以来一直想实现一个简单的封装来方便编程但是由于水平有限一直没有较好的办法,看了IBM开发网上的两篇文章以后感觉作者的设计思想很好一定能扩充成一个实用的JDBC封装。所以我在文章提供的源码基础上加了一些功能这些功能包括支持多种数据类型,处理了空值,利用反射方便的在Row对象和值对象之间进行转换,还有加了一个我自认为通用的DAO类来方便用户的操作。
我把源码提供出来有两个目的一个是希望能帮助比我还初学的初学者熟悉JDBC,另外就是请各位高手不吝赐教,改进程序中的错误如果能将你们的对JDBC的封装方法提供出来那就更好了(不要说你们只用EJB或者Hibernate,JDO什么的?)。
IBM开发网的那两篇文章分别是《一个简单的JDBC包装器》《对一个简单的JDBC包装器的扩展及应用》,我的邮箱是xsimple2003@yahoo.com.cn有事请与我联系。
设计思想
?把DBMS抽象成类Database,这个类负责管理数据库连接以及提供表对象。
?把数据库中的一张或多张表抽象成类Table,这个类中提供对表的添加,修改,删除的JDBC封装。
?将数据库表中的一条记录抽象成类Row,这个类用HashMap保存关系数据库中表格中一行数据的字段名和值并提供一些相关操作。另外这个类还提供了两个静态方法用于在Row对象和ValueObject之间进行方便的转换。
?把对个Row的集合抽象成RowSet,这个类中用一个vector把多个Row对象保存起来并提供一些相关操作。
代码分析
由于已经给出源码所以我只对代码中关键的和需要注意的地方加以说明,大家可以执行源码一边演示一边体会。
?Database类源码如下:
packagecom.gdrj.util.database;
importjava.sql.*;
importjavax.sql.*;
importcom.gdrj.util.servicelocator.*;
publicclassDatabase{
/**
*这个数据库连接成员只有在与数据库直接建立连接的情况下是有效的
*/

privateConnectionconn=null;
/**
*当这个参数有效时,表明程序是直接与数据库建立的连接而不是从连接池里取得连接
*/

privateStringurl,user,password;
/**
*当这个参数有效时,表明程序是从连接池里取得连接。
*/

privateStringdatasource;
/**
*用数据库地址,用户名,密码初始化数据库对象,这个构造器用于程序是直接
*与数据库建立连接的情况。
*@paramurl
*@paramuser
*@parampassword
*/

publicDatabase(Stringurl,Stringuser,Stringpassword){
this.url=url;
this.user=user;
this.password=password;
}

/**
*用JNDI数据源名初始化数据库对象,这个构造器用于从连接池取数据库连接的情况。
*@paramdatasource
*/

publicDatabase(Stringdatasource){
this.datasource=datasource;
}

/**
*得到数据库连接,对于是否从连接池里取连接做了自动处理即根据用户调用了哪个构造器
*来判断是否直接与数据库建立连接还是从连接池里取连接。
*对于用户来说不用考虑程序是从那里取得连接,他只管正确的初始化数据库对象。
*@return
*@throwsSQLException
*/

publicConnectiongetConnection()throwsException{
if(datasource==null){//直接与数据库建立连接

if(conn==null){
conn=DriverManager.getConnection(url,user,password);
}
}
else{//从应用服务器的连接池里取得连接

ServiceLocatorsl=ServiceLocator.getInstance();
DataSourceds=sl.getDataSource(datasource);
returnds.getConnection();//每调用一次都返回一个连接池中的数据库连接

}
returnconn;
}

/**
*释放连接,如果是直接与数据库连接的情况则什么也不做
*如果是从连接池中取得的连接那么释放传来的连接
*@paramconn
*/

publicvoiddisConnect(Connectionconnection){
if(datasource!=null){//只处理从连接池取连接的情况

try{
if(connection!=null){
connection.close();
}
}
catch(Exceptionex){}
}
}

/**
*得到与参数名对应的表对象,注意这里不作任何数据库操作
*@paramname
*@return
*/

publicTablegetTable(Stringname){
returnnewTable(this,name);
}
}

这个类是对DBMS的抽象,所以使用时应用程序中只要有一个Database对象就够了,如果你是以与数据库之间建立连接的方式使用那么你用Database(Stringurl,Stringuser,Stringpassword)构造器进行初始化。如果是从应用服务器的连接池中取得连接的方式使用那么用Database(Stringdatasource)构造器初始化,这样以后你使用这个对象进行getConnection和disConnection时就不用去考虑始终保持一个连接(C/S方式),还是将连接返回连接池了因为在disConnection中已经做了处理。集体使用方法将Table类。在getConnection中的从连接池中取连接的代码你只要参考以下《J2EE核心模式》中的服务定位器模式就知道是怎么回事了,你在用Database(Stringurl,Stringuser,Stringpassword)初始化时其中的代码不起作用。
?Table类源码如下:
packagecom.gdrj.util.database;
importjava.sql.*;
importjava.util.*;
importcom.gdrj.util.*;
publicclassTable{
/**
*通过这个数据库对象得到数据库连接
*/

privateDatabasedatabase;
/**
*数据库中一个或多个(只限查询)表的名
*/

privateStringname;
/**
*初始化表对象,此时不作任何数据库相关操作
*一般通过database的getTable调用
*@paramdatabase
*@paramname
*/

publicTable(Databasedatabase,Stringname){
this.database=database;
this.name=name;
}
/**
*查询某一行
*@return
*/

publicRowgetRow(Stringfields,Stringcriteria,Object[]args)throws
DBAccessException{
RowSetrows=executeQuery(fields,criteria,args);
if(rows==null){
returnnull;
}
returnrows.get(0);
}
/**
*得到一个多行记录
*@paramcriteria查询条件
*@paramargs查询条件的参数列表
*@return
*/

publicRowSetgetRows(Stringfields,Stringcriteria,Object[]args)throws
DBAccessException{
returnexecuteQuery(fields,criteria,args);
}
/**
*执行SQL查询
*@paramfields要查询的字段,如果传入null则表示查询表中所有字段
*@paramcriteria用户输入的查询Where条件
*@paramargs用到的参数数组
*@return返回符合结果行集
*/

privateRowSetexecuteQuery(Stringfields,Stringcriteria,Object[]args)throws
DBAccessException{
Connectionconn=null;
RowSetrows=newRowSet();
Stringsql=null;
if(fields==null){
fields="*";
}
try{
conn=database.getConnection();//取得数据库连接,在方法内部对不同的连接情况进行了处理

sql="select"+fields+"from"+name+
((criteria==null)?"":
("where"+criteria));
PreparedStatementpstmt=conn.prepareStatement(sql);
if(args!=null){//如果有查询参数则设置参数

for(inti=0;i<args.length;i++){
pstmt.setObject(i+1,args[i]);
}
}
ResultSetrs=pstmt.executeQuery();
ResultSetMetaDatarsmd=rs.getMetaData();
intcols=rsmd.getColumnCount();
/**@todo判断是否为零*/

if(cols==0){
returnnull;
}
while(rs.next()){
Rowrow=newRow();
for(inti=1;i<=cols;i++){
Stringname=rsmd.getColumnName(i);
Objectvalue=rs.getObject(i);//作通用类型处理,这样row中的类型都是Object型的。

/**
*这里要做空值处理,因为在进行RowToValueObject转换时如果是空值则不能得到值的类型
*所以如果是空值那么把value设置成类型信息
*/

if(value==null){
value=Class.forName(rsmd.getColumnClassName(i));
}
//System.out.println(value.getClass());//用于得到数据库中的类型对应Java中的什么类型

row.put(name,value);
}
rows.add(row);
}
rs.close();
pstmt.close();
}
catch(Exceptionex){
thrownewDBAccessException(InforGeter.getErrorInfor(this,"executeQuery",
ex,"执行SQL("+sql+")查询时出错!"));
}
finally{
database.disConnect(conn);//调用数据库对象的释放连接方法(此方法内对取得连接方式的不同情况做了处理)

}
returnrows;
}
/**
*增加一行
*@paramrow
*/

publicintputRow(Rowrow)throwsDBAccessException{
returnputRow(row,null,null);
}
/**
*修改一行(没有条件就是增加)
*@paramrow
*@paramconditions
*/

publicintputRow(Rowrow,Stringconditions,Object[]args)throws
DBAccessException{
Stringss="";
intaffectableRow=0;//执行SQL后影响的行数

if(conditions==null){
ss="INSERTINTO"+name+"(";
for(inti=0;i<row.length();++i){
Stringk=row.getKey(i);
ss+=k;
if(i!=row.length()-1){
ss+=",";
}
}
ss+=")VALUES(";
for(intj=0;j<row.length();++j){
ss+=(row.get(j)==null)?"null":"?";//如果row中有空值则设置为null,否则设置为查询参数

if(j!=row.length()-1){
ss+=",";
}
}
ss+=")";
}
else{
ss="UPDATE"+name+"SET";
for(inti=0;i<row.length();++i){
Stringk=row.getKey(i);
ss+=k+"="+((row.get(i)==null)?"null":"?");//设置查询参数

if(i!=row.length()-1){
ss+=",";
}
}
ss+="WHERE";
ss+=conditions;
}
Connectionconn=null;
try{
conn=database.getConnection();
PreparedStatementst=conn.prepareStatement(ss);
intj=0;//查询参数计数器

for(inti=0;i<row.length();i++){
if(row.get(i)!=null){//如果不是空则解析查询参数

st.setObject(++j,row.get(i));//解析查询参数
}
}
if(args!=null){
for(inti=0;i<args.length;i++){
st.setObject(++j,args[i]);//预定的规则,null不能放到查询参数中要以name=null的静态形式存放

}
}
affectableRow=st.executeUpdate();
st.close();
}
catch(Exceptionex){
ex.printStackTrace();
thrownewDBAccessException(InforGeter.getErrorInfor(this,"putRow",ex,
"更新表"+name+"中的数据时出错!"));
}
finally{
database.disConnect(conn);
}
returnaffectableRow;
}

/**
*删除一行
*@paramrow
*/

publicintdelRow(Rowrow)throwsDBAccessException{
Stringss="";
intaffectableRow=0;
ss="deletefrom"+name+"where";

for(inti=0;i<row.length();++i){
Stringk=row.getKey(i);
ss+=k+((row.get(i)==null)?"isnull":"=?");//设置查询参数有空值处理

if(i!=row.length()-1){
ss+="and";
}
}
Connectionconn=null;
try{
conn=database.getConnection();
PreparedStatementst=conn.prepareStatement(ss);
intj=0;//查询参数计数器

for(inti=0;i<row.length();i++){
if(row.get(i)!=null){
st.setObject(++j,row.get(i));//解析查询参数

}
}
affectableRow=st.executeUpdate();
st.close();
}
catch(Exceptionex){
thrownewDBAccessException(InforGeter.getErrorInfor(this,"delRow",ex,
"删除表"+name+"中的数据时出错!"));
}
finally{
database.disConnect(conn);
}
returnaffectableRow;
}
/**
*有条件的删除即删除多行
*@paramcondition
*@paramargs
*/

publicintdelRow(Stringcondition,Object[]args)throwsDBAccessException{
Stringss="";
intaffectableRow=0;
ss="deletefrom"+name+"where";
ss+=condition;
Connectionconn=null;
try{
conn=database.getConnection();
PreparedStatementst=conn.prepareStatement(ss);
if(args!=null){
for(inti=0;i<args.length;i++){
st.setObject(i+1,args[i]);
}
}
affectableRow=st.executeUpdate();
st.close();
}
catch(Exceptionex){
thrownewDBAccessException(InforGeter.getErrorInfor(this,"delRow",ex,
"删除表"+name+"中的数据时出错!"));
}
finally{
database.disConnect(conn);
}
returnaffectableRow;
}
}

使用时可以用Database对象的getTable方法传入数据库表的名称来得到一个Table对象。得到这个对象后就可以对这个数据库表进行操作了,这个类提供了六个方法根据传过来的参数对数据库表进行添加修改删除操作。代码中没有特别难懂的地方,需要注意的是我在原有代码的基础上对空值进行的处理,在查询时如果表中的数据是空值的话那么我把字段对应的Java类型放到Row对象里,因为在进行Row对象到值对象的转换时用到了java反射API必须知道Row中的字段值的类型才能去调用值对象的setXXXX方法(见Row对象的toValueObject方法)。
?行对象的源码如下:
packagecom.gdrj.util.database;
importjava.util.*;
importjava.math.BigDecimal;
importjava.lang.reflect.*;
publicclassRow{
/**
*排序,由于Hashtable不提供通过索引取得值的方法,并且其中的键值对也不是按照put上去时的顺序排列的。
*注意:Vector中加入的对象是有序的,即按加入的顺序排列并且能够根据索引访问,可以看成是可变大小的数组
*List可以取代Vector
*/

privateVectorordering=newVector();
/**
*存放键值对(表中字段名称与字段值)
*/

privateHashMapmap=newHashMap();
publicRow(){
}
/**
*向HashMap中追加键值对,即字段名称与字段值
*@paramname
*@paramvalue
*/

publicvoidput(Stringname,Objectvalue){
if(!map.containsKey(name)){
ordering.addElement(name);//将键保存起来

}
map.put(name,value);
}
/**
*得到行对象中字段的个数
*@return
*/

publicintlength(){
returnmap.size();
}
/**
*根据字段名称取得字段值
*@paramname
*@return
*/

publicObjectget(Stringname){
returnmap.get(name);
}
/**
*根据字段在HashMap中的编号取得字段值
*@paramwhich
*@return
*/

publicObjectget(intwhich){
Stringkey=(String)ordering.elementAt(which);
returnmap.get(key);
}
/**
*根据字段序号取得字段名称
*@paramwhich
*@return
*/

publicStringgetKey(intwhich){
Stringkey=(String)ordering.elementAt(which);
returnkey;
}
/**
*打印,用于调试
*/

publicvoiddump(){
for(Iteratore=map.keySet().iterator();e.hasNext();){
Stringname=(String)e.next();
Objectvalue=map.get(name);
System.out.print(name+"="+value+",");
}
System.out.println("");
}
/**
*将行对象转换成值对象
*@paramrow
*@paramtype值对象类型
*@return
*@throwsjava.lang.Exception这里的异常一般在DAO中处理,因为DAO调用
*这个方法进行Row和ValueObject的转换
*/

publicstaticObjecttoValueObject(Rowrow,Classtype)throwsException{
Objectvo=type.newInstance();//创建一个值对象

Field[]fields=type.getDeclaredFields();//得到值对象中所有字段
for(inti=0;i<fields.length;i++){
Stringname=fields[i].getName();//得到JavaBean的字段名

StringnameInRow=toInRowName(name);//在此进行值对象名称到行对象名称的转换
Objectvalue=row.get(nameInRow);//得到从数据库中取出的与字段名对应的值

StringmethodName="set"+Character.toUpperCase(name.charAt(0))+
name.substring(1);//得到setXXXX方法名

ClassargClass=null;
if(valueinstanceofClass){
argClass=(Class)value;
value=null;
}else{
argClass=value.getClass();
}
Methodmethod=type.getMethod(methodName,newClass[]{argClass});//得到set方法

method.invoke(vo,newObject[]{value});//调用setXXXX方法
}
returnvo;
}
/**
*根据传过来的值对象和类型把值对象转换到行对象中
*@paramvo
*@return
*@throwsjava.lang.Exception这里的异常一般在DAO中处理,因为DAO调用
*这个方法进行Row和ValueObject的转换
*/

publicstaticRowfromValueObject(Objectvo)throwsException{
Rowrow=newRow();
Classtype=vo.getClass();//得到Class用于进行反射处理

Field[]fields=type.getDeclaredFields();
for(inti=0;i<fields.length;i++){
Stringname=fields[i].getName();
StringmethodName="get"+Character.toUpperCase(name.charAt(0))+
name.substring(1);
Methodmethod=type.getMethod(methodName,newClass[]{});
Objectvalue=method.invoke(vo,newObject[]{});
StringnameInRow=toInRowName(name);//在此进行值对象中的名称向行对象中的名称转换

row.put(nameInRow,value);
}
returnrow;
}
/**
*将值对象中属性名转换成对应的行对象中的字段名(因为行对象中的字段名
*在更新数据库时必须与数据库表中字段名完全匹配)
*一般规则为fsiId--->fsi_id(现在假设的情况是如果出现有两个单词
*以上的值对象属性名则数据库表中的字段名一定是有下划线的)
*@paramvoName
*@return
*/

publicstaticStringtoInRowName(StringvoName){
StringBuffersb=newStringBuffer();
for(inti=0;i<voName.length();i++){//遍历voName如果有大写字母则将大写字母转换为_加小写

charcur=voName.charAt(i);
if(Character.isUpperCase(cur)){
sb.append("_");
sb.append(Character.toLowerCase(cur));
}
else{
sb.append(cur);
}
}
returnsb.toString();
}
}

Row对象中用了一个HashMap对象存放着对应数据库表中的字段名和对应值,由于Map对象的无序性,所以用了一个vector(当然也可以用List代替)来存放字段名(按用户添加的顺序)这样就可以提供get(inti)方法来顺序取得Map中的值了。要注意的是三个静态辅助方法toValueObject,fromVauleObject,toInRowName。toValueObject方法用于将一个行对象转换为值对象方法中利用了Java的多态和反射机制(请大家参考反射API)。FromValueObject是上一个方法的逆操作,toInRowName方法是实现值对象中的属性名向数据库表中字段名的转换,因为一般在数据库建表时是用的这种形式stu_id,而Java中JavaBean的属性是这样的stuId。
?RowSet的代码如下:
packagecom.gdrj.util.database;
importjava.util.*;
publicclassRowSet{
privateVectorvector=newVector();

publicRowSet(){
}

publicvoidadd(Rowrow){
vector.addElement(row);
}

publicintlength(){
returnvector.size();
}

publicRowget(intwhich){
if(length()<1){
returnnull;
}
else{
return(Row)vector.elementAt(which);
}
}

publicvoiddump(){
for(Enumeratione=vector.elements();e.hasMoreElements();){
((Row)e.nextElement()).dump();
}
}
}

这个类就是把Row对象放到Vector以便操作。就不多说了。
?为了方便使用我写了一个GeneralDAO类(我对DAO模式还在理解中请各位高手批评指教)代码如下:
packagecom.gdrj.util.database;
importjava.util.*;
publicclassGeneralDAO{
/**
*这个DAO对应的表对象
*/

privateTabletable;
/**
*默认构造函数
*/

publicGeneralDAO(){
}

/**
*用数据库对象和表名初始化DAO
*@paramdb
*@paramtableName
*/

publicGeneralDAO(Databasedb,StringtableName){
getTable(db,tableName);
}
privatevoidgetTable(Databasedb,Stringname){
table=db.getTable(name);
}
/**
*根据条件将查找到的数据以值对象集合的形式返回
*@paramfields要查找的字段(*或null表示所有字段)
*@paramcriteria查询条件
*@paramargs与查询条件对应的参数数组
*@paramvoType值对象的类型
*@return
*@throwsjava.lang.Exception
*/

publicCollectionfindDatas(Stringfields,Stringcriteria,Object[]args,
ClassvoType)throwsException{
RowSetrows=table.getRows(fields,criteria,args);
Collectioncol=newArrayList();
for(inti=0;i<rows.length();i++){
Objectvo=Row.toValueObject(rows.get(i),voType);//返回一个值对象,注意是voType类型的对象

col.add(vo);
}
returncol;
}

/**
*向表中插入一条数据
*@paramvo与表对象对应的值对象
*@return
*@throwsjava.lang.Exception
*/

publicintinsertData(Objectvo)throwsException{
returntable.putRow(Row.fromValueObject(vo));
}

/**
*更新一条数据
*@paramvo与表对象对应的值对象
*@paramcriteria更新条件
*@paramargs与更新条件对应的参数数组
*@return
*@throwsjava.lang.Exception
*/

publicintupdateData(Objectvo,Stringcriteria,Object[]args)throws
Exception{
returntable.putRow(Row.fromValueObject(vo),criteria,args);
}

/**
*删除一条数据(条件比较严格各个字段的值必须与值对象中属性的值匹配)
*@paramvo
*@return
*@throwsjava.lang.Exception
*/

publicintdeleteData(Objectvo)throwsException{
returntable.delRow(Row.fromValueObject(vo));
}
/**
*删除多条数据
*@paramcondition删除条件
*@paramargs与条件对应的参数数组
*@return
*@throwsjava.lang.Exception
*/

publicintdeleteDatas(Stringcondition,Object[]args)throwsException{
returntable.delRow(condition,args);
}
}

这个DAO类是对Table类的一个方便的封装。用户如果向操作数据库只要创建一个Database对象,一个DAO对象,一个值对象(对应表结构),然后就可以进行方便的数据库操作了,下面给出一个实例来演示这个小小框架的用法。
演示程序
首先建立一个teacher表,语法如下
createtableteacher(
idintnotnull,
namevarchar(20)notnull,
birthdaysmalldatetimenull,
addressvarchar(100)null,
incomemoneynull,
constraintidPRIMARYKEYNONCLUSTERED(id)
)
然后建立一个与teacher表对应的值对象类。
publicclassTeacherVOimplementsSerializable{
privateIntegerid;
privateStringname;
privateStringaddress;
privateBigDecimalincome;
privatejava.sql.Timestampbirthday;
publicTeacherVO(){
}
publicIntegergetId(){
returnid;
}
publicvoidsetId(Integerid){
this.id=id;
}
publicStringgetName(){
returnname;
}
publicvoidsetName(Stringname){
this.name=name;
}
publicjava.sql.TimestampgetBirthday(){
returnbirthday;
}
publicvoidsetBirthday(java.sql.Timestampbirthday){
this.birthday=birthday;
}
publicStringgetAddress(){
returnaddress;
}
publicvoidsetAddress(Stringaddress){
this.address=address;
}
publicjava.math.BigDecimalgetIncome(){
returnincome;
}
publicvoidsetIncome(java.math.BigDecimalincome){
this.income=income;
}
publicStringtoString(){
return"编号:"+id+"姓名:"+name+"生日:"+birthday
+"地址:"+address+"收入:"+income;
}
}

最后主程序的源码如下:
packageorg.together.jdbcwrap.test;
importjava.util.*;
importcom.gdrj.util.database.*;

publicclassGeneralDAOExample{
publicstaticvoidmain(String[]args)throwsException{
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Databasedb=newDatabase("jdbc:odbc:emmis","sa","815023");
GeneralDAOdao=newGeneralDAO(db,"teacher");
/**
*利用GeneralDAO进行查询
*/

Collectioncol=dao.findDatas("*","birthdayisnull",null,TeacherVO.class);
for(Iteratoriter=col.iterator();iter.hasNext();){
Objectitem=iter.next();
System.out.println("item="+item);
}
/**
*利用GeneralDAO进行添加
*/

TeacherVOvo=newTeacherVO();
vo.setAddress("沈阳");
vo.setBirthday(newjava.sql.Timestamp(0));
vo.setId(newInteger(11));
vo.setIncome(newjava.math.BigDecimal(1000));
vo.setName("陶小川");
//dao.insertData(vo);//添加一条记录

//dao.updateData(vo,"id=10",null);//更新一条记录
//dao.deleteData(vo);//删除一条记录
//dao.deleteDatas("id>5",null);//添加符合条件记录
}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值