自定义注解+反射封装实现JdbcTemplate对象自定义转换
在进行老项目升级维护的时候很多地方用到了JdbcTemplate,由于查询的数据比较复杂,如果我们的数据库返回字段不是驼峰命名,需要手动对对象类型进行转换,,比较麻烦,今天借助自定义注解+反射的方式实现高度自定义的JdbcTemplate的对象转换以及相应扩展
在开始之前我们看看最终调用效果是什么样的
String sql ="select twh.ID TWHID,tu.USER_ID USERID from U_TWH_USER tu "+
"left join TWH twh on tu.TWH_ID=twh.ID "+
" where tu.USER_ID='"+userId+"' AND twh.DZYZ_FLAG='是' limit 1";
List<SignAuthResp> query = jdbcTemplate.query(sql, new SignAuthResp());
jdbcTemplate.query 大家都很熟悉了,快按照步骤来设计吧,感受程序设计的快乐!!!
第一步:定义映射类型
我们进行对象转换当然需要定义映射类先,我们希望在用的时候 让我们的映射类去集成一个转换类,并且设计转换规则,这里只是演示就随便设计一个。
@Data
public class SignAuthResp {
public String tjyId;
public String twhId;
public String userId;
}
我们希望对这个类进行转换,按照jdbcTemplate的转换规则,我们需要在类里面去继承RowMapper然后实现里面的mapRow方法,如果每一个类都这样写就太麻烦了,我这里想到的是设计一个通用的父类让我们映射对象继承这个类,在父类里面去继承RowMapper实现mapRow方法。见如下
@Data
public class SignAuthResp extends ConvertResp<SignAuthResp> {
public String tjyId;
public String twhId;
public String userId;
}
近一步设计,此时我们可以在父类里面去写转换逻辑,那么我们想到如果要得到通用的效果,我们可以设置一些自定义注解,比如适配大写,适配驼峰,或者强制映射等,最后我们的映射类代码如下
@Data
@AllUpper //将结果全部转为大写映射
public class SignAuthResp extends ConvertResp<SignAuthResp> {
@MapperFrom(from = "USERID") //强制将USERID映射到tjyId
public String tjyId;
@MapperFrom(from = "TWHID") //强制将USERID映射到twhId
public String twhId;
public String userId; //默认大写映射
}
第二步:设计转换父类
因为比较多比较复杂,完整代码如下
/**
* 返回对象数据映射类
* @param <T>
*/
public class ConvertResp<T> implements RowMapper<T> {
private final Class<T> respBean;
public ConvertResp() {
try {
throw new RuntimeException(" ");
}catch (Exception e){
StackTraceElement[] stackTrace = e.getStackTrace();
StackTraceElement stackTraceElement = stackTrace[1];
String className = stackTraceElement.getClassName();
try {
respBean = (Class<T>) Class.forName(className);
}catch (Exception r){
throw new IllegalArgumentException("反射对象失败! "+className);
}
}
}
@Override
public T mapRow(ResultSet rs, int rowNum) throws SQLException {
return handleConvert(respBean, rs);
}
public T handleConvert(Class<T> tClass,ResultSet rs){
Field[] fields = respBean.getFields();
T t = null;
try {
t = respBean.newInstance();
}catch (Exception e){
throw new IllegalArgumentException("反射获取对象失败!"+respBean.getName());
}
for (Field field:fields){
field.setAccessible(true); // 设置为可访问
if (respBean.isAnnotationPresent(AllUpper.class)) {
setValueAllUpper(t,field,rs);
}
}
return t;
}
public void setValueAllUpper(T t,Field field,ResultSet rs) {
try {
//优先处理映射的值
if(field.isAnnotationPresent(MapperFrom.class)){
MapperFrom annotation = field.getAnnotation(MapperFrom.class);
String value = annotation.from();
if (field.getType().equals(Integer.class)) {
field.set(t, rs.getInt(value.toUpperCase()));
}
if (field.getType().equals(String.class)) {
field.set(t, rs.getString(value.toUpperCase()));
}
if (field.getType().equals(Long.class)) {
field.set(t, rs.getLong(value.toUpperCase()));
}
if (field.getType().equals(Date.class)) {
field.set(t, rs.getDate(value.toUpperCase()));
}
if (field.getType().equals(Boolean.class)) {
field.set(t, rs.getBoolean(value.toUpperCase()));
}
if (field.getType().equals(Double.class)) {
field.set(t, rs.getDouble(value.toUpperCase()));
}
return;
}
if (field.getType().equals(Integer.class)) {
if (field.isAnnotationPresent(MapperValue.class)){
MapperValue annotation = field.getAnnotation(MapperValue.class);
field.set(t, annotation.valueInt());
return;
}
field.set(t, rs.getInt(field.getName().toUpperCase()));
return;
}
if (field.getType().equals(String.class)) {
if (field.isAnnotationPresent(MapperValue.class)) {
MapperValue annotation = field.getAnnotation(MapperValue.class);
String value = annotation.value();
if(!value.trim().isEmpty()){
field.set(t, value);
return;
}
String valueStr = annotation.valueString();
field.set(t, valueStr);
return;
}
field.set(t, rs.getString(field.getName().toUpperCase()));
return;
}
if (field.getType().equals(Long.class)) {
if (field.isAnnotationPresent(MapperValue.class)) {
MapperValue annotation = field.getAnnotation(MapperValue.class);
field.set(t, annotation.valueLong());
return;
}
field.set(t, rs.getLong(field.getName().toUpperCase()));
return;
}
//date格式暂时不处理
if (field.getType().equals(Date.class)) {
if (field.isAnnotationPresent(MapperValue.class)) {
MapperValue annotation = field.getAnnotation(MapperValue.class);
String date = annotation.valueDate();
if(date.trim().equals("now")){
field.set(t, new Date());
return;
}
}
field.set(t, rs.getDate(field.getName().toUpperCase()));
return;
}
if (field.getType().equals(Boolean.class)) {
if (field.isAnnotationPresent(MapperValue.class)) {
MapperValue annotation = field.getAnnotation(MapperValue.class);
field.set(t, annotation.valueBoolean());
return;
}
field.set(t, rs.getBoolean(field.getName().toUpperCase()));
}
if (field.getType().equals(Double.class)) {
if (field.isAnnotationPresent(MapperValue.class)) {
MapperValue annotation = field.getAnnotation(MapperValue.class);
field.set(t, annotation.valueDouble());
return;
}
field.set(t, rs.getBoolean(field.getName().toUpperCase()));
}
}catch (Exception e){
if(e.getMessage().contains("无效的列名")){
if(!field.getName().toUpperCase().equals("QIANZIFLAG")){
System.out.println("数据映射异常 无效的列名:"+field.getName().toUpperCase() +" | FiledName === >>>"+field.getName());
}
}else{
System.out.println("数据映射异常 FiledName === >>>"+field.getName());
}
}
}
}
设计巧妙之处在于,子类继承过来之后 会调用父类构造方法,我们在父类需要拿到子类的Class就能够进行反射了,那么我们如何在父类拿到子类的Class呢?每次都让子类去传吗? 不!!! 我的做法是在父类的构造方法里手动抛出一个异常进行捕获 异常一般有堆栈信息可跟踪能够拿到子类转递过来的全路径,通过Class.forName(路径)进行反射,反射后在mapRow中去处理全部的转换逻辑,对String、Interger、Long、.....进行不同类型的转换 其中也可以包括时间、BigDecimal等等 具体转换逻辑见代码...
第三步:编写自定义注解
这个自定义注解主要用于标识类以及字段 标识强制转换、大小写适配等等,主要转换逻辑还是在ConvertResp 注解代码如下
1.AllUpper.java 注解类 - 被注解的类会按照大写匹配属性
import java.lang.annotation.*;
@Documented
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AllUpper {
}
2.MapperFrom.java 注解字段 - 被注解的字段会按照from内容强制进行匹配转换
import java.lang.annotation.*;
@Documented
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapperFrom {
String from() default "";
}
3.MapperValue.java 注解字段 - 被注解的字段会按照value强制填充值
import java.lang.annotation.*;
@Documented
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MapperValue {
String value() default "";
String valueString() default "";
int valueInt() default 0;
long valueLong() default 0L;
double valueDouble() default 0d;
boolean valueBoolean() default false;
String valueDate() default "";
}
结尾,这种方式扩展性极强,你可以通过这种方式转换其他自定义对象!
希望本篇文章对你的开发有所帮助~~
by gityyge
RayfordTof
2024-11-19 22:58第一借錢
https://168cash.com.tw/