我们其实还可以将JDBC工具类进行重构,因为增删改我们是不需要返回结果集的,只有查询才返回结果集,用户得到想要的数据再进行处理。我们首先来重构增删改的功能。
1、将增删改封装成一个update方法
private DataSource dataSouce;
public DBAssitst(DataSource dataSource) {
this.dataSouce = dataSource;
}
public void update(String sql, Object... params) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
conn = dataSouce.getConnection();
stmt = conn.prepareStatement(sql);
ParameterMetaData pmd = stmt.getParameterMetaData();
int num = pmd.getParameterCount();
if (num > 0) {
if (params == null) {
throw new IllegalArgumentException("有占位符,但没有传入具体的参数值");
}
if (num != params.length) {
throw new IllegalArgumentException("参数个数不匹配");
}
for (int i = 0; i < num; i++) {
stmt.setObject(i + 1, params[i]);
}
}
stmt.execute();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
release(rs, stmt, conn);
}
2、查询方法
public Object query(String sql,ResultSetHandler handler,Object...params) {
Connection conn = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try{
conn = dataSouce.getConnection();
stmt = conn.prepareStatement(sql);
//设置参数
ParameterMetaData pmd = stmt.getParameterMetaData();
int num = pmd.getParameterCount();//得到sql语句中的参数个数
if(num>0){
if(params==null){
throw new IllegalArgumentException("有占位符,但没有传入具体的参数值");
}
if(num!=params.length){
throw new IllegalArgumentException("参数个数不匹配");
}
for(int i=0;i<num;i++){
stmt.setObject(i+1, params[i]);
}
}
rs = stmt.executeQuery();//得到结果集对象。具体怎么封装:谁用谁知道
return handler.handle(rs);//转化结果集
}catch(Exception e){
throw new RuntimeException(e);
}finally{
release(rs, stmt, conn);
}
}
ResultSetHandler是一个接口,此接口有一个方法来处理结果集,因为我们可以只是查询一条记录(返回一个实体队形)我们还可以查询多条记录(返回多个实体对象)。现在我们用策略设计模式来实现此功能。
2.1、定义一个接口
public interface ResultSetHandler {
Object handle(ResultSet rs);
}
public interface ResultSetHandler {
Object handle(ResultSet rs);
}
2.2、实现查询一条记录的handelr
public class BeanHandler implements ResultSetHandler {
private Class clazz;
public BeanHandler(Class clazz){
this.clazz=clazz;
}
public Object handle(ResultSet rs) {
try {
if (rs.next()) {
Object bean =clazz.newInstance();
ResultSetMetaData rsmd=rs.getMetaData();
int num=rsmd.getColumnCount();//列数
for (int i = 0; i < num; i++) {
String fieldName=rsmd.getColumnName(i+1);
Object fieldValue=rs.getObject(i+1);
//反射字段
Field f=clazz.getDeclaredField(fieldName);
f.setAccessible(true);
f.set(bean, fieldValue);
}
return bean;
}else{
return null;
}
} catch (Exception e) {
throw new RuntimeException("封装数据失败");
}
}
}
2.3 实现查询多条记录的hanlder
public class BeanListHandler implements ResultSetHandler {
private Class clazz;// 目标类型
public BeanListHandler(Class clazz) {
this.clazz = clazz;
}
public Object handle(ResultSet rs) {
try {
List list = new ArrayList();
while (rs.next()) {
Object bean = clazz.newInstance();
ResultSetMetaData rsmd = rs.getMetaData();
int num = rsmd.getColumnCount();
for (int i = 0; i < num; i++) {
String fildName = rsmd.getColumnName(i + 1);
Object fieldValue = rs.getObject(i + 1);
Field f = clazz.getDeclaredField(fildName);
f.setAccessible(true);
f.set(bean, fieldValue);
}
list.add(bean);
}
return list;
} catch (Exception e) {
throw new RuntimeException("封装数据失败");
}
}
}
实体类的代码我就不写了,其实这两个handler利用数据库的元数据MetaData和反射机制完成的,注意,我们封装的实体类中的成员变量必须能和数据库中的字段一一对应,要不利用反射字段的方法就泡汤了,因为我们通过结果集获取每一个字段的名字,反射技术再通过每一个字段的名字得到每一个字段的实例,然后再为此字段赋值,当然了,前提条件是得到此对象的字节码文件之后才能进行。
测试方法代码
@Test
public void testFindOne(){
Account a = (Account)da.query("select * from account where id=?", new BeanHandler(Account.class),6);
System.out.println(a);
}
@Test
public void testFindAll(){
List list =(List) da.query("select * from account", new BeanListHandler(Account.class));
for(Object o:list)
System.out.println(list);
}
3.0、小结
其实这就是一个代码的重构问题,我觉得里面好玩的地方在于利用反射技术和得到数据库的元数据的完美结合,才能将数据库中的信息毫无遗漏的查询出来,这个配合简直堪称很cool。