Web基础之反射机制优化JDBC中DAO层对象的封装


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> T queryForObject(String sql, Class<T> 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> T queryForObject(String sql, Class<T> 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 <T> List<T> queryForObjectList(String sql, Class<T> clazz,
			Object... args) {
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<T> 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<T>();
			// 得到结果集中的元数据
			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<T>) 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<ClassInfoBean> 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<StudentInfoBean> 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<TradeInfoBean> findAllTradeInfoBySno(int sno) {

		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<TradeInfoBean> tradeInfoList = new ArrayList<TradeInfoBean>();
		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<ChargeInfoBean> findAllChargeInfoBySno(int sno) {

		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<ChargeInfoBean> chargeInfoList = new ArrayList<ChargeInfoBean>();
		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反射机制提供了一种减少类似重复代码的新思路;
其实,为了完成这一优化,搞懂反射的基本原理,我可算是费了九牛二虎之力,也用尽了我的洪荒之力了;只为了搞懂反射这一知识点;把老师讲的视频看了好几遍,然后跟着视频把代码敲了一遍;今天为了整理博客,再次重新回顾了这一过程;也用自己的话分析原理;这也是写博客的一大好处;整理你所学的知识,然后用你的理解把你学的知识分享给别人;同时在这一过程中,又加深了自己所学的知识;
共勉!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值