壹影博客.
我在下午4点钟开始想你
自定义注解+反射封装实现JdbcTemplate对象自定义转换
  • 2024-11-14日
  • 1评论
  • 140围观

自定义注解+反射封装实现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

Lv.1 @回复 沙发

第一借錢

https://168cash.com.tw/

渝ICP备19011465号 | 渝ICP备19011465号-1