业务背景:系统中所有实体对象都涉及到基本的CRUD操作:
所有实体的CUD操作代码基本相同,仅仅发送给数据库的SQL语句不同而已,因此可以把CUD操作的所有相同代码抽取到工具类的一个update方法中,并定义参数接收变化的SQL语句。
实体的R操作,除SQL语句不同之外,根据操作的实体不同,对ResultSet的映射也各不相同,因此可义一个query方法,除以参数形式接收变化的SQL语句外,可以使用策略模式由qurey方法的调用者决定如何把ResultSet中的数据映射到实体对象中。(Bean,BeanList,Long)
String columnName = md.getColumnName(i);
Object columnData = rs.getObject(i);
Field feild = clazz.getDeclaredField(columnName);
feild.setAccessible(true);
feild.set(obj, columnData);
通过对元数据的封装,实现如下功能:
1.支持任意数据库连接池
2.支持一个方法完成增,删,改操作
3.支持查询操作直接返回JavaBean对象.
思路:
1.定义一个核心类,内部维护DataSource接口,用于接收任意连接池对象.
2.在核心类中暴露一个update方法,接收sql语句,和占位符参数,完成增删改的操作.
3.在核心类中暴露一个query方法,返回一个javabean的List集合,接收sql语句,占位符参数和处理ResultSet的Handler,Handler处理完结果后返回给query方法.query方法可以定义为泛型方法,以支持返回不同bean类型的List集合.
项目结构如下:
Account.java
package bean;
public class Account {
private int id;
private String name;
private double money;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getMeney() {
return money;
}
public void setMeney(double meney) {
this.money = meney;
}
@Override
public String toString() {
return "Account [id=" + id + ", name=" + name + ", meney=" + money + "]";
}
}
DBHelper.java
package core;
import java.sql.Connection;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.List;
import javax.sql.DataSource;
public class DBHelper {
private DataSource ds;
public DBHelper(DataSource ds) {
this.ds = ds;
}
// 查询结果集处理
public interface QueryHandler<T> {
T handler(ResultSet rs);
}
/**
* 增删改操作
*
* @param sql
* @param args
*/
public void update(String sql, Object... args) {
Connection conn = null;
PreparedStatement stmt = null;
try {
// 获得连接池中的连接对象
conn = ds.getConnection();
// 得到PreparedStatement对象
stmt = conn.prepareStatement(sql);
// 获取参数data
ParameterMetaData data = stmt.getParameterMetaData();
// 获取参数占位符个数
int count = data.getParameterCount();
// 有占位符
if (count > 0) {
if (null == args) {
throw new IllegalArgumentException("找不到对应的占位符参数");
}
if (count != args.length) {
throw new IllegalArgumentException("占位符与参数个数不匹配");
}
// 替换占位符
for (int i = 0; i < count; i++) {
stmt.setObject(i + 1, args[i]);
}
}
// 执行增、删、改方法
stmt.executeUpdate();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭资源
release(null, stmt, conn);
}
}
/**
* 查询操作
*
* @param sql
* @param qh
* @param args
* @return
*/
public <T> List<T> query(String sql, QueryHandler<List<T>> qh, Object... args) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
// 获得连接池中的连接对象
conn = ds.getConnection();
// 得到PreparedStatement对象
stmt = conn.prepareStatement(sql);
// 获取参数data
ParameterMetaData data = stmt.getParameterMetaData();
// 获取参数占位符个数
int count = data.getParameterCount();
// 有占位符
if (count > 0) {
if (null == args) {
throw new IllegalArgumentException("找不到对应的占位符参数");
}
if (count != args.length) {
throw new IllegalArgumentException("占位符与参数个数不匹配");
}
// 替换占位符
for (int i = 0; i < count; i++) {
stmt.setObject(i + 1, args[i]);
}
}
// 执行查询
rs = stmt.executeQuery();
// 处理查询结果并返回
return qh.handler(rs);
} catch (SQLException e) {
e.printStackTrace();
} finally {
release(rs, stmt, conn);
}
return null;
}
/**
* 释放资源
*
* @param rs
* @param stmt
* @param conn
*/
public void release(ResultSet rs, PreparedStatement stmt, Connection conn) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
rs = null;
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
stmt = null;
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
conn = null;
}
}
}
DefaultQueryHandler.java
package core;
import java.lang.reflect.Field;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import core.DBHelper.QueryHandler;
/**
* 默认的结果集处理类
*
* @author mChenys
*
* @param <T>
*/
public class DefaultQueryHandler<T> implements QueryHandler<List<T>> {
private Class<T> clazz;
public DefaultQueryHandler(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public List<T> handler(ResultSet rs) {
// 定义一个list集合
List<T> list = new ArrayList<T>();
try {
// 获得结果集的元数据MetaData
ResultSetMetaData data = rs.getMetaData();
// 获得结果集中的列数
int count = data.getColumnCount();
// 声明javabean对象
T bean = null;
// 遍历查询的每一条记录,将其封装到具体的javabean对象中
while (rs.next()) {
// 通过要封装数据的javabean的字节码对象创建一个实例
bean = this.clazz.newInstance();
// 遍历该条记录下的所有列
for (int i = 0; i < count; i++) {
// 拿到当前列字段的值
Object value = rs.getObject(i + 1);
// 拿到当前列的字段名字
String name = data.getColumnName(i + 1);
// 根据数据库的字段名获得javabean中对应的字段
Field field = this.clazz.getDeclaredField(name);
// 设置javabean对象的private字段的可访问权限
field.setAccessible(true);
// 设置字段的值到javabean对象中
field.set(bean, value);
}
// 将封装一条完整查询数据的javabean对象添加的list集合中
list.add(bean);
}
} catch (Exception e) {
e.printStackTrace();
}
// 5.将封装了多条数据的list集合返回
return list;
}
}
_Test.java
package test;
import java.util.List;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import bean.Account;
import core.DBHelper;
import core.DefaultQueryHandler;
import core.DBHelper.QueryHandler;
public class _Test {
//创建DBHelper类
private static DBHelper dbHelper = new DBHelper(new ComboPooledDataSource());;
public static void main(String[] args) {
testInsert();
// testDelete();
// testUpdate();
// testQuery();
}
//测试增加
private static void testInsert() {
dbHelper.update("insert into account values(null,'小陈',1000)", null);
}
//测试删除
private static void testDelete() {
dbHelper.update("delete from account where money < ?", "1000");
}
//测试更新
private static void testUpdate() {
dbHelper.update("update account set money= money+1000 where name= ?", "小陈");
}
//测试查询
private static void testQuery() {
QueryHandler<List<Account>> qh = new DefaultQueryHandler<Account>(Account.class);
List<Account> list = dbHelper.query("select * from account where money > ?", qh, "200");
for (Account a : list) {
System.out.println(a);
}
}
}