有两种方法可以实现对数据库统一的增删改查操作,一个是利用反射,一个是利用元数据。前者虽然实现简单,但是反射毕竟性能较低,不适合在移动设备上使用,下面我用两种方法实现 JDBC 的一个小框架。
一、利用反射技术
思路分析:
我们对数据库的增删改查操作可以分为两类,一类是增删改即 update,一类是查询即 select。之所以这样分,是因为对数据库的增删改不需要涉及到 Bean 的操作,而查询就要分析我们要查询的对象。
首先我们考虑更新,我们需要用户将 sql 语句,参数传递给我们,然后我们可以利用 PreparedStatement 对象动态的将参数加入到预编译的 sql 中,这很简单,只需几行代码搞定,参数列表可以是数组,也可以是可变参。
查询操作我们除了需要知道上面的两个参数以外,还要知道我们操作的是什么 Bean,这也有两种方式,一种是让用户将 Bean 的 Class 字节码给我们,我们利用反射获取 Bean 里面的每一个字段,之后通过字段名内省出 Bean 的所有属性,从而达到将数据封装到 Bean 的效果。还有一种方式是用到策略模式,我们既然不知道要操作什么 Bean,那么我们就让用户来决定,我们只需要给用户暴露一个接口,将 ResultSet 传过去,用户就可以自己封装数据。为了简化用户操作,我们可以事先写好几个常用的实现类。下面我就演示如何用几行代码搞定增删改查操作:
public static <T> T crud(Class<?> type, String sql, Object... param) {
Connection con = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
T t = null;
try {
ps = con.prepareStatement(sql);
for (int i = 1; param != null && i <= param.length; i++) {
ps.setObject(i, param[i - 1]);
}
boolean execute = ps.execute();
if (execute) {
t = (T) type.newInstance();
rs = ps.getResultSet();
rs.next();
Field[] fields = t.getClass().getDeclaredFields();
for (int i = 0; i < fields.length; i++) {
String fieldName = fields[i].getName();
Object data = rs.getObject(fieldName);
PropertyDescriptor descriptor = new PropertyDescriptor(
fieldName, t.getClass());
Method method = descriptor.getWriteMethod();
method.invoke(t, data);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
release(con, ps, rs);
}
return t;
}
二、利用元数据
思路分析:
上面的代码虽然简单,但是性能比较差,如果我们利用数据库元数据信息,就可以大大提升效率。至于数据库的元数据可以分为:DataBaseMetaData、ParameterMetaData、ResultSetMetaData。他们分别是数据库元数据,我们可以通过 DataBaseMetaData 获取与库相关的信息,比如 url、用户名、版本号、驱动名等等。ParameterMetaData 我们可以获取 sql 语句中的参数个数、参数类型。ResultSetMetaData 可以得到表的列数、指定列的名称、指定列的类型等。有了以上信息,我们为我们利用元数据实现 CRUD 很简单。这种方式我将利用策略模式实现。就是将封装数据的行为交给用户,当然,框架就是更方便的满足用户需求,我也将写两个实现类。
工具类:
package cn.dk.tools;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class CrudTool {
private static ComboPooledDataSource source = new ComboPooledDataSource(
"mysql");
public static Connection getConnection() {
try {
return source.getConnection();
} catch (Exception e) {
throw new ExceptionInInitializerError(e);
}
}
public static void release(Connection con, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
} finally {
try {
st.close();
} catch (SQLException e) {
} finally {
try {
st.close();
} catch (SQLException e) {
} finally {
try {
con.close();
} catch (SQLException e) {
}
}
}
}
}
}
public static int update(String sql, Object[] param) {
Connection con = getConnection();
PreparedStatement ps = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; param != null && i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
return ps.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
public static Object select(String sql, Object[] param,
IResultSetHandler handler) {
Connection con = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; param != null && i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
rs = ps.executeQuery();
return handler.fillObject(rs);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
release(con, ps, rs);
}
}
public static Number selectNum(String sql, Object[] param) {
Connection con = getConnection();
PreparedStatement ps = null;
ResultSet rs = null;
try {
ps = con.prepareStatement(sql);
for (int i = 0; param != null && i < param.length; i++) {
ps.setObject(i + 1, param[i]);
}
rs = ps.executeQuery();
if(rs.next())
return (Number) rs.getObject(1);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
release(con, ps, rs);
}
return 0;
}
}
实现类1:封装 Bean
package cn.dk.tools;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
@SuppressWarnings("unchecked")
public class BeanHandler implements IResultSetHandler {
public BeanHandler(Class clazz) {
this.clazz = clazz;
}
private Class clazz;
public Object fillObject(ResultSet rs) {
ResultSetMetaData metaData;
try {
Object instance = clazz.newInstance();
if (rs != null && rs.next()) {
metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 0; i < columnCount; i++) {
String propertyName = metaData.getColumnName(i + 1);
Object object = rs.getObject(propertyName);
PropertyDescriptor descriptor = new PropertyDescriptor(
propertyName, clazz);
Method writeMethod = descriptor.getWriteMethod();
writeMethod.invoke(instance, object);
}
}
return instance;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
实现类2:返回集合
package cn.dk.tools;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unchecked")
public class BeanListHandler implements IResultSetHandler {
public BeanListHandler(Class clazz) {
this.clazz = clazz;
}
private Class clazz;
public Object fillObject(ResultSet rs) {
ResultSetMetaData metaData;
try {
List list = null;
if (rs != null) {
list = new ArrayList();
while (rs.next()) {
Object instance = clazz.newInstance();
metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 0; i < columnCount; i++) {
String propertyName = metaData.getColumnName(i + 1);
Object object = rs.getObject(propertyName);
PropertyDescriptor descriptor = new PropertyDescriptor(
propertyName, clazz);
Method writeMethod = descriptor.getWriteMethod();
writeMethod.invoke(instance, object);
}
list.add(instance);
}
}
return list;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
这种方法就给了我们一种思想,一种解决问题的思路。