利用Java反射机制+泛型重新封装的BaseDao

 

利用Java反射机制+泛型重新封装的BaseDao

 

 

 

前段时间仿着C#写了个Java版的BaseDao,呵呵,感觉真烂!后来看到有学长利用反射+泛型封装的BaseDao后,心里甚感不快。从来没去探究过Java的反射机制,也从来没想到过自动数据类型映射的做法。于是乎,自己琢磨了下,弄了一个自己的版本,感觉有收获了。实践得真知,我糅合了下自己的理解,以及别人做法的的优点,写了这个版本。感觉有点问题,但还是先帖出来,作为下一个版本的参照目标。

 

BaseDao源码清单(负责SQL命令执行及泛型pojo集合的返回):

 

 package com.china.codingmouse.cmsdk4j.dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Vector; import com.china.codingmouse.cmsdk4j.util.SQLParamHelper; /** * BaseDao 泛型数据库访问基类 * @author CodingMouse * @version 1.0.0.1 2009-3-26 * @param 类型参数 */ public class BaseDao { private Connection conn = null; // 数据库连接对象 private PreparedStatement ps = null; // 预编译的SQL命令执行对象 private ResultSet rs = null; // 结果集对象 /** * executeUpdate 执行SQL更新命令的方法 * @param sql SQL命令字符串 * @param param 参数列表 * @return true-成功/false-失败 */ protected boolean executeUpdate(String sql, Object[] param) { int rowCount = 0; // 保存执行SQL更新命令受影响的行数 try { // 获取数据库连接对象 this.conn = DBConnection.getConnection(); // 获取预编译SQL语句执行对象并传入SQL命令字串 this.ps = this.conn.prepareStatement(sql); // 自动映射SQL参数 this.ps = SQLParamHelper.JavaParam2SQLParam(param, this.ps); // 执行SQL更新命令并保存返回的受影响行数 rowCount = this.ps.executeUpdate(); } catch (SQLException ex) { System.err.println("异常信息:执行SQL更新命令时发生错误!/r/n" + ex.getMessage()); } finally { // 释放资源 this.releaseResource(this.rs, this.ps, this.conn); } // 返回结果 return rowCount > 0; } /** * executeQuery 执行SQL查询命令的方法 * @param sql SQL命令字符串 * @param param 参数列表 * @param classPath 待返回的泛型pojo类运行时类名路径字串(包含完整包名+类名,如:TestPojo.class.getName()) * @return Vector 泛型pojo集合; */ protected Vector executeQuery(String sql, Object[] param, String classPath) { Vector pojoSet = null; // 泛型pojo集合 try { // 获取数据库连接对象 this.conn = DBConnection.getConnection(); // 获取预编译SQL语句执行对象并传入SQL命令字串 this.ps = this.conn.prepareStatement(sql); // 自动映射SQL参数 this.ps = SQLParamHelper.JavaParam2SQLParam(param, this.ps); // 执行SQL更新命令并保存取回的结果集对象 this.rs = this.ps.executeQuery(); // 获取关于 ResultSet 对象中列的类型和属性信息的 ResultSetMetaData 对象 ResultSetMetaData rsmd = rs.getMetaData(); // 利用Java反射机制查找对应 pojo 的Class Class pojoClass = Class.forName(classPath); // 创建新的 pojoSet (pojo集合) 实例 pojoSet = new Vector(); // 利用Java反射机制读取取回的结果集并自动映射为对应的 pojo 实例 while (this.rs.next()) { // 生成单个 pojo 实例 T pojo = SQLParamHelper.SQLParam2JavaParam(rsmd, pojoClass, rs); // 将自动映射完毕的 pojo 实例封装到 pojoSet 泛型集合 pojoSet.add(pojo); } } catch (SQLException ex) { System.err.println("异常信息:获取数据库连接对象错误!/r/n" + ex.getMessage()); } catch (ClassNotFoundException ex) { System.err.println("异常信息:无法找到指定的 pojo 类!/r/n" + ex.getMessage()); } finally { // 释放资源 this.releaseResource(this.rs, this.ps, this.conn); } // 返回 pojoSet 泛型集合 return pojoSet; } /** * releaseResource 释放指定的数据库访问对象资源 * @param rs 结果集对象 * @param ps 预编译的SQL语句执行对象(PreparedStatement及其子类CallableStatement) * @param conn 与特定数据库的连接(会话)对象 */ private void releaseResource(ResultSet rs, PreparedStatement ps, Connection conn) { if(rs != null) { try { rs.close(); } catch (SQLException ex) { System.err.println("异常信息:关闭结果集对象错误!/r/n" + ex.getMessage()); } } if(ps != null) { try { ps.close(); } catch (SQLException ex) { System.err.println("异常信息:关闭SQL命令执行对象错误!/r/n" + ex.getMessage()); } } if(conn != null) { try{ if (!conn.isClosed()) { conn.close(); } } catch (SQLException ex){ System.err.println("异常信息:关闭数据库连接错误!/r/n" + ex.getMessage()); } } } }  

  

DBConnection 源码清单(负责从创建数据库连接,使用了同步):

 

 

package com.china.codingmouse.cmsdk4j.dao; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import com.china.codingmouse.cmsdk4j.util.DBConfigReader; /** * DBConnection 与特定数据库的连接(会话)对象管理类 * @author CodingMouse * @version 1.0.0.1 2009-3-26 */ public final class DBConnection { private static Connection conn = null; // 数据库连接对象 /** * 私有化默认构造器 */ private DBConnection() { // 获取数据库连接对象 getConnection(); } /** * 同步创建与特定数据库连接(会话)对象的方法 */ private static synchronized void createConnection() { try { // 从DBConfig.xml配置文件中同步读取数据库驱动Url、连接Url String[] drvierAndUrl = DBConfigReader.ReadDBConfig(); if (conn == null || conn.isClosed()) { // 将指定字串的数据驱动加载到JVM(Java虚拟机环境) Class.forName(drvierAndUrl[0]); // 获取数据库连接 conn = DriverManager.getConnection(drvierAndUrl[1]); } } catch (ClassNotFoundException ex) { System.err.println("异常信息:无法找到特定数据库驱动类错误!/r/n" + ex.getMessage()); } catch (SQLException ex) { System.err.println("异常信息:特定数据库访问及其他相关错误!/r/n" + ex.getMessage()); } } /** * 获取与特定数据库连接(会话)对象的方法 * @return 数据库连接对象 */ public static Connection getConnection() { // 同步创建数据库连接对象 createConnection(); // 返回数据库连接对象 return conn; } /** * 开始数据库连接对象的事务 */ public void beginTrans() { try { if (conn != null && !conn.isClosed() && conn.getAutoCommit()) { // 禁用事务自动提交(设置事务手动提交) conn.setAutoCommit(false); } } catch (SQLException ex) { System.err.println("异常信息:数据库连接开始事务时发生错误!/r/n" + ex.getMessage()); } } /** * 提交数据库连接对象的事务 */ public void commitTrans() { try { if (conn != null && !conn.isClosed() && !conn.getAutoCommit()) { // 提交事务 conn.commit(); // 启用事务自动提交 conn.setAutoCommit(true); } } catch (SQLException ex) { System.err.println("异常信息:数据库连接提交事务时发生错误!/r/n" + ex.getMessage()); } } /** * 回滚数据库连接对象的事务 */ public void rollbackTrans() { try { if (conn != null && !conn.isClosed() && !conn.getAutoCommit()) { // 回滚事务 conn.rollback(); // 启用事务自动提交 conn.setAutoCommit(true); } } catch (SQLException ex) { System.err.println("异常信息:数据库连接回滚事务时发生错误!/r/n" + ex.getMessage()); } } }

 

DBConfigReader 源码清单(负责XML配置文件的内容读取,没用使用三方jar包,仅用了JDK内置的XML相关类):

 

 

package com.china.codingmouse.cmsdk4j.util; import java.io.IOException; import java.io.InputStream; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException; /** * DBConfigReader 数据库配置文件阅读工具类 * @author CodingMouse * @version 1.0.0.1 2009-3-26 */ public final class DBConfigReader { /** * 同步读取数据库配置文件内容 * @return 仅有两个元素的配置信息内容字串数组(0下标-数据库驱动Url,1下标-数据库连接Url) */ public static synchronized String[] ReadDBConfig() { // 从DBConfig.xml配置文件中读取数据库驱动Url、连接Url String[] driverAndUrl = new String[2]; try { // 获取字节输入流对象 // InputStream xmlInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("DBConfig.xml"); InputStream xmlInputStream = DBConfigReader.class.getClassLoader().getResourceAsStream("DBConfig.xml"); // 创建XML解析器并获取DocumentBuilderFactory的对象 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); // 获取能解析xml文件为文档的的DocumentBuilder对象 DocumentBuilder db = dbf.newDocumentBuilder(); // 将指定的xml文件解析成文档对象 Document doc = db.parse(xmlInputStream); // 获取文档的根节点元素 Element element = doc.getDocumentElement(); // 获取根节点元素集合 NodeList nodelist = element.getChildNodes(); // 保存当前数据库服务器名称 String currentDBServer = null; // 遍历整个根节点()元素集合,读取子节点的值 for(int i = 0; i < nodelist.getLength();i++){ // 从节点集合获取节点为i的对象 Node rootNode = nodelist.item(i); //判断该节点是否是元素对象,如果是继续读下去 if(rootNode instanceof Element){ // 获取节点i对象的子节点集合 NodeList childNodeList = rootNode.getChildNodes(); // 获取当前数据库服务器类型节点名称 if (rootNode.getNodeName().toLowerCase().equals("currentdbserver")) { // 保存当前数据库服务器类型节点文本值(即节点与其闭合节点间的文本内容) currentDBServer = rootNode.getTextContent().toLowerCase(); } // 根据数据库服务器信息列表获取其子节点信息 if(rootNode.getNodeName().toLowerCase().equals("dbserverlist")){ for(int j = 0 ; j < childNodeList.getLength(); j++){ // 获取次级子节点() Node childNode = childNodeList.item(j); if(childNode instanceof Element){ // 获取次级孙子节点()集合 NodeList grandchildNodeList = childNode.getChildNodes(); // 匹配数据库服务器类型名称 if(childNode.getAttributes().getNamedItem("id").getNodeValue().toLowerCase().equals(currentDBServer)){ for(int k = 0; k < grandchildNodeList.getLength(); k++) { // 获取孙子节点()信息 Node grandchildNode = grandchildNodeList.item(k); // 保存获取到的数据库驱动Url、连接Url字串信息 if(grandchildNode instanceof Element){ if (grandchildNode.getAttributes().getNamedItem("name").getNodeValue().toLowerCase().equals("driverurl")) { driverAndUrl[0] = grandchildNode.getTextContent(); } if (grandchildNode.getAttributes().getNamedItem("name").getNodeValue().toLowerCase().equals("connectionurl")) { driverAndUrl[1] = grandchildNode.getTextContent(); } } } // 中止匹配 break; } } } // 中止子节点查找 break; } } } } catch (ParserConfigurationException ex) { System.err.println("异常信息:发生了一个严重的配置错误!/r/n" + ex.getMessage()); } catch (SAXException ex) { System.err.println("异常信息:XML 解析器或应用程序的基本错误和警告!/r/n" + ex.getMessage()); } catch (IOException ex) { System.err.println("异常信息:发生了一个 I/O(文件输入/输出) 异常!/r/n" + ex.getMessage()); } // 返回结果 return driverAndUrl; } }

 

SQLParamHelper 源码清单(负责Java数据类型与SQL参数数据类型的自动映射,写得有点细,但我觉得细化些好,部分类型还未进行测试):

 

 

package com.china.codingmouse.cmsdk4j.util; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.math.BigDecimal; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; /** * SQLParamHelper SQL参数辅助工具类 * @author CodingMouse * @version 1.0.0.1 2009-3-26 */ public final class SQLParamHelper { /** * 自动将Java数据类型参数映射转换为SQL参数 * @param param Java数据类型参数数组 * @param ps SQL命令执行对象 * @return 返回自动匹配添加好SQL参数的SQl命令执行对象 */ public static PreparedStatement JavaParam2SQLParam(Object[] param, PreparedStatement ps) { // 使用Class.isInstance()方法判定指定的 Object // 是否与此 Class 所表示的对象赋值兼容。 // 此方法是 Java 语言 instanceof 运算符的动态等效方法。 // 如果当前SQL命令包含参数则进行Java数据类型与SQL命令参数的人工映射 if (param != null && param.length != 0) { try { for (int i = 0; i < param.length; i++) { // 将第i个参数的Java数据类型通过其对应的包装器类人工映射并设定SQL命令参数 if (Boolean.class.isInstance(param[i])) { // 映射boolean类型 ps.setBoolean(i + 1, Boolean.parseBoolean(param[i].toString())); } else if (Byte.class.isInstance(param[i])) { // 映射byte类型 ps.setByte(i + 1, Byte.parseByte(param[i].toString())); } else if (byte[].class.isInstance(param[i])) { ps.setBytes(i + 1, (param[i].toString()).getBytes()); // 映射byte[]类型 } else if (Character.class.isInstance(param[i]) || String.class.isInstance(param[i])) { // 映射char和String类型 ps.setString(i + 1, String.valueOf(param[i])); } else if (Short.class.isInstance(param[i])) { // 映射short类型 ps.setShort(i + 1, Short.parseShort(param[i].toString())); } else if (Integer.class.isInstance(param[i])) { // 映射int类型 ps.setInt(i + 1, Integer.parseInt(param[i].toString())); } else if (Long.class.isInstance(param[i])) { // 映射long类型 ps.setLong(i + 1, Long.parseLong(param[i].toString())); } else if (Float.class.isInstance(param[i])) { // 映射float类型 ps.setFloat(i + 1, Float.parseFloat(param[i].toString())); } else if (Double.class.isInstance(param[i])) { // 映射double类型 ps.setDouble(i + 1, Double.parseDouble(param[i].toString())); } else if (BigDecimal.class.isInstance(param[i])) { // 映射BigDecimal类型 ps.setBigDecimal(i + 1, new BigDecimal(param[i].toString())); } else if (Date.class.isInstance(param[i])) { // 映射Date类型 ps.setDate(i + 1, Date.valueOf(param[i].toString())); } else if (Time.class.isInstance(param[i])) { // 映射Time类型 ps.setTime(i + 1, Time.valueOf(param[i].toString())); } else if (Timestamp.class.isInstance(param[i])) { // 映射Timestamp类型 ps.setTimestamp(i + 1, Timestamp.valueOf(param[i].toString())); } else { throw new Exception("Java程序中包含不支持的自动映射数据类型:" + param[i].getClass().getName()); } } } catch(SQLException ex) { System.err.println("异常信息:特定数据库访问及其他相关错误!/r/n" + ex.getMessage()); } catch (Exception ex) { System.err.println("异常信息:程序兼容问题!/r/n" + ex.getMessage()); } } // 返回结果 return ps; } /** * 自动将SQL数据类型参数映射转换为Java数据类型 * @param 泛型类型参数 * @param rsmd 关于 ResultSet 对象中列的类型和属性信息的 ResultSetMetaData 对象 * @param pojoClass 利用Java反射机制查找对应 pojo 的Class * @param rs 结果集对象 * @return 泛型 pojo 实例 */ @SuppressWarnings("unchecked") // 禁用对此方法的类型安全检查 public static T SQLParam2JavaParam(ResultSetMetaData rsmd, Class pojoClass, ResultSet rs) { // 定义泛型 pojo 实例 T pojo = null; try { // 生成单个泛型 pojo 实例 pojo = (T)pojoClass.newInstance(); // 遍历数据集的每一列,通过共同遵守的Pascal命名规则反射查找并执行对应 // pojo 类的赋值(getter)方法以实现结果集到pojo泛型集合的自动映射 for (int i = 1; i <= rsmd.getColumnCount(); i++) { // 取得第i列列名 String setMethodName = rsmd.getColumnName(i); // 通过命名规则处理第i列列名,取得 pojo 中对应字段的取值(setter)方法名 setMethodName = "set" + setMethodName.substring(0, 1).toUpperCase() + setMethodName.substring(1); // 取得第i列的数据类型 int dbType = rsmd.getColumnType(i); // 当前反射方法 Method method = null; // 对应第i列的SQL数据类型人工映射到对应的Java数据类型, // 并反射执行该列的在 pojo 中对应属性的 setter 方法完成赋值 if (dbType == Types.TINYINT) { method = pojoClass.getMethod(setMethodName, byte.class); method.invoke(pojo, rs.getByte(i)); } else if (dbType == Types.SMALLINT) { method = pojoClass.getMethod(setMethodName, short.class); method.invoke(pojo, rs.getShort(i)); } else if (dbType == Types.INTEGER) { method = pojoClass.getMethod(setMethodName, int.class); method.invoke(pojo, rs.getInt(i)); } else if (dbType == Types.BIGINT) { method = pojoClass.getMethod(setMethodName, long.class); method.invoke(pojo, rs.getLong(i)); } else if (dbType == Types.FLOAT || dbType == Types.REAL) { method = pojoClass.getMethod(setMethodName, float.class); method.invoke(pojo, rs.getFloat(i)); } else if (dbType == Types.DOUBLE) { method = pojoClass.getMethod(setMethodName, double.class); method.invoke(pojo, rs.getDouble(i)); } else if (dbType == Types.DECIMAL || dbType == Types.NUMERIC) { method = pojoClass.getMethod(setMethodName, BigDecimal.class); method.invoke(pojo, rs.getBigDecimal(i)); } else if (dbType == Types.BIT) { method = pojoClass.getMethod(setMethodName, boolean.class); method.invoke(pojo, rs.getBoolean(i)); } else if (dbType == Types.CHAR || dbType == Types.VARCHAR || dbType == Types.LONGVARCHAR || dbType == Types.CLOB) { method = pojoClass.getMethod(setMethodName, String.class); method.invoke(pojo, rs.getString(i)); } else if (dbType == Types.DATE) { // 继承于 java.util.Date 类 method = pojoClass.getMethod(setMethodName, Date.class); method.invoke(pojo, rs.getDate(i)); } else if (dbType == Types.TIME) { // 继承于 java.util.Date 类 method = pojoClass.getMethod(setMethodName, Time.class); method.invoke(pojo, rs.getTime(i)); } else if (dbType == Types.TIMESTAMP) { // 继承于 java.util.Date 类 method = pojoClass.getMethod(setMethodName, Timestamp.class); method.invoke(pojo, rs.getTimestamp(i)); } else if (dbType == Types.BINARY || dbType == Types.VARBINARY || dbType == Types.LONGVARBINARY || dbType == Types.BLOB) { method = pojoClass.getMethod(setMethodName, byte[].class); method.invoke(pojo, rs.getBytes(i)); } else { throw new Exception("数据库中包含不支持的自动映射数据类型:" + dbType); } } } catch (InstantiationException ex) { System.err.println("异常信息:指定的类对象无法被 Class 类中的 newInstance 方法实例化!/r/n" + ex.getMessage()); } catch (NoSuchMethodException ex) { System.err.println("异常信息:无法找到某一特定的方法!/r/n" + ex.getMessage()); } catch (IllegalAccessException ex) { System.err.println("异常信息:对象定义无法访问,无法反射性地创建一个实例!/r/n" + ex.getMessage()); } catch (InvocationTargetException ex) { System.err.println("异常信息:由调用方法或构造方法所抛出异常的经过检查的异常!/r/n" + ex.getMessage()); } catch (SecurityException ex) { System.err.println("异常信息:安全管理器检测到安全侵犯!/r/n" + ex.getMessage()); } catch (IllegalArgumentException ex) { System.err.println("异常信息:向方法传递了一个不合法或不正确的参数!/r/n" + ex.getMessage()); } catch (SQLException ex) { System.err.println("异常信息:获取数据库连接对象错误!/r/n" + ex.getMessage()); } catch (Exception ex) { System.err.println("异常信息:程序兼容问题!/r/n" + ex.getMessage()); } // 返回结果 return pojo; } }

 

DBConfig.xml 文件内容(负责保存各类数据库驱动完整包名+类名及连接字串):

 

 

MsSQLServer2005 com.microsoft.jdbc.sqlserver.SQLServerDriver jdbc:microsoft:sqlserver://127.0.0.1:1433;databaseName=GroundHogSDK4jTestDB;user=sa;password=sa com.microsoft.sqlserver.jdbc.SQLServerDriver jdbc:sqlserver://127.0.0.1:1433;databaseName=GroundHogSDK4jTestDB;user=sa;password=sql2005 sun.jdbc.odbc.JdbcOdbcDriver jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ=F://GroundHogSDK4jTestDB.mdb com.mysql.jdbc.Driver jdbc:mysql://127.0.0.1:1333/GroundHogSDK4jTestDB

 

------------------------------------------------------------------------------------

 

以下是相关的测试类源码清单!

 

------------------------------------------------------------------------------------

 

UserPojo 源码清单(测试一个简单的Pojo):

 

 

package com.china.codingmouse.cmsdk4j.example.pojo; import java.sql.Timestamp; /** * UserPojo 用户信息实体类 * @author CodingMouse * @version 1.0.0.1 2009-3-29 */ public class UserPojo { private int id; // 用户ID private String name; // 用户姓名 private boolean sex; // 用户性别 private int age; // 用户年龄 private String address; // 用户住址 private Timestamp regTime; // 用户注册时间 /** * 默认构造器 */ public UserPojo() { super(); } /** * 参数化构造器 * @param id 用户ID * @param name 用户姓名 * @param sex 用户性别 * @param age 用户年龄 * @param address 用户住址 * @param regTime 用户注册时间 */ public UserPojo(int id, String name, boolean sex, int age, String address, Timestamp regTime) { super(); this.setId(id); this.setName(name); this.setSex(sex); this.setAge(age); this.setAddress(address); this.setRegTime(regTime); } /** * 用户ID取值方法 * @return 用户ID */ public int getId() { return id; } /** * 用户ID赋值方法 * @param id 用户ID */ public void setId(int id) { this.id = id; } /** * 用户姓名取值方法 * @return 用户姓名 */ public String getName() { return name; } /** * 用户姓名赋值方法 * @param name 用户姓名 */ public void setName(String name) { this.name = name; } /** * 用户性别取值方法 * @return 用户性别 */ public boolean getSex() { return sex; } /** * 用户性别赋值方法 * @param sex 用户性别 */ public void setSex(boolean sex) { this.sex = sex; } /** * 用户年龄取值方法 * @return 用户年龄 */ public int getAge() { return age; } /** * 用户年龄赋值方法 * @param age 用户年龄 */ public void setAge(int age) { this.age = age; } /** * 用户住址取值方法 * @return 用户住址 */ public String getAddress() { return address; } /** * 用户住址赋值方法 * @param address 用户住址 */ public void setAddress(String address) { this.address = address; } /** * 用户注册时间取值方法 * @return 用户注册时间 */ public Timestamp getRegTime() { return regTime; } /** * 用户注册时间赋值方法 * @param regTime 用户注册时间 */ public void setRegTime(Timestamp regTime) { this.regTime = regTime; } /** * 重写equals方法 */ @Override public boolean equals(Object obj) { // 自身比较 if (obj == this) { return true; } // 类型相同 if (obj.getClass() == this.getClass()) { // 强转类型以匹配属性 UserPojo up = (UserPojo)obj; if (up.getId() == this.getId() && up.getName().equals(this.getName()) && up.getSex() == this.getSex() && up.getAge() == this.getAge() && up.getAddress().equals(this.getAddress()) && up.getRegTime().equals(this.getRegTime())) { return true; } } // 未通过相等比较则返回逻辑假 return false; } /** * 重写hashCode方法 */ @Override public int hashCode() { // 生成简单的位运算hash散列码 String key = this.toString(); int prime = key.hashCode(); int hash = prime; for (int i = 0; i < key.length(); i++) { hash ^= (hash << 23 >> 17) ^ key.charAt(i) * 13131; } // 返回结果 return (hash % prime) * 33; } /** * 重写toString方法 */ @Override public String toString() { // 采用数组的toString方式输出 return "[" + this.getId() + ", " + this.getName() + ", " + this.getSex() + ", " + this.getAge() + ", " + this.getAddress() + ", " + this.getRegTime() + "]"; } }   

 

UserPojohashCodeequals方法重写的测试:

 

 

package com.china.codingmouse.cmsdk4j.example.pojo.test; import java.sql.Timestamp; import com.china.codingmouse.cmsdk4j.example.pojo.UserPojo; /** * 用户信息实体类Equals与HashCode方法测试类 * @author CodingMouse * @version 1.0.0.1 2009-3-29 */ public class UserPojoEqualsAndHashCodeTest { /** * 测试类主方法 * @param args */ public static void main(String[] args) { UserPojo up1 = new UserPojo(3, "邓超", true, 25, "四川隆昌", new Timestamp(System.currentTimeMillis())); UserPojo up2 = new UserPojo(3, "邓超", true, 25, "四川隆昌", new Timestamp(System.currentTimeMillis())); System.out.println("User1的内容:" + up1); System.out.println("User2的内容:" + up2); System.err.println("User1的散列码:" + up1.hashCode()); System.err.println("User2的散列码:" +up2.hashCode()); System.out.println("测试User1与User2地址(==)相等:" + (up1 == up2)); System.out.println("测试User1与User2内容(equals)相等:" + up1.equals(up2)); UserPojo up3 = new UserPojo(6, "CodingMouse", false, 22, "中华人民共和国四川成都", new Timestamp(System.currentTimeMillis())); UserPojo up4 = new UserPojo(13, "Michael Jackson", false, 53, "美利坚合众国纽约市唐人街", new Timestamp(System.currentTimeMillis())); System.out.println("User3的内容:" + up3); System.out.println("User4的内容:" + up4); System.err.println("User3的散列码:" +up3.hashCode()); System.err.println("User4的散列码:" +up4.hashCode()); System.out.println("测试User3与User4地址(==)相等:" + (up3 == up4)); System.out.println("测试User3与User4内容(equals)相等:" + up3.equals(up4)); } }

 

UserPojo测试类输出结果:

 

 

User1的内容:[3, 邓超, true, 25, 四川隆昌, 2009-03-30 16:53:13.995] User2的内容:[3, 邓超, true, 25, 四川隆昌, 2009-03-30 16:53:13.995] User1的散列码:-619793365 User2的散列码:-619793365 测试User1与User2地址(==)相等:false 测试User1与User2内容(equals)相等:true User3的内容:[6, CodingMouse, false, 22, 中华人民共和国四川成都, 2009-03-30 16:53:13.999] User4的内容:[13, Michael Jackson, false, 53, 美利坚合众国纽约市唐人街, 2009-03-30 16:53:13.999] User3的散列码:-37057156 User4的散列码:-714365817 测试User3与User4地址(==)相等:false 测试User3与User4内容(equals)相等:false

 

UserDao源码清单(负责通知BaseDao类型参数及添加相关业务逻辑代码,这里由于无实际业务逻辑,仅以注释说明):

 

 

package com.china.codingmouse.cmsdk4j.example.dao; import java.util.Vector; import com.china.codingmouse.cmsdk4j.dao.BaseDao; import com.china.codingmouse.cmsdk4j.example.pojo.UserPojo; /** * UserDao 用户信息数据访问类 * @author CodingMouse * @version 1.0.0.1 2009-3-29 */ public class UserDao extends BaseDao { /** * 重写BaseDao的执行SQL更新命令方法以便添加业务逻辑 * @param sql SQL命令字符串 * @param param 参数列表 */ @Override public boolean executeUpdate(String sql, Object[] param) { // 在这里添加业务逻辑 return super.executeUpdate(sql, param); } /** * 重写BaseDao的执行SQL查询命令方法以便添加业务逻辑 * @param sql SQL命令字符串 * @param param 参数列表 */ @Override public Vector executeQuery(String sql, Object[] param, String classPath) { // 在这里添加业务逻辑 return super.executeQuery(sql, param, classPath); } }

 

UserDaoTest源码清单(测试了一下简单的CRUD操作):

 

 

package com.china.codingmouse.cmsdk4j.example.dao.test; import java.sql.Timestamp; import java.util.Vector; import com.china.codingmouse.cmsdk4j.example.dao.UserDao; import com.china.codingmouse.cmsdk4j.example.pojo.UserPojo; /** * UserDaoTest 用户信息数据访问类测试类 * @author CodingMouse * @version 1.0.0.1 2009-3-29 */ public class UserDaoTest { // 创建用户信息实体数据访问类实例 UserDao ud = null; /** * 默认构造器 */ public UserDaoTest() { this.ud = new UserDao(); } /** * 测试类主方法 * @param args */ public static void main(String[] args) { // 创建自身的实例 UserDaoTest userDaoTest = new UserDaoTest(); // 显示数据 System.err.println("数据库中的原始数据:"); userDaoTest.showMessage(); // 添加一条数据 if (userDaoTest.ud.executeUpdate("insert into [User] values(?, ?, ?, ?, ?)", new Object[] {"陈九", 0, 19, "成都市纱帽街" + (int)(Math.random() * 100) + "号", new Timestamp(System.currentTimeMillis())})) { javax.swing.JOptionPane.showMessageDialog(null, "数据添加成功!"); } else { javax.swing.JOptionPane.showMessageDialog(null, "数据添加失败!"); } // 显示数据 System.err.println("/r/n添加一条数据之后:"); userDaoTest.showMessage(); // 修改一条数据 if (userDaoTest.ud.executeUpdate("update [User] set [Name] = ? , [Sex] = ?, [Age] = ?, [Address] = ?, [RegTime] = ? where [Id] = ?", new Object[] {"徐八" + (int)(Math.random() * 100), false, 18, (int)(Math.random() * 100) + "石头里蹦出来的!", new Timestamp(System.currentTimeMillis()), 6})) { javax.swing.JOptionPane.showMessageDialog(null, "数据修改成功!"); // 显示数据 System.err.println("/r/n修改一条数据之后:"); userDaoTest.showMessage(); } else { javax.swing.JOptionPane.showMessageDialog(null, "数据修改失败!"); } // 删除一条数据 if (userDaoTest.ud.executeUpdate("delete from [User] where [Name] = ?", new Object[] {"陈九"})) { javax.swing.JOptionPane.showMessageDialog(null, "数据删除成功!"); // 显示数据 System.err.println("/r/n删除一条数据之后:"); userDaoTest.showMessage(); } else { javax.swing.JOptionPane.showMessageDialog(null, "数据删除失败!"); } } /** * 查询并显示所有用户信息 */ private void showMessage() { // 查询数据库User表内容并返回Pojo泛型集合 Vector vud = this.ud.executeQuery("select [Id], [Name], [Sex], [Age], [Address], [RegTime] from [User]", null, UserPojo.class.getName()); // 显示集合内容 for(UserPojo up : vud) { System.out.println(up); } } }

 

测试运行结果为CRUD操作全部通过,控制台输出为:

 

 

数据库中的原始数据: [1, 张三, true, 23, 成都市大慈寺路1号, 2003-01-23 08:12:34.0] [2, 李四, true, 18, 成都市春熙路102号, 2009-09-03 19:52:02.0] [3, 王五, false, 29, 成都市书院街169号, 2005-12-07 13:41:56.0] [4, 赵六, true, 19, 成都市望平街119号, 2001-10-11 17:32:05.0] [5, 周七, false, 36, 成都市福字街206号, 2006-02-21 18:39:21.0] [6, 徐八42, false, 18, 43石头里蹦出来的!, 2009-03-30 16:51:47.697] 添加一条数据之后: [1, 张三, true, 23, 成都市大慈寺路1号, 2003-01-23 08:12:34.0] [2, 李四, true, 18, 成都市春熙路102号, 2009-09-03 19:52:02.0] [3, 王五, false, 29, 成都市书院街169号, 2005-12-07 13:41:56.0] [4, 赵六, true, 19, 成都市望平街119号, 2001-10-11 17:32:05.0] [5, 周七, false, 36, 成都市福字街206号, 2006-02-21 18:39:21.0] [6, 徐八42, false, 18, 43石头里蹦出来的!, 2009-03-30 16:51:47.697] [9, 陈九, false, 19, 成都市纱帽街69号, 2009-03-30 18:50:17.86] 修改一条数据之后: [1, 张三, true, 23, 成都市大慈寺路1号, 2003-01-23 08:12:34.0] [2, 李四, true, 18, 成都市春熙路102号, 2009-09-03 19:52:02.0] [3, 王五, false, 29, 成都市书院街169号, 2005-12-07 13:41:56.0] [4, 赵六, true, 19, 成都市望平街119号, 2001-10-11 17:32:05.0] [5, 周七, false, 36, 成都市福字街206号, 2006-02-21 18:39:21.0] [6, 徐八83, false, 18, 85石头里蹦出来的!, 2009-03-30 18:50:22.657] [9, 陈九, false, 19, 成都市纱帽街69号, 2009-03-30 18:50:17.86] 删除一条数据之后: [1, 张三, true, 23, 成都市大慈寺路1号, 2003-01-23 08:12:34.0] [2, 李四, true, 18, 成都市春熙路102号, 2009-09-03 19:52:02.0] [3, 王五, false, 29, 成都市书院街169号, 2005-12-07 13:41:56.0] [4, 赵六, true, 19, 成都市望平街119号, 2001-10-11 17:32:05.0] [5, 周七, false, 36, 成都市福字街206号, 2006-02-21 18:39:21.0] [6, 徐八83, false, 18, 85石头里蹦出来的!, 2009-03-30 18:50:22.657]

 

相关的 SQL Server 数据库脚本:

 

 

-- 建库 use [master] go if exists(select * from [sysdatabases] where [name] = 'GroundHogSDK4jTestDB') drop database [GroundHogSDK4jTestDB] go create database [GroundHogSDK4jTestDB] on ( name = 'GroundHogSDK4jTestDB', filename = 'D:/Workspace/GroundHogSDK4jTestDB.mdf', size = 3mb, maxsize = 100mb, filegrowth = 1mb ) log on ( name = 'GroundHogSDK4jTestDB_Log', filename = 'D:/Workspace/GroundHogSDK4jTestDB_Log.ldf', size = 1mb, maxsize = 50mb, filegrowth = 1mb ) go -- 建表 use [GroundHogSDK4jTestDB] go create table [User] ( [Id] int identity(1, 1) constraint [PK_User_Id] primary key, [Name] nvarchar(8) constraint [UQ_User_Name] unique not null, [Sex] bit not null, [Age] int constraint [CK_User_Age] check([Age] between 18 and 60) not null, [Address] nvarchar(100) constraint [CK_User_Address] check(len([Address]) > 0) not null, [RegTime] datetime constraint [DF_User_RegTime] default(getdate()) not null ) go -- 添加测试数据 insert into [User] values('张三', 1, 23, '成都市大慈寺路1号', '2003-01-23 8:12:34') insert into [User] values('李四', 1, 18, '成都市春熙路102号', '2009-09-03 19:52:02') insert into [User] values('王五', 0, 29, '成都市书院街169号', '2005-12-07 13:41:56') insert into [User] values('赵六', 1, 19, '成都市望平街119号', '2001-10-11 17:32:05') insert into [User] values('周七', 0, 36, '成都市福字街206号', '2006-02-21 18:39:21') insert into [User] values('王八', 1, 25, '成都市顺城街132号', '2003-07-19 23:19:33') go -- 查看测试数据 select [Id], [Name], [Sex], [Age], [Address], [RegTime] from [User] go   

 

 

以上代码编译及调试环境:Microsoft Vista Home Basic SP1简体中文版 + JDK5.0 + Eclipse3.3 + Microsoft SQL Server 2005

 

 

前面提到感觉这样的封装还是有问题,这个问题就是代码中还是存在SQL命令(排除使用存储过程的情况,这里仅体现封装二字),下个版本干脆就将SQL命令全部使用String[]拼接字段名列表,使用Object[]拼接Where条件列表,分别为CRUD操作提供四个具体的方法,这样我觉得应该算作比较优雅了吧!最重要的是,以数组参数方式自动构建SQL命令时可以很方便地在方法内容对所有SQL参数进行部分校验工作,这样可以提高SQL命令的可靠性。

 

使用Java来实现OOP的确显得很优雅,因为自己可以了解其实现的所有细节,行行代码都尽是自己思维的体现。呵呵!妙哉!

 

 

                                                                                               By CodingMouse

                                                                                               2009331

 

 

你可能感兴趣的:(利用Java反射机制+泛型重新封装的BaseDao)