快捷搜索:

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

媒介

这个小小的数据库操作封装框架是参考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类源码如下:

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); //作通用类型处置惩罚,这样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 (Exception ex) {

throw new DBAccessException(InforGeter.getErrorInfor(this, "executeQuery",

ex, "履行SQL(" + sql + ")查询时掉足!"));

}

finally {

database.disConnect(conn); //调用数据库工具的开释连接措施(此措施内对取得连接要领的不合环境做了处置惩罚)

}

return rows;

}

/**

* 增添一行

* @param row

*/

public int putRow(Row row) throws DBAccessException {

return putRow(row, null, null);

}

/**

* 改动一行(没有前提便是增添)

* @param row

* @param conditions

*/

public int putRow(Row row, String conditions, Object[] args) throws

DBAccessException {

String ss = "";

int affectableRow = 0; //履行SQL后影响的行数

if (conditions == null) {

ss = "INSERT INTO " + name + "(";

for (int i = 0; i < row.length(); ++i) {

String k = row.getKey(i);

ss += k;

if (i != row.length() - 1) {

ss += ", ";

}

}

ss += ") VALUES (";

for (int j = 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 (int i = 0; i < row.length(); ++i) {

String k = row.getKey(i);

ss += k + "=" + ( (row.get(i) == null) ? "null" : "?"); //设置查询参数

if (i != row.length() - 1) {

ss += ", ";

}

}

ss += " WHERE ";

ss += conditions;

}

Connection conn = null;

try {

conn = database.getConnection();

PreparedStatement st = conn.prepareStatement(ss);

int j = 0; //查询参数计数器

for (int i = 0; i < row.length(); i++) {

if (row.get(i) != null) { //假如不是空则解析查询参数

st.setObject(++j, row.get(i)); //解析查询参数

}

}

if (args != null) {

for (int i = 0; i < args.length; i++) {

st.setObject(++j, args[i]);//预定的规则,null不能放到查询参数中要以name=null的静态形式寄放

}

}

affectableRow = st.executeUpdate();

st.close();

}

catch (Exception ex) {

ex.printStackTrace();

throw new DBAccessException(InforGeter.getErrorInfor(this, "putRow", ex,

"更新表" + name + "中的数据时掉足!"));

}

finally {

database.disConnect(conn);

}

return affectableRow;

}

/**

* 删除一行

* @param row

*/

public int delRow(Row row) throws DBAccessException {

String ss = "";

int affectableRow = 0;

ss = "delete from " + name + " where ";

for (int i = 0; i < row.length(); ++i) {

String k = row.getKey(i);

ss += k + ((row.get(i) == null)?" is null":"=?"); //设置查询参数有空值处置惩罚

if (i != row.length() - 1) {

ss += " and ";

}

}

Connection conn = null;

try {

conn = database.getConnection();

PreparedStatement st = conn.prepareStatement(ss);

int j = 0;//查询参数计数器

for (int i = 0; i < row.length(); i++) {

if (row.get(i) != null) {

st.setObject(++j, row.get(i)); //解析查询参数

}

}

affectableRow = st.executeUpdate();

st.close();

}

catch (Exception ex) {

throw new DBAccessException(InforGeter.getErrorInfor(this, "delRow", ex,

"删除表" + name + "中的数据时掉足!"));

}

finally {

database.disConnect(conn);

}

return affectableRow;

}

/**

* 有前提的删除即删除多行

* @param condition

* @param args

*/

public int delRow(String condition, Object[] args) throws DBAccessException {

String ss = "";

int affectableRow = 0;

ss = "delete from " + name + " where ";

ss += condition;

Connection conn = null;

try {

conn = database.getConnection();

PreparedStatement st = conn.prepareStatement(ss);

if (args != null) {

for (int i = 0; i < args.length; i++) {

st.setObject(i + 1, args[i]);

}

}

affectableRow = st.executeUpdate();

st.close();

}

catch (Exception ex) {

throw new DBAccessException(InforGeter.getErrorInfor(this, "delRow", ex,

"删除表" + name + "中的数据时掉足!"));

}

finally {

database.disConnect(conn);

}

return affectableRow;

}

}

