简述电子商务网站开发的研究意义网站优化费用报价明细
将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转成STRINGwhile (s != null) {sb.append(s).append("\n");s = br.readLine();}reString = sb.toString();} catch (SQLException|IOException e) {e.printStackTrace();}return reString;
}