Web基础之反射机制优化JDBC中DAO层对象的封装
一、前言:
在前面我们完成的Servlet+JDBC+JSP项目实战中,我们的DAO层代码(即封装数据库操作的代码)中,有许多类似的代码,我们将通过我们前面学的反射知识来解决这一问题,减少类似的代码,到达优化的目的;
二、分析:
在我们前面的项目中,每个DAO层(ClassInfoDao、StudentInfoDao)的代码无非就是增删查改(CRUD)操作,我们可以发现,无论哪一种操作,都是类似的过程;首先获取数据库的连接-->整理一条SQL语句-->通过预编译对象补全SQL语句中的占位符-->执行数据库操作-->得到操作结果-->(封装查询到的数据-->返回封装结果),后面两步的操作是查询的才有的;
在这一过程中,我们会发现有很多类似这样的代码块:
那么该怎样优化这样类似重复的代码块呢?我们首先来分析一下,各种操作的不同之处,上面已经说了相同的部分了,不同的部分只有SQL语句的不同,参数值的不同;那么这两部分怎么通过反射优化呢?
三、优化原理:
举两个例子来说明吧;
栗子一:
ClassInfoDao层中和StudentInfoDao层中都有新增(add()方法)操作,不同地方是SQL语句不同,传递的参数不同;
那么,我们可以在工具包中,新建一个DAOUtil类,中间有一个add()方法,把这个add()方法做成通用的add()方法,不管是新增班级或者新增学生都通过调用这个方法来实现;
好了,重点在如何实现,首先,SQL语句是必须传递过来的,然后SQL语句中的参数(占位符)也是要传递过来的;怎么根据参数值去设置值呢?这里就需要用到反射的知识了;首先,我们的根据传递过来的参数,通过反射获取参数的类型,然后根据不同的类型,通过预编译对象调用不同的set方法来设置值就好了;重点在setArgs()方法
如下:
以前我们的新增学生和新增班级的add()方法分别在StudentInfoDao和ClassInfoDao中是这样的:
对比一下,你发现这两段代码有多么相似了吗?如果有十个DAO层,这样的代码你要写十遍呢?
public void addClassInfo(ClassInfoBean bean) {
Connection conn = null;
Statement stmt = null;
try {
// 获取数据库连接
conn=DBUtil.getConnection();
// 整理一条SQL语句
String sql = "INSERT INTO class_info (cname) VALUES ('"
+ bean.getCname() + "')";
// 创建SQL执行对象
stmt = conn.createStatement();
// 执行sql语句
int row = stmt.executeUpdate(sql);
if (row != 1) {
throw new RuntimeException("新增班级失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DBUtil.release(conn, stmt, null);
}
}
public void addStudentInfo(StudentInfoBean bean) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取数据库连接
conn=DBUtil.getConnection();
// 整理一条SQL语句
String sql = "INSERT INTO student_info (sname,ssex,sage,cid,sbalance) VALUES (?,?,?,?,?)";
// 创建SQL执行对象
pstmt=conn.prepareStatement(sql);
// 给预处理对象赋值
pstmt.setString(1, bean.getSname());
pstmt.setString(2, bean.getSsex());
pstmt.setInt(3, bean.getSage());
pstmt.setInt(4, bean.getCid());
pstmt.setBigDecimal(5, BigDecimal.ZERO);
//执行SQL语句
int row=pstmt.executeUpdate();
if (row != 1) {
throw new RuntimeException("新增学生信息失败!");
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DBUtil.release(conn, pstmt, null);
}
}
优化后的通用的add()方法
/**
* @param sql 数据库查询语句
* @param args 参数列表 Object... args 可以是0到任意个参数;Java中的棒棒糖语法
*/
public static void add(String sql, Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 创建SQL执行对象
pstmt = conn.prepareStatement(sql);
// 通过反射来设置预编译对象的值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 执行sql语句
int row = pstmt.executeUpdate();
if (row != 1) {
throw new RuntimeException("新增失败!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.release(conn, pstmt, null);
}
}
通用add()方法中的setArgs()方法
private static void setArgs(PreparedStatement pstmt, Object[] args)
throws SQLException {
Object arg = null;
Class> clazz = null;
for (int i = 1; i <= args.length; i++) {
arg = args[i - 1];
if (arg == null) {
// 没有解决的问题
pstmt.setNull(i, 0);
} else {
clazz = arg.getClass();
if (clazz == Boolean.class || clazz == Boolean.TYPE) {
pstmt.setBoolean(i, Boolean.valueOf(arg.toString()));
} else if (clazz == Byte.class || clazz == Byte.TYPE) {
pstmt.setByte(i, Byte.valueOf(arg.toString()));
} else if (clazz == Short.class || clazz == Short.TYPE) {
pstmt.setShort(i, Short.valueOf(arg.toString()));
} else if (clazz == Long.class || clazz == Long.TYPE) {
pstmt.setLong(i, Long.parseLong(arg.toString()));
} else if (clazz == Integer.class || clazz == Integer.TYPE) {
pstmt.setInt(i, Integer.valueOf(arg.toString()));
} else if (clazz == Float.class || clazz == Float.TYPE) {
pstmt.setFloat(i, Float.valueOf(arg.toString()));
} else if (clazz == Double.class || clazz == Double.TYPE) {
pstmt.setDouble(i, Double.valueOf(arg.toString()));
} else if (clazz == Character.class || clazz == Character.TYPE) {
pstmt.setString(i, arg.toString());
} else if (clazz == String.class) {
pstmt.setString(i, arg.toString());
} else if (clazz == BigDecimal.class) {
pstmt.setBigDecimal(i, (BigDecimal) arg);
} else {
pstmt.setObject(i, arg);
}
}
}
}
这里明白了,那么删除,更改也是一样的;
栗子二:
ClassInfoDao层中和StudentInfoDao层中都有根据某一条件查询的操作,不同地方也是SQL语句不同,传递的参数不同;
那么,我们可以在工具包中,新建一个DAOUtil类,中间有一个查询()方法,把这个查询()方法做成通过的查询()方法,不管是新增班级或者新增学生都通过调用这个方法来实现;
和上面不同地方是,上面只需要判断新增是否成功,而这里,得到查询结果后,还需要封装得到的查询结果;
首先我们还是来对比一下这段查询的代码吧:
同样,在每一个Dao层一般都会有这种查询;这里根据班级ID查询班级信息的代码没有放上来了;
那么如何封装这样的类似的代码呢?
public StudentInfoBean findStudentBySno(int sno) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs =null;
StudentInfoBean bean=null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 整理一条SQL语句
//String sql = "SELECT sno,sname,ssex,sage,sbalance from student_info where cid=?";
// 创建执行sql的对象
pstmt = conn.prepareStatement("SELECT cid,sno,sname,ssex,sage,sbalance from student_info where sno=?");
//执行sql语句
pstmt.setInt(1, sno);
rs =pstmt.executeQuery();
//遍历结果集
while(rs.next()){
int scid =rs.getInt("cid");
int stusno=rs.getInt("sno");
String name=rs.getString("sname");
String ssex=rs.getString("ssex");
int sage=rs.getInt("sage");
BigDecimal sbalance=rs.getBigDecimal("sbalance");
//封装成学生Bean对象
bean = new StudentInfoBean();
bean.setCid(scid);
bean.setSno(sno);
bean.setSname(name);
bean.setSage(sage);
bean.setSsex(ssex);
bean.setSbalance(sbalance);
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
DBUtil.release(conn, pstmt, rs);
}
return bean;
}
前面一段和上面是类似的,主要就是得到查询结果集以后如何封装的问题;不过需要多传递一个参数
因为不同查询,会封装成不同的对象,所以,需要传递一个类型告知给封装成什么类型;
得到查询结果集如何封装?
1、得到查询结果的元数据
2、根据元数据得到查询结果的列数
3、每获取结果集中的列名称
4、根据名称找到要封装成bean对象上的属性
5、获取属性的类型
6、获取该属性的setter方法
7、根据列民从结果集中获取值,并通过setter方法设置上去
拿下面这条SQL语句来说:
SELECT cid,sno,sname,ssex,sage,sbalance from student_info where sno=1
查询到的结果是:
根据原数据得结果集的列数是:6
假设获取第三列的名称:sname
去StudentInfoean上找是否有sname属性,没有去父类上找(根据Java继承机制思考)
通过反射获取sname的属性类型是String类型
拼SetSname()方法
通过列名从结果集中得到值为Pony
根据属性类型,这里是String类型,所以调用setString方法,把Pony设置上去;
其他属性类似;
优化原代码:
/**
* 查询结果为单个对象
* @param sql 数据库查询语句
* @param clazz 要封装的数据类型
* @param args 参数列表
* @return
*/
public static T queryForObject(String sql, Class clazz,
Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 创建预编译对象
pstmt = conn.prepareStatement(sql);
// 通过反射来设置参数的值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 执行sql语句查询得到结果集
rs = pstmt.executeQuery();
// 创建对象
Object result = clazz.newInstance();
// 得到查询结果的元数据
ResultSetMetaData data = rs.getMetaData();
// 得到查询结果的列数
int colCount = data.getColumnCount();
while (rs.next()) {
//数据库中都是从1开始的
for (int i = 1; i <= colCount; i++) {
// 获取结果集中的列名称
String colName = data.getColumnName(i);
// 找到要封装成bean对象的属性
Field field = findField(clazz, colName);
//获取属性的类型
Class> fieldType = field.getType();
//获取该属性的setter方法
Method setMethod = findSetter(clazz, getSetName(colName),
fieldType);
//根据属性类型从查询结果中根据列民获取该列的值,然后为该属性设置值
if (fieldType == Boolean.class) {
setMethod.invoke(result,
Boolean.valueOf(rs.getBoolean(colName)));
} else if (fieldType == Boolean.TYPE) {
setMethod.invoke(result, rs.getBoolean(colName));
} else if (fieldType == Byte.class) {
setMethod.invoke(result,
Byte.valueOf(rs.getByte(colName)));
} else if (fieldType == Byte.TYPE) {
setMethod.invoke(result, rs.getByte(colName));
} else if (fieldType == Short.class) {
setMethod.invoke(result,
Short.valueOf(rs.getShort(colName)));
} else if (fieldType == Short.TYPE) {
setMethod.invoke(result, rs.getShort(colName));
} else if (fieldType == Character.class) {
setMethod.invoke(
result,
Character.valueOf(rs.getString(colName).charAt(
0)));
} else if (fieldType == Character.TYPE) {
setMethod.invoke(result, rs.getString(colName)
.charAt(0));
} else if (fieldType == Integer.class) {
setMethod.invoke(result,
Integer.valueOf(rs.getInt(colName)));
} else if (fieldType == Integer.TYPE) {
setMethod.invoke(result, rs.getInt(colName));
} else if (fieldType == Long.class) {
setMethod.invoke(result,
Long.valueOf(rs.getLong(colName)));
} else if (fieldType == Long.TYPE) {
setMethod.invoke(result, rs.getLong(colName));
} else if (fieldType == Float.class) {
setMethod.invoke(result,
Float.valueOf(rs.getFloat(colName)));
} else if (fieldType == Float.TYPE) {
setMethod.invoke(result, rs.getFloat(colName));
} else if (fieldType == Double.class) {
setMethod.invoke(result,
Double.valueOf(rs.getDouble(colName)));
} else if (fieldType == Double.TYPE) {
setMethod.invoke(result, rs.getDouble(colName));
} else if (fieldType == String.class) {
setMethod.invoke(result, rs.getString(colName));
} else if (fieldType == BigDecimal.class) {
setMethod.invoke(result, rs.getBigDecimal(colName));
} else {
setMethod.invoke(result, rs.getObject(colName));
}
}
}
return clazz.cast(result);
} catch (Exception e) {
throw new RuntimeException("查询信息失败", e);
} finally {
DBUtil.release(conn, pstmt, rs);
}
}
查找设置属性的方法、查找属性的方法、拼set的方法、setArgs方法同上
private static Method findSetter(Class> clazz, String methodName,
Class> paramType) throws NoSuchMethodException {
try {
return clazz.getDeclaredMethod(methodName,
new Class>[] { paramType });
} catch (NoSuchMethodException e) {
Class> superClazz = clazz.getSuperclass();
if (superClazz == null) {
throw e;
}
return findSetter(superClazz, methodName, paramType);
}
}
/**
* 如果当前类没有该属性,则去父类上寻找是否有该属性,知道找到Object类,如果还是没有该属性,则返回空
*/
public static Field findField(Class> clazz, String colName)
throws Exception {
try {
return clazz.getDeclaredField(colName);
} catch (NoSuchFieldException e) {
Class> superClazz = clazz.getSuperclass();
if (superClazz == null) {
throw e;
}
return findField(superClazz, colName);
}
}
//拼Set方法
private static String getSetName(String colName) {
if (colName.length() == 1) {
return "set" + Character.toUpperCase(colName.charAt(0));
} else {
return "set" + Character.toUpperCase(colName.charAt(0))
+ colName.substring(1);
}
}
这里查询的是单条数据,还可以封装查询List集合的数据;比如查找班级中所有的学生,这样的过程和上面第二个栗子类似,不在累赘了;
这里需要特别注意的是数据库中设计的字段和bean中的字段要完全一致;这是耦合点;
四:优化后的代码:
DAOUtil类:
package com.huaxin.Util;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.huaxin.bean.ClassInfoBean;
import com.huaxin.bean.StudentInfoBean;
public class DAOUtil {
/**
* @param sql 数据库查询语句
* @param args 参数列表 Object... args 可以是0到任意个参数;Java中的棒棒糖语法
*/
public static void add(String sql, Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 创建SQL执行对象
pstmt = conn.prepareStatement(sql);
// 通过反射来设置预编译对象的值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 执行sql语句
int row = pstmt.executeUpdate();
if (row != 1) {
throw new RuntimeException("新增失败!");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.release(conn, pstmt, null);
}
}
/**
*
* @param sql
* @param args
* @return
*/
public static int update(String sql, Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 创建SQL执行对象
pstmt = conn.prepareStatement(sql);
// 设置预编译对象的值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 执行sql语句
int row = pstmt.executeUpdate();
return row;
} catch (SQLException e) {
throw new RuntimeException("新增信息失败", e);
} finally {
DBUtil.release(conn, pstmt, null);
}
}
/**
* 删除操作,其实就是更新操作
* @param sql
* @param args
* @return
*/
public static int delete(String sql, Object... args) {
return update(sql, args);
}
/**
* 查询结果为单个对象
* @param sql 数据库查询语句
* @param clazz 要封装的数据类型
* @param args 参数列表
* @return
*/
public static T queryForObject(String sql, Class clazz,
Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 创建预编译对象
pstmt = conn.prepareStatement(sql);
// 通过反射来设置参数的值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 执行sql语句查询得到结果集
rs = pstmt.executeQuery();
// 创建对象
Object result = clazz.newInstance();
// 得到查询结果的元数据
ResultSetMetaData data = rs.getMetaData();
// 得到查询结果的列数
int colCount = data.getColumnCount();
while (rs.next()) {
//数据库中都是从1开始的
for (int i = 1; i <= colCount; i++) {
// 获取结果集中的列名称
String colName = data.getColumnName(i);
// 找到要封装成bean对象的属性
Field field = findField(clazz, colName);
//获取属性的类型
Class> fieldType = field.getType();
//获取该属性的setter方法
Method setMethod = findSetter(clazz, getSetName(colName),
fieldType);
//根据属性类型从查询结果中根据列民获取该列的值,然后为该属性设置值
if (fieldType == Boolean.class) {
setMethod.invoke(result,
Boolean.valueOf(rs.getBoolean(colName)));
} else if (fieldType == Boolean.TYPE) {
setMethod.invoke(result, rs.getBoolean(colName));
} else if (fieldType == Byte.class) {
setMethod.invoke(result,
Byte.valueOf(rs.getByte(colName)));
} else if (fieldType == Byte.TYPE) {
setMethod.invoke(result, rs.getByte(colName));
} else if (fieldType == Short.class) {
setMethod.invoke(result,
Short.valueOf(rs.getShort(colName)));
} else if (fieldType == Short.TYPE) {
setMethod.invoke(result, rs.getShort(colName));
} else if (fieldType == Character.class) {
setMethod.invoke(
result,
Character.valueOf(rs.getString(colName).charAt(
0)));
} else if (fieldType == Character.TYPE) {
setMethod.invoke(result, rs.getString(colName)
.charAt(0));
} else if (fieldType == Integer.class) {
setMethod.invoke(result,
Integer.valueOf(rs.getInt(colName)));
} else if (fieldType == Integer.TYPE) {
setMethod.invoke(result, rs.getInt(colName));
} else if (fieldType == Long.class) {
setMethod.invoke(result,
Long.valueOf(rs.getLong(colName)));
} else if (fieldType == Long.TYPE) {
setMethod.invoke(result, rs.getLong(colName));
} else if (fieldType == Float.class) {
setMethod.invoke(result,
Float.valueOf(rs.getFloat(colName)));
} else if (fieldType == Float.TYPE) {
setMethod.invoke(result, rs.getFloat(colName));
} else if (fieldType == Double.class) {
setMethod.invoke(result,
Double.valueOf(rs.getDouble(colName)));
} else if (fieldType == Double.TYPE) {
setMethod.invoke(result, rs.getDouble(colName));
} else if (fieldType == String.class) {
setMethod.invoke(result, rs.getString(colName));
} else if (fieldType == BigDecimal.class) {
setMethod.invoke(result, rs.getBigDecimal(colName));
} else {
setMethod.invoke(result, rs.getObject(colName));
}
}
}
return clazz.cast(result);
} catch (Exception e) {
throw new RuntimeException("查询信息失败", e);
} finally {
DBUtil.release(conn, pstmt, rs);
}
}
/**
* 查询结果集为多个相同的对象
** @param sql 数据库查询语句
* @param clazz 要封装的数据类型
* @param args 参数列表
* @return
*/
public static List queryForObjectList(String sql, Class clazz,
Object... args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List resultList = null;
try {
// 获取连接
conn = DBUtil.getConnection();
pstmt = conn.prepareStatement(sql);
// 设置值
if (args != null && args.length > 0) {
setArgs(pstmt, args);
}
// 得到结果集
rs = pstmt.executeQuery();
// 创建返回对象
resultList = new ArrayList();
// 得到结果集中的元数据
ResultSetMetaData data = rs.getMetaData();
// 得到列数
int colCount = data.getColumnCount();
// 遍历结果集
while (rs.next()) {
Object result = clazz.newInstance();
for (int i = 1; i <= colCount; i++) {
// 得到该列的名字
String colName = data.getColumnName(i);
// 获取属相
Field field = clazz.getDeclaredField(colName);
// 获取属相所对应的类类型
Class> fieldType = field.getType();
// 获取该属性的Set方法
// 1.拼setter方法
String setName = "set"
+ Character.toUpperCase(colName.charAt(0))
+ colName.substring(1);
// 2.获取set方法
Method setMethod = clazz.getDeclaredMethod(setName,
fieldType);
// 调用set方法设置值,由于这里知道只有这几种类型,所以其他类型就没有写了
if (fieldType == String.class) {
setMethod.invoke(result, rs.getString(colName));
} else if (fieldType == Integer.class) {
setMethod.invoke(result,
Integer.valueOf(rs.getInt(colName)));
} else if (fieldType == Integer.TYPE) {
setMethod.invoke(result, rs.getInt(colName));
} else if (fieldType == BigDecimal.class) {
setMethod.invoke(result,
(BigDecimal) (rs.getBigDecimal(colName)));
} else {
setMethod.invoke(result, rs.getObject(colName));
}
}
// 类型强转,并将该对象加到List集合中
resultList.add(clazz.cast(result));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
DBUtil.release(conn, pstmt, rs);
}
return (List) resultList;
}
private static void setArgs(PreparedStatement pstmt, Object[] args)
throws SQLException {
Object arg = null;
Class> clazz = null;
for (int i = 1; i <= args.length; i++) {
arg = args[i - 1];
if (arg == null) {
// 没有解决的问题
pstmt.setNull(i, 0);
} else {
clazz = arg.getClass();
if (clazz == Boolean.class || clazz == Boolean.TYPE) {
pstmt.setBoolean(i, Boolean.valueOf(arg.toString()));
} else if (clazz == Byte.class || clazz == Byte.TYPE) {
pstmt.setByte(i, Byte.valueOf(arg.toString()));
} else if (clazz == Short.class || clazz == Short.TYPE) {
pstmt.setShort(i, Short.valueOf(arg.toString()));
} else if (clazz == Long.class || clazz == Long.TYPE) {
pstmt.setLong(i, Long.parseLong(arg.toString()));
} else if (clazz == Integer.class || clazz == Integer.TYPE) {
pstmt.setInt(i, Integer.valueOf(arg.toString()));
} else if (clazz == Float.class || clazz == Float.TYPE) {
pstmt.setFloat(i, Float.valueOf(arg.toString()));
} else if (clazz == Double.class || clazz == Double.TYPE) {
pstmt.setDouble(i, Double.valueOf(arg.toString()));
} else if (clazz == Character.class || clazz == Character.TYPE) {
pstmt.setString(i, arg.toString());
} else if (clazz == String.class) {
pstmt.setString(i, arg.toString());
} else if (clazz == BigDecimal.class) {
pstmt.setBigDecimal(i, (BigDecimal) arg);
} else {
pstmt.setObject(i, arg);
}
}
}
}
private static Method findSetter(Class> clazz, String methodName,
Class> paramType) throws NoSuchMethodException {
try {
return clazz.getDeclaredMethod(methodName,
new Class>[] { paramType });
} catch (NoSuchMethodException e) {
Class> superClazz = clazz.getSuperclass();
if (superClazz == null) {
throw e;
}
return findSetter(superClazz, methodName, paramType);
}
}
/**
* 如果当前类没有该属性,则去父类上寻找是否有该属性,知道找到Object类,如果还是没有该属性,则返回空
*/
public static Field findField(Class> clazz, String colName)
throws Exception {
try {
return clazz.getDeclaredField(colName);
} catch (NoSuchFieldException e) {
Class> superClazz = clazz.getSuperclass();
if (superClazz == null) {
throw e;
}
return findField(superClazz, colName);
}
}
//拼Set方法
private static String getSetName(String colName) {
if (colName.length() == 1) {
return "set" + Character.toUpperCase(colName.charAt(0));
} else {
return "set" + Character.toUpperCase(colName.charAt(0))
+ colName.substring(1);
}
}
}
优化后的ClassInfoDao类:
package com.huaxin.dao;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.huaxin.Util.DAOUtil;
import com.huaxin.Util.DBUtil;
import com.huaxin.bean.ClassInfoBean;
public class ClassInfoDao {
public void addClassInfo(ClassInfoBean bean) {
DAOUtil.add("INSERT INTO class_info (cname) VALUES (?)",
bean.getCname());
}
public List findAll() {
return DAOUtil.queryForObjectList("select cid,cname from class_info",
ClassInfoBean.class);
}
}
优化后的StudentInfoBean类:
(里面设计数据库事物管理的暂时没有优化;)
package com.huaxin.dao;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.Date;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.huaxin.Util.DAOUtil;
import com.huaxin.Util.DBUtil;
import com.huaxin.bean.ChargeInfoBean;
import com.huaxin.bean.ClassInfoBean;
import com.huaxin.bean.StudentInfoBean;
import com.huaxin.bean.TradeInfoBean;
public class StudentInfoDao {
public void addStudentInfo(StudentInfoBean bean) {
DAOUtil.add(
"INSERT INTO student_info (sname,ssex,sage,cid,sbalance) VALUES (?,?,?,?,?)",
bean.getSname(), bean.getSsex(), bean.getSage(), bean.getCid(),
BigDecimal.ZERO);
}
public StudentInfoBean findStudentBySno(int sno) {
return DAOUtil
.queryForObject(
"SELECT cid,sno,sname,ssex,sage,sbalance from student_info where sno=?",
StudentInfoBean.class, sno);
}
public List findAllStudentByClassId(int cid) {
return DAOUtil
.queryForObjectList(
"SELECT cid,sno,sname,ssex,sage,sbalance from student_info where cid=?",
StudentInfoBean.class, cid);
}
/**
* 这部分代码设计了事务控制,暂时不做优化
* @param bean
*/
@SuppressWarnings("resource")
public void updateStudentSbalanceBySno(ChargeInfoBean bean) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取连接
conn = DBUtil.getConnection();
// 事物控制机制
conn.setAutoCommit(false);
// 整理一条SQL语句
// String sql =
// "SELECT sno,sname,ssex,sage,sbalance from student_info where cid=?";
// 创建执行sql的对象
pstmt = conn
.prepareStatement("UPDATE student_info SET sbalance=sbalance+? WHERE SNO=?");
// 执行sql语句
pstmt.setBigDecimal(1, bean.getCgmoney());
pstmt.setInt(2, bean.getSno());
int row = pstmt.executeUpdate();
if (row != 1) {
throw new RuntimeException("更新学生余额失败!");
} else {
// 更新学生充值数据表
// 创建预编译对象
pstmt = conn
.prepareStatement("INSERT INTO charge_info (cgno,sno,cglocation,cgtime,cgmoney) VALUES(?,?,?,?,?)");
// 给预编译对象赋值
pstmt.setString(1, bean.getCgno());
pstmt.setInt(2, bean.getSno());
pstmt.setString(3, bean.getCglocation());
pstmt.setDate(4, bean.getCgtime());
pstmt.setBigDecimal(5, bean.getCgmoney());
// 执行sql语句
int row1 = pstmt.executeUpdate();
if (row1 != 1) {
throw new RuntimeException("更新交易流水号失败");
} else {
conn.commit();
}
}
} catch (SQLException e) {
try {
conn.rollback();
} catch (SQLException e1) {
throw new RuntimeException(e1);
}
} finally {
DBUtil.release(conn, pstmt, null);
}
}
/*
* 学生交易相关方法
*/
@SuppressWarnings("resource")
public void addTradeInfo(TradeInfoBean bean) {
Connection conn = null;
PreparedStatement pstmt = null;
try {
// 获取数据库连接
conn = DBUtil.getConnection();
// 事物控制
conn.setAutoCommit(false);
// 整理一条SQL语句
// 创建预编译对象
pstmt = conn
.prepareStatement("INSERT INTO trade_info (tno,sno,tlocation,tgoods,tdate,tmoney) VALUES(?,?,?,?,?,?)");
// 给预编译对象赋值
pstmt.setString(1, bean.getTno());
pstmt.setInt(2, bean.getSno());
pstmt.setString(3, bean.getTlocation());
pstmt.setString(4, bean.getTgoods());
pstmt.setDate(5, bean.getTdate());
pstmt.setBigDecimal(6, bean.getTmoney());
// 执行sql语句
int row = pstmt.executeUpdate();
if (row != 1) {
throw new RuntimeException("更新交易流水号失败");
} else {
pstmt = conn
.prepareStatement("UPDATE student_info SET sbalance=sbalance+? WHERE sno=?");
// 执行sql语句
pstmt.setBigDecimal(1, bean.getTmoney().negate());
pstmt.setInt(2, bean.getSno());
int row1 = pstmt.executeUpdate();
if (row1 != 1) {
throw new RuntimeException("更新学生余额失败!");
} else {
conn.commit();
}
}
} catch (Exception e) {
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
throw new RuntimeException(e);
}
} finally {
DBUtil.release(conn, pstmt, null);
}
}
public List findAllTradeInfoBySno(int sno) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List tradeInfoList = new ArrayList();
try {
// 获取连接
conn = DBUtil.getConnection();
// 创建执行sql的对象
pstmt = conn
.prepareStatement("SELECT tid,tno,sno,tlocation,tgoods,tdate,tmoney FROM trade_info WHERE sno=? ORDER BY tdate DESC");
// 执行sql语句
pstmt.setInt(1, sno);
rs = pstmt.executeQuery();
// 遍历结果集
while (rs.next()) {
int tid = rs.getInt("tid");
String tno = rs.getString("tno");
int sno1 = rs.getInt("sno");
String tlocation = rs.getString("tlocation");
String tgoods = rs.getString("tgoods");
Date tdate = rs.getDate("tdate");
BigDecimal tmoney = rs.getBigDecimal("tmoney");
TradeInfoBean bean = new TradeInfoBean();
bean.setTno(tno);
bean.setSno(sno1);
bean.setTdate(tdate);
bean.setTgoods(tgoods);
bean.setTid(tid);
bean.setTlocation(tlocation);
bean.setTmoney(tmoney);
tradeInfoList.add(bean);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.release(conn, pstmt, rs);
}
return tradeInfoList;
}
public List findAllChargeInfoBySno(int sno) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
List chargeInfoList = new ArrayList();
try {
// 获取连接
conn = DBUtil.getConnection();
// 创建执行sql的对象
pstmt = conn
.prepareStatement("SELECT sno,cgno,cglocation,cgtime,cgmoney FROM charge_info WHERE sno=? ORDER BY cgtime DESC");
// 执行sql语句
pstmt.setInt(1, sno);
rs = pstmt.executeQuery();
// 遍历结果集
while (rs.next()) {
int sno1 = rs.getInt("sno");
String cgno = rs.getString("cgno");
String location = rs.getString("cglocation");
Date cgtime = rs.getDate("cgtime");
BigDecimal cgmoney = rs.getBigDecimal("cgmoney");
ChargeInfoBean bean = new ChargeInfoBean();
bean.setSno(sno1);
bean.setCgno(cgno);
bean.setCglocation(location);
bean.setCgtime(cgtime);
bean.setCgmoney(cgmoney);
chargeInfoList.add(bean);
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.release(conn, pstmt, rs);
}
return chargeInfoList;
}
}
其他类和我们前面的项目实战(三)是一模一样的;所以就不贴了;
修改后的整体项目框架
五、总结
通过对JDBC中DAO层的优化,增强了对反射的理解;同时,对于减少Java中类似代码有了新的思路;以前我们减少重复代码,无非是通过把某一些重复的代码封装成方法,但在这里,却没法发挥作用;但Java反射机制提供了一种减少类似重复代码的新思路;
其实,为了完成这一优化,搞懂反射的基本原理,我可算是费了九牛二虎之力,也用尽了我的洪荒之力了;只为了搞懂反射这一知识点;把老师讲的视频看了好几遍,然后跟着视频把代码敲了一遍;今天为了整理博客,再次重新回顾了这一过程;也用自己的话分析原理;这也是写博客的一大好处;整理你所学的知识,然后用你的理解把你学的知识分享给别人;同时在这一过程中,又加深了自己所学的知识;
共勉!