应用时可以用Database工具的getTable措施传入数据库表的名称来获得一个Table工具。获得这个工具后就可以对这个数据库表进行操作了,这个类供给了六个措施根据传过来的参数对数据库表进行添加改动删除操作。代码中没有分外难解的地方,必要留意的是我在原有代码的根基上对空值进行的处置惩罚,在查询时假如表中的数据是空值的话那么我把字段对应的Java类型放到Row工具里,由于在进行Row工具到值工具的转换时用到了java反射API必须知道Row中的字段值的类型才能去调用值工具的setXXXX措施(见Row工具的toValueObject措施)。

 行工具的源码如下:

package com.gdrj.util.database;

import java.util.*;

import java.math.BigDecimal;

import java.lang.reflect.*;

public class Row {

/**

* 排序,因为Hashtable不供给经由过程索引取得值的措施,并且此中的键值对也不是按照put上去时的顺序排列的。

* 留意:Vector中加入的工具是有序的,即按加入的顺序排列并且能够根据索引造访,可以当作是可变大年夜小的数组

* List可以取代Vector

*/

private Vector ordering = new Vector();

/**

* 寄放键值对(表中字段名称与字段值)

*/

private HashMap map = new HashMap();

public Row() {

}

/**

* 向HashMap中追加键值对,即字段名称与字段值

* @param name

* @param value

*/

public void put(String name, Object value) {

if (!map.containsKey(name)) {

ordering.addElement(name); //将键保存起来

}

map.put(name, value);

}

/**

* 获得行工具中字段的个数

* @return

*/

public int length() {

return map.size();

}

/**

* 根据字段名称取得字段值

* @param name

* @return

*/

public Object get(String name) {

return map.get(name);

}

/**

* 根据字段在HashMap中的编号取得字段值

* @param which

* @return

*/

public Object get(int which) {

String key = (String) ordering.elementAt(which);

return map.get(key);

}

/**

* 根据字段序号取得字段名称

* @param which

* @return

*/

public String getKey(int which) {

String key = (String) ordering.elementAt(which);

return key;

}

/**

* 打印,用于调试

*/

public void dump() {

for (Iterator e = map.keySet().iterator(); e.hasNext(); ) {

String name = (String) e.next();

Object value = map.get(name);

System.out.print(name + "=" + value + ", ");

}

System.out.println("");

}

/**

* 将行工具转换成值工具

* @param row

* @param type值工具类型

* @return

* @throws java.lang.Exception 这里的非常一样平常在DAO中处置惩罚,由于DAO调用

* 这个措施进行Row和ValueObject的转换

*/

public static Object toValueObject(Row row, Class type) throws Exception {

Object vo = type.newInstance(); //创建一个值工具

Field[] fields = type.getDeclaredFields(); //获得值工具中所有字段

for (int i = 0; i < fields.length; i++) {

String name = fields[i].getName(); //获得JavaBean的字段名

String nameInRow = toInRowName(name);//在此进行值工签字称到行工签字称的转换

Object value = row.get(nameInRow); //获得从数据库中掏出的与字段名对应的值

String methodName = "set" + Character.toUpperCase(name.charAt(0)) +

name.substring(1); //获得setXXXX措施名

Class argClass = null;

if (value instanceof Class) {

argClass = (Class)value;

value = null;

}else{

argClass = value.getClass();

}

Method method = type.getMethod(methodName, new Class[] {argClass}); //获得set措施

method.invoke(vo, new Object[] {value});//调用setXXXX措施

}

return vo;

}

/**

* 根据传过来的值工具和类型把值工具转换到行工具中

* @param vo

* @return

* @throws java.lang.Exception 这里的非常一样平常在DAO中处置惩罚,由于DAO调用

* 这个措施进行Row和ValueObject的转换

*/

public static Row fromValueObject(Object vo) throws Exception {

Row row = new Row();

Class type = vo.getClass(); //获得Class用于进行反射处置惩罚

Field[] fields = type.getDeclaredFields();

for (int i = 0; i < fields.length; i++) {

String name = fields[i].getName();

String methodName = "get" + Character.toUpperCase(name.charAt(0)) +

name.substring(1);

Method method = type.getMethod(methodName, new Class[] {});

Object value = method.invoke(vo, new Object[] {});

String nameInRow = toInRowName(name);//在此进行值工具中的名称向行工具中的名称转换

row.put(nameInRow, value);

}

return row;

}

/**

* 将值工具中属性名转换成对应的行工具中的字段名(由于行工具中的字段名

* 在更新数据库时必须与数据库表中字段名完全匹配)

* 一样平常规则为fsiId ---> fsi_id(现在假设的环境是假如呈现有两个单词

* 以上的值工具属性名则数据库表中的字段名必然是有下划线的)

* @param voName

* @return

*/

public static String toInRowName(String voName) {

StringBuffer sb = new StringBuffer();

for (int i = 0; i < voName.length(); i++) { //遍历voName假如有大年夜写字母则将大年夜写字母转换为_加小写

char cur = voName.charAt(i);

if (Character.isUpperCase(cur)) {

sb.append("_");

sb.append(Character.toLowerCase(cur));

}

else {

sb.append(cur);

}

}

return sb.toString();

}

}

