前言 这个小小的数据库操作封装框架是参考IBM开发网上的两篇文章并在其基础上扩充了一些功能而得到的。所以首先要感谢两篇文章的作者。
学习JDBC以来一直想实现一个简单的封装来方便编程但是由于水平有限一直没有较好的办法,看了IBM开发网上的两篇文章以后感觉作者的设计思想很好一定能扩充成一个实用的JDBC封装。所以我在文章提供的源码基础上加了一些功能这些功能包括支持多种数据类型,处理了空值,利用反射方便的在Row对象和值对象之间进行转换,还有加了一个我自认为通用的DAO类来方便用户的操作。
我把源码提供出来有两个目的一个是希望能帮助比我还初学的初学者熟悉JDBC,另外就是请各位高手不吝赐教,改进程序中的错误如果能将你们的对JDBC的封装方法提供出来那就更好了(不要说你们只用EJB或者Hibernate,JDO什么的?)。
IBM开发网的那两篇文章分别是《一个简单的 JDBC 包装器》《对一个简单的 JDBC 包装器的扩展及应用》,我的邮箱是
[email protected]有事请与我联系。
设计思想
把DBMS抽象成类Database,这个类负责管理数据库连接以及提供表对象。
把数据库中的一张或多张表抽象成类Table,这个类中提供对表的添加,修改,删除的JDBC封装。
将数据库表中的一条记录抽象成类Row,这个类用HashMap保存关系数据库中表格中一行数据的字段名和值并提供一些相关操作。另外这个类还提供了两个静态方法用于在Row对象和ValueObject之间进行方便的转换。
把对个Row的集合抽象成RowSet,这个类中用一个vector把多个Row对象保存起来并提供一些相关操作。
代码分析
由于已经给出源码所以我只对代码中关键的和需要注意的地方加以说明,大家可以执行源码一边演示一边体会。
Database类源码如下: package com.gdrj.util.database;
import java.sql.*;
import javax.sql.*;
import com.gdrj.util.servicelocator.*;
public class Database {
/**
* 这个数据库连接成员只有在与数据库直接建立连接的情况下是有效的
*/
private Connection conn = null;
/**
* 当这个参数有效时,表明程序是直接与数据库建立的连接而不是从连接池里取得连接
*/
private String url, user, password;
/**
* 当这个参数有效时,表明程序是从连接池里取得连接。
*/
private String datasource;
/**
* 用数据库地址,用户名,密码初始化数据库对象,这个构造器用于程序是直接
* 与数据库建立连接的情况。
* @param url
* @param user
* @param password
*/
public Database(String url, String user, String password) {
this.url = url;
this.user = user;
this.password = password;
}
/**
* 用JNDI数据源名初始化数据库对象,这个构造器用于从连接池取数据库连接的情况。
* @param datasource
*/
public Database(String datasource) {
this.datasource = datasource;
}
/**
* 得到数据库连接,对于是否从连接池里取连接做了自动处理即根据用户调用了哪个构造器
* 来判断是否直接与数据库建立连接还是从连接池里取连接。
* 对于用户来说不用考虑程序是从那里取得连接,他只管正确的初始化数据库对象。
* @return
* @throws SQLException
*/
public Connection getConnection() throws Exception {
if (datasource == null) { //直接与数据库建立连接
if (conn == null) {
conn = DriverManager.getConnection(url, user, password);
}
}
else { //从应用服务器的连接池里取得连接
ServiceLocator sl = ServiceLocator.getInstance();
DataSource ds = sl.getDataSource(datasource);
return ds.getConnection();//每调用一次都返回一个连接池中的数据库连接
}
return conn;
}
/**
* 释放连接,如果是直接与数据库连接的情况则什么也不做
* 如果是从连接池中取得的连接那么释放传来的连接
* @param conn
*/
public void disConnect(Connection connection) {
if (datasource != null) { //只处理从连接池取连接的情况
try {
if (connection != null) {
connection.close();
}
}
catch (Exception ex) {}
}
}
/**
* 得到与参数名对应的表对象,注意这里不作任何数据库操作
* @param name
* @return
*/
public Table getTable(String name) {
return new Table(this, name);
}
}
这个类是对DBMS的抽象,所以使用时应用程序中只要有一个Database对象就够了,如果你是以与数据库之间建立连接的方式使用那么你用Database(String url, String user, String password)构造器进行初始化。如果是从应用服务器的连接池中取得连接的方式使用那么用Database(String datasource)构造器初始化,这样以后你使用这个对象进行getConnection和disConnection时就不用去考虑始终保持一个连接(C/S方式),还是将连接返回连接池了因为在disConnection中已经做了处理。集体使用方法将Table类。在getConnection中的从连接池中取连接的代码你只要参考以下《J2EE核心模式》中的服务定位器模式就知道是怎么回事了,你在用Database(String url, String user, String password)初始化时其中的代码不起作用。
? Table类源码如下:
package com.gdrj.util.database;
import java.sql.*;
import java.util.*;
import com.gdrj.util.*;
public class Table {
/**
* 通过这个数据库对象得到数据库连接
*/
private Database database;
/**
* 数据库中一个或多个(只限查询)表的名
*/
private String name;
/**
* 初始化表对象,此时不作任何数据库相关操作
* 一般通过database的getTable调用
* @param database
* @param name
*/
public Table(Database database, String name) {
this.database = database;
this.name = name;
}
/**
* 查询某一行
* @return
*/
public Row getRow(String fields, String criteria, Object[] args) throws
DBAccessException {
RowSet rows = executeQuery(fields, criteria, args);
if (rows == null) {
return null;
}
return rows.get(0);
}
/**
* 得到一个多行记录
* @param criteria 查询条件
* @param args 查询条件的参数列表
* @return
*/
public RowSet getRows(String fields, String criteria, Object[] args) throws
DBAccessException {
return executeQuery(fields, criteria, args);
}
/**
* 执行SQL查询
* @param fields 要查询的字段,如果传入null则表示查询表中所有字段
* @param criteria用户输入的查询Where条件
* @param args 用到的参数数组
* @return 返回符合结果行集
*/
private RowSet executeQuery(String fields, String criteria, Object[] args) throws
DBAccessException {
Connection conn = null;
RowSet rows = new RowSet();
String sql = null;
if (fields == null) {
fields = "*";
}
try {
conn = database.getConnection(); //取得数据库连接,在方法内部对不同的连接情况进行了处理
sql = "select " + fields + " from " + name +
( (criteria == null) ? "" :
(" where " + criteria));
PreparedStatement pstmt = conn.prepareStatement(sql);
if (args != null) { //如果有查询参数则设置参数
for (int i = 0; i < args.length; i++) {
pstmt.setObject(i + 1, args[i]);
}
}
ResultSet rs = pstmt.executeQuery();
ResultSetMetaData rsmd = rs.getMetaData();
int cols = rsmd.getColumnCount();
/**@todo 判断是否为零*/
if (cols == 0) {
return null;
}
while (rs.next()) {
Row row = new Row();
for (int i = 1; i <= cols; i++) {
String name = rsmd.getColumnName(i);
Object value = rs.getObject(i); //作通用类型处理,这