ResultSet自动封装数据到实体对象
ResultSet 自动封装数据到实体对象
在传统的 JDBC 开发中,将 ResultSet 结果集手动映射到 Java 实体对象(Entity)往往需要编写大量重复的 setter 代码。本文将介绍一种基于 Java 反射(Reflection)机制的工具类 AntResult,它能够自动根据数据库列名匹配实体类的属性,并将数据封装到对象集合中。
核心工具类:AntResult
该类主要通过反射获取实体类的 setter/getter 方法,并根据 ResultSetMetaData 中的列名动态调用相应方法完成数据填充。
package com.daicy.util;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class AntResult {
/**
* 匹配 Bean 的 getter 和 setter 方法
* @param clazz 类对象
* @param beanProperty 属性名
* @return [getter, setter]
*/
private Object[] beanMatch(Class clazz, String beanProperty) {
Object[] result = new Object[2];
char beanPropertyChars[] = beanProperty.toCharArray();
beanPropertyChars[0] = Character.toUpperCase(beanPropertyChars[0]);
String s = new String(beanPropertyChars);
// 构建可能的方法名
String names[] = { ("set" + s).intern(), ("get" + s).intern(),
("is" + s).intern(), ("write" + s).intern(),
("read" + s).intern() };
Method getter = null;
Method setter = null;
Method methods[] = clazz.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
// 只取公共字段
if (!Modifier.isPublic(method.getModifiers()))
continue;
String methodName = method.getName().intern();
for (int j = 0; j < names.length; j++) {
String name = names[j];
if (!name.equals(methodName))
continue;
if (methodName.startsWith("set") || methodName.startsWith("read"))
setter = method;
else
getter = method;
}
}
result[0] = getter;
result[1] = setter;
return result;
}
/**
* 注册 Bean 属性值
* @param object 对象实例
* @param beanProperty 属性名
* @param value 字符串值
*/
private void beanRegister(Object object, String beanProperty, String value) {
Object[] beanObject = beanMatch(object.getClass(), beanProperty);
Object[] cache = new Object[1];
Method getter = (Method) beanObject[0];
Method setter = (Method) beanObject[1];
try {
// 通过 get 获得方法类型
String methodType = getter.getReturnType().getName();
if (methodType.equalsIgnoreCase("long")) {
cache[0] = new Long(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("int") || methodType.equalsIgnoreCase("integer")) {
cache[0] = new Integer(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("short")) {
cache[0] = new Short(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("float")) {
cache[0] = new Float(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("double")) {
cache[0] = new Double(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("boolean")) {
cache[0] = new Boolean(value);
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("java.lang.String")) {
cache[0] = value;
setter.invoke(object, cache);
} else if (methodType.equalsIgnoreCase("java.io.InputStream")) {
// 暂不处理 InputStream
} else if (methodType.equalsIgnoreCase("char")) {
cache[0] = (Character.valueOf(value.charAt(0)));
setter.invoke(object, cache);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据 Connection 和 SQL 获取对象集合
*/
public Collection get(final Connection connection, final Class clazz, final String sql) {
PreparedStatement ptmt = null;
ResultSet rset = null;
Collection collection = null;
try {
ptmt = connection.prepareStatement(sql);
rset = ptmt.executeQuery();
collection = get(rset, clazz);
} catch (SQLException e) {
System.err.println(e.getMessage());
} finally {
try {
// 关闭 rs 并释放资源
if (rset != null) {
rset.close();
rset = null;
}
// 关闭 ps 并释放资源
if (ptmt != null) {
ptmt.close();
ptmt = null;
}
} catch (SQLException e) {
System.err.println(e.getMessage());
}
}
return collection;
}
/**
* 根据 ResultSet 获取对象集合
*/
public Collection get(final ResultSet result, final Class clazz) {
Collection collection = null;
try {
ResultSetMetaData rsmd = result.getMetaData();
// 获得数据列数
int cols = rsmd.getColumnCount();
// 创建等同数据列数的 arraylist 类型 collection 实例
collection = new ArrayList(cols);
// 遍历结果集
while (result.next()) {
Object object = null;
try {
// 从 class 获得对象实体
object = clazz.newInstance();
} catch (Exception e) {
// 忽略实例化异常
}
// 循环每条记录
for (int i = 1; i <= cols; i++) {
beanRegister(object, rsmd.getColumnName(i), result.getString(i));
}
// 将数据插入 collection
collection.add(object);
}
} catch (SQLException e) {
System.err.println(e.getMessage());
} finally {
// 资源关闭由上层调用者处理或在此补充
}
return collection;
}
//===========================================//
public static void main(String[] args) {
// 加载驱动
try {
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// 连接字符串
String url = "jdbc:sqlserver://localhost:1433;databaseName=test";
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 创建连接
connection = DriverManager.getConnection(url, "sa", "javalife");
AntResult test = new AntResult();
// Ltest 是我测试用类,实际操作请注入相关对象,支持 set,get,is,read,writer 为前缀数据对,更多请继续添加。
Collection collection = test.get(connection, com.ant.po.UserInfo.class, "select * from userinfo");
for (Iterator it = collection.iterator(); it.hasNext();) {
com.ant.po.UserInfo ltest = (com.ant.po.UserInfo) it.next();
System.out.println(ltest.getUserid() + ":" + ltest.getUserName());
}
} catch (SQLException e) {
// SQL 异常,用于抛出 SQL 语句处理中所引发的错误。
System.err.println(e.getMessage());
} finally {
try {
// 关闭 connection 并释放资源
if (connection != null) {
connection.close();
connection = null;
}
} catch (SQLException e) {
e.printStackTrace();
}
// 如果关闭时产生异常将由此抛出
}
}
}实体类示例:UserInfo
为了配合上述工具类,实体类的属性名需要与数据库列名保持一致(区分大小写取决于数据库配置),并提供标准的 public getter/setter 方法。
// UserInfo.java 实体类
package com.ant.entity;
public class UserInfo {
private int userID;
private String userName;
private String userPass;
public int getUserID() {
return userID;
}
public void setUserID(int userID) {
this.userID = userID;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPass() {
return userPass;
}
public void setUserPass(String userPass) {
this.userPass = userPass;
}
}实现原理与注意事项
- 命名规范:类中的属性名需要和数据库的列名保持一致。工具类通过
ResultSetMetaData.getColumnName()获取列名,并尝试匹配实体类中set+ 首字母大写的属性名方法。 - 反射机制:该实现利用了 Java 反射机制动态调用方法。这与某些 MVC 框架(如 Struts 的 FormBean)封装数据的原理类似,都是通过属性名匹配进行值注入。
- 类型转换:
beanRegister方法中手动处理了常见基本类型(int, long, boolean 等)的字符串转换。在实际生产中,直接调用ResultSet的 typed getters(如getInt,getString)通常比通过字符串解析更安全且高效。 - 扩展性:当前实现支持以
set,get,is,read,write为前缀的方法对。如需支持更多复杂类型(如 Date、BigDecimal 等),需在beanRegister中继续添加分支逻辑。
说明
版本与时效性说明:
本文代码示例属于早期 Java 反射实践方案。
- 代码中使用的
Class.newInstance()方法已在 Java 9 中被标记为 deprecated,现代开发建议使用getDeclaredConstructor().newInstance()。- 基本类型包装类构造函数(如
new Integer(value))也已过时,建议使用valueOf或直接利用ResultSet的类型化方法。- 在实际企业级开发中,建议直接使用成熟的 ORM 框架(如 MyBatis、Hibernate)或 Spring JDBC Template,以获得更好的性能、安全性及维护性。
- 本文代码主要用于理解反射与 JDBC 底层交互原理,不建议直接用于新版本 Java 的生产环境。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
本文地址:https://1diff.fun/archives/resultset-zi-dong-feng-zhuang-shu-ju-dao-shi-ti-dui-xiang.html
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。