Row工具顶用了一个HashMap工具寄放着对应数据库表中的字段名和对应值,因为Map工具的无序性,以是用了一个vector(当然也可以用List代替)来寄放字段名(按用户添加的顺序)这样就可以供给get(int i)措施来顺序取得Map中的值了。要留意的是三个静态帮助措施toValueObject,fromVauleObject,toInRowName。toValueObject措施用于将一个行工具转换为值工具措施中使用了Java的多态和反射机制(请大年夜家参考反射API)。FromValueObject是上一个措施的逆操作,toInRowName措施是实现值工具中的属性名向数据库表中字段名的转换,由于一样平常在数据库建表时是用的这种形式stu_id,而Java中JavaBean的属性是这样的stuId。

 RowSet的代码如下:

package com.gdrj.util.database;

import java.util.*;

public class RowSet {

private Vector vector = new Vector();

public RowSet() {

}

public void add(Row row) {

vector.addElement(row);

}

public int length() {

return vector.size();

}

public Row get(int which) {

if (length() < 1) {

return null;

}

else {

return (Row) vector.elementAt(which);

}

}

public void dump() {

for (Enumeration e = vector.elements(); e.hasMoreElements(); ) {

( (Row) e.nextElement()).dump();

}

}

}

这个类便是把Row工具放到Vector以便操作。就不多说了。

 为了方便应用我写了一个GeneralDAO类(我对DAO模式还在理解中请各位高手品评指教)代码如下:

package com.gdrj.util.database;

import java.util.*;

