将ResultSet转实体类

将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;
}
  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值