将ResultSet转实体类
sqlExecutor.executeQuery
的执行结果的返回值是ResultSet:package java.sql;
一般在程序中我们需要把查询结果转为实体类返回给前端,此处可以使用的方法:
ResultSet转实体类方法1 2
1:resultSet.getXXX(columnIndex) 根据列的序号依次取值,并赋值给实体类的具体属性
定义一个实体类比如EntityDefine.class
public class EntityDefine implements Serializable {
private static final long serialVersionUID = 3394089767543785764L;
// 定义属性
private String key;
private Integer count;
private Date updateTime;
...;
// get set 方法
public String getKey(){
return this.key;
}
public String setKey(String key) {
this.key = key;
}
...;
}
定义一个处理ResultSet的方法handleResultSet
private EntityDefine handleResultSet(ResultSet resultSet) throws SQLException {
EntityDefine entity = new EntityDefine();
int col = 1;
// 字符型
entity.setKey(resultSet.getString(col++));
// 整型
entity.setCount(resultSet.getInt(col++));
// 时间戳
entity updateTime = resultSet.getTimestamp(col++);
entity.setUpdateTime(updateTime);
entity.setXXX(resultSet.getString(col++));
return entity;
}
以上即可将ResultSet转为实体类。
优点是简单 ;
缺点是需要对ResultSet中的数据提前调试,确定每一列对应的属性(一般和数据库列的顺序一致)
2:实体类+注解+反射
注解是一种在Java中用来提供元数据的工具,它们可以在编译时和运行时为代码添加信息,并且可以被反射机制获取和处理。
(1)首先我们定义一个注解,用在实体类的所有属性上,都加上;
需要在注解中声明对应的数据库字段名;
属性对应的get方法可以编码时直接给出(如果属性值过多则配置会麻烦),或者用方法动态拼接(例子用这种方法);
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface MyResultSetFiled {
/** 对应的数据库字段名 */
String dbFiled();
Class<?> fieldType() default String.class;
/** 属性对应的get方法 */
String get() default "";
/** 是否需要自动补充当前时间 */
boolean autoDate() default false;
}
关于注解:
注解按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;这3个生命周期分别对应于:Java源文件(.java文件) —> .class文件 —> 内存中的字节码。
那怎么来选择合适的注解生命周期呢?
首先要明确生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。
一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;
如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;
如果只是做一些检查性的操作,比如 @Override 和 @SuppressWarnings,则可选用 SOURCE 注解。
@Target 指的是注解的作用目标。
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包
(2)定义一个实体类比如EntityDefine.class
public class EntityDefine implements Serializable {
private static final long serialVersionUID = 3394089767543785764L;
// 定义属性,并添加注解
@MyResultSetFiled(dbFiled = "key")
private String key;
@MyResultSetFiled(dbFiled = "count")
private Integer count;
@MyResultSetFiled(dbFiled = "update_time")
private Date updateTime;
...;
// get set 方法
public String getKey(){
return this.key;
}
public String setKey(String key) {
this.key = key;
}
...;
}
注意此处dbFiled要是数据库中的字段名,一般是采用下划线命名法,与实体类中定义属性的驼峰命名法有所区别。
可以将dbFiled后面跟的这些值封装成常量,便于维护。
(3)定义一个抽象类,在此方法中直接给类的各种属性封装好对应的值,所以建议做成构造函数的实现形式
public abstract class ResultSetWrapper implements Serializable{
private static final long serialVersionUID = -2088813051606973972L;
/**
* 给给定的类的属性赋值,根据注解获取数据库的字段,从ResultSet中获取对应的值value
* 动态拼接对应属性的Set方法
* 利用反射,执行Set方法,将value赋值给对应的属性
* 此方法没有返回值,给定的对象在该方法中执行一遍set后返回
* @param resultSet 数据库查询结果集
* @param clazz 要转化的实体类
*/
public void setData(ResultSet resultSet, Class<?> clazz) {
try {
// 获取类中所有定义的属性
Field[] fields = clazz.getDeclaredFields();
// 遍历
for(Field field : fields) {
// 获取属性上的注解信息
MyResultSetFiled resultSetFiled = field.getAnnotation(AuditField.class);
if(resultSetFiled==null) continue;
// resultSet.getObject(列名),
// 此方法可以配合我们获取注解中的数据库字段名使用,来获取对应的列的值
Object value = resultSet.getObject(resultSetFiled.dbFiled());
// 动态凭借对应属性的Set方法
String fieldSetName = parSetName(field.getName());
// 反射获取方法
Method fieldSetMet = clazz.getMethod(fieldSetName, field.getType());
if (null != value && !"".equals(value)) {
String fieldType = value.getClass().getSimpleName();
// 根据value的不同:String, Int, Data等,执行不同的set方法,即invoke(this, value)
if ("String".equals(fieldType)) {
fieldSetMet.invoke(this, value);
} else if ("CLOB".equals(fieldType)) {
// Clob转String,见第三部分《Clob转String》介绍
String strClob = ClobToString((Clob) value);
fieldSetMet.invoke(this, strClob);
} else if ("Date".equals(fieldType) || "TIMESTAMP".equals(fieldType)) {
Date temp = parseDate(value.toString());
fieldSetMet.invoke(this, temp);
} else if ("Integer".equals(fieldType)
|| "int".equals(fieldType)) {
Integer intval = Integer.parseInt(value.toString());
fieldSetMet.invoke(this, intval);
} else if ("Long".equalsIgnoreCase(fieldType)) {
Long temp = Long.parseLong(value.toString());
fieldSetMet.invoke(this, temp);
} else if ("Double".equalsIgnoreCase(fieldType)) {
Double temp = Double.parseDouble(value.toString());
fieldSetMet.invoke(this, temp);
} else if ("Boolean".equalsIgnoreCase(fieldType)) {
Boolean temp = Boolean.parseBoolean(value.toString());
fieldSetMet.invoke(this, temp);
} else {
System.out.println("not supper type" + fieldType);
}
}
}
}catch(NoSuchMethodException|SecurityException|IllegalAccessException|IllegalArgumentException|InvocationTargetException|SQLException e) {
}
}
/**
* 拼接在某属性的 set方法
* @param fieldName 字段名
* @return String setXXX
*/
private String parSetName(String fieldName) {
if (null == fieldName || "".equals(fieldName)) {
return null;
}
return "set" + fieldName.substring(0, 1).toUpperCase()
+ fieldName.substring(1);
}
}
实体类EntityDefine.class完善,继承上述抽象类,并在构造方法中,声明:
public class EntityDefine extends ResultItem {
...;
public EntityDefine() {}
// 构造方法
public EntityDefine (ResultSet resultSet){
super();
this.setData(resultSet, EntityDefine.class);
}
...;
}
(4)程序中处理:
new EntityDefine(resultSet);
即可获取到一个被完整赋值的对象实例;
优点:定义好了注解、构造函数之后,在使用时,直接new 即可,非常方便
缺点:配置复杂
Clob转String
在ResultSet转实体类中,存在Clob类型转换问题,需要转为String
/**
* CLOB转String
* @param clob clob
* @return 字符串
*/
public static String ClobToString(Clob clob) {
String reString = "";
try {
// 得到流
Reader is = clob.getCharacterStream();
BufferedReader br = new BufferedReader(is);
String s = br.readLine();
StringBuilder sb = new StringBuilder();
// 执行循环将字符串全部取出付值给StringBuilder,由StringBuilder转成STRING
while (s != null) {
sb.append(s).append("\n");
s = br.readLine();
}
reString = sb.toString();
} catch (SQLException|IOException e) {
e.printStackTrace();
}
return reString;
}