public class GeneralDAO {

/**

* 这个DAO对应的表工具

*/

private Table table;

/**

* 默认构造函数

*/

public GeneralDAO() {

}

/**

* 用数据库工具和表名初始化DAO

* @param db

* @param tableName

*/

public GeneralDAO(Database db, String tableName) {

getTable(db, tableName);

}

private void getTable(Database db, String name) {

table = db.getTable(name);

}

/**

* 根据前提将查找到的数据以值工具聚拢的形式返回

* @param fields 要查找的字段(*或null表示所有字段)

* @param criteria查询前提

* @param args与查询前提对应的参数数组

* @param voType值工具的类型

* @return

* @throws java.lang.Exception

*/

public Collection findDatas(String fields, String criteria, Object[] args,

Class voType) throws Exception {

RowSet rows = table.getRows(fields, criteria, args);

Collection col = new ArrayList();

for (int i = 0; i < rows.length(); i++) {

Object vo = Row.toValueObject(rows.get(i), voType); //返回一个值工具,留意是voType类型的工具

col.add(vo);

}

return col;

}

/**

* 向表中插入一条数据

* @param vo 与表工具对应的值工具

* @return

* @throws java.lang.Exception

*/

public int insertData(Object vo) throws Exception {

return table.putRow(Row.fromValueObject(vo));

}

/**

* 更新一条数据

* @param vo 与表工具对应的值工具

* @param criteria更新前提

* @param args与更新前提对应的参数数组

* @return

* @throws java.lang.Exception

*/

public int updateData(Object vo, String criteria, Object[] args) throws

Exception {

return table.putRow(Row.fromValueObject(vo), criteria, args);

}

/**

* 删除一条数据(前提对照严格各个字段的值必须与值工具中属性的值匹配)

* @param vo

* @return

* @throws java.lang.Exception

*/

public int deleteData(Object vo) throws Exception {

return table.delRow(Row.fromValueObject(vo));

}

/**

* 删除多条数据

* @param condition 删除前提

* @param args与前提对应的参数数组

* @return

* @throws java.lang.Exception

*/

public int deleteDatas(String condition, Object[] args) throws Exception {

return table.delRow(condition, args);

}

}

这个DAO类是对Table类的一个方便的封装。用户假如向操作数据库只要创建一个Database工具,一个DAO工具,一个值工具(对应表布局),然后就可以进行方便的数据库操作了,下面给出一个实例来演示这个小小框架的用法。

演示法度榜样

首先建立一个teacher表,语法如下

create table teacher (

idint not null,

namevarchar(20) not null,

birthdaysmalldatetimenull,

address varchar(100)null,

incomemoneynull,

constraint id PRIMARY KEYNONCLUSTERED ( id )

)

然后建立一个与teacher表对应的值工具类。

public class TeacherVO implements Serializable {

private Integer id;

private String name;

private String address;

private BigDecimal income;

private java.sql.Timestamp birthday;

public TeacherVO() {

}

public Integer getId() {

return id;

}

public void setId(Integer id) {

this.id = id;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public java.sql.Timestamp getBirthday() {

return birthday;

}

public void setBirthday(java.sql.Timestamp birthday) {

this.birthday = birthday;

}

public String getAddress() {

return address;

}

public void setAddress(String address) {

this.address = address;

}

public java.math.BigDecimal getIncome() {

return income;

}

public void setIncome(java.math.BigDecimal income) {

this.income = income;

}

public String toString(){

return " 编号:" + id + " 姓名:" + name + " 生日:" + birthday

+ " 地址:" + address + " 收入:" + income;

}

}

着末主法度榜样的源码如下:

package org.together.jdbcwrap.test;

import java.util.*;

import com.gdrj.util.database.*;

public class GeneralDAOExample {

public static void main(String[] args)throws Exception {

Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");

Database db = new Database("jdbc:odbc:emmis","sa","815023");

GeneralDAO dao = new GeneralDAO(db,"teacher");

/**

* 使用GeneralDAO进行查询

*/

Collection col = dao.findDatas("*","birthday is null",null,TeacherVO.class);

for (Iterator iter = col.iterator(); iter.hasNext(); ) {

Object item = iter.next();

System.out.println("item = " + item);

}

/**

* 使用GeneralDAO进行添加

*/

TeacherVO vo = new TeacherVO();

vo.setAddress("沈阳");

vo.setBirthday(new java.sql.Timestamp(0));

vo.setId(new Integer(11));

vo.setIncome(new java.math.BigDecimal(1000));

vo.setName("陶小川");

//dao.insertData(vo);//添加一笔记录

//dao.updateData(vo,"id=10",null); //更新一笔记录

//dao.deleteData(vo); //删除一笔记录

//dao.deleteDatas("id>5",null); //添加相符前提记录

}

}

您可能还会对下面的文章感兴趣: