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

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

发表评论

EQiblyAlurl

Lv.1 @回复 板凳

<h2>  详细的转子平衡过程  </h2>

<h3>  准备必要的工具  </h3>
<ul>
<li>  加速度计、激光转速传感器、安装架、软件应用程序和其他工具.  </li>
<li>  在仪器和计算机之间建立USB连接,确保软件设置正确。  </li>
</ul>

<h3>  设置传感器  </h3>
<ul>
<li>  在振动幅度最大的区域,通常靠近轴承支架,将振动传感器牢固地连接到机器外壳上。  </li>
<li>   将激光转速表(相位角传感器)定位,使其瞄准转子。 在转子上贴上反光带,以获得精确的相位角读数.    </li>
</ul>

<h3> 启动软件  </h3>
<ul>
<li>   在您的计算机上启动Balanset程序。  </li>
<li>   选择合适的平衡模式:单平面或双平面,具体取决于转子类型和您的具体要求。    </li>
</ul>

<h3>  测量初始振动  </h3>
<a href="https://vibromera.eu/wp-content/uploads/2024/11/2-Camera_01.png" target="_blank">
<img src="https://vibromera.eu/wp-content/uploads/2024/11/2-Camera_01.png" alt="2-Camera_01" style="width: 50%; display: block; margin-bottom: 10px;">
</a>
<ul>
<li>   以其预期的工作速度操作转子。  </li>
<li>   该应用程序将获取振动幅度、旋转速度和相角的数据,以确定初始不平衡状态。  </li>
</ul>

<h3> 安装测试重量  </h3>
<a href="https://vibromera.eu/wp-content/uploads/2024/11/3-Camera-2_01.png" target="_blank">
<img src="https://vibromera.eu/wp-content/uploads/2024/11/3-Camera-2_01.png" alt="3-Camera-2_01" style="width: 50%; display: block; margin-bottom: 10px;">
</a>
<ul>
<li>   停止转子并在转子上的特定位置附加试重。 重量的质量可以在软件中指定(例如,以克为单位)。    </li>
<li>  恢复转子运行,程序将捕获由此产生的振动幅度和相位变化。  </li>
</ul>
<a href="https://vibromera.eu/wp-content/uploads/2024/11/5-Camera_01.png" target="_blank">
<img src="https://vibromera.eu/wp-content/uploads/2024/11/5-Camera_01.png" alt="5-Camera_01" style="width: 50%; display: block; margin-bottom: 10px;">
</a>


<h3> 计算补偿重量  </h3>
<ul>
<li>  该软件使用测量值自动计算必要的补偿重量的大小和放置角度。  </li>
<li>   计算结果通过图表和图形以数字和视觉方式显示。  </li>
</ul>
<a href="https://vibromera.eu/wp-content/uploads/2024/02/Bs1ManualEngV156-May2023-10448629.png" target="_blank">
<img src="https://vibromera.eu/wp-content/uploads/2024/02/Bs1ManualEngV156-May2023-10448629.png" alt="Bs1 Manual" style="width: 30%; display: block; margin-bottom: 10px;">
</a>

<h3> 安装补偿重量  </h3>
<ul>
<li>  将计算出的校正重量以指定的位置和角度安装到转子上.  </li>
<li>   可以进行定期检查,以确保平衡过程有效地减少振动。  </li>
</ul>
<a href="https://vibromera.eu/wp-content/uploads/2024/11/1-Camera-2_01.png" target="_blank">
<img src="https://vibromera.eu/wp-content/uploads/2024/11/1-Camera-2_01.png" alt="1-Camera-2_01" style="width: 50%; display: block; margin-bottom: 10px;">
</a>

<h3> 平衡过程的验证和结论  </h3>
<ul>
<li>   安装校正砝码后,再次运行转子并检查残余振动水平。    </li>
<li>  如果测量的振动落在ISO1940定义的公差范围内,则认为平衡过程是成功的。  </li>
<li>   如果振动仍然过大,重复平衡步骤,根据需要进一步调整校正重量。  </li>
</ul>

<h3> 生成平衡结果的文档  </h3>
<ul>
<li>  所有平衡结果都被记录并存档在软件中,您可以从中生成一份可打印的报告,总结振动水平、补偿重量及其安装位置。  </li>
</ul>

<h3>  平衡后核对表  </h3>
<ul>
<li>  验证所有平衡重和测量传感器的安全连接.  </li>
<li>   确认转子自由而安静地旋转,没有任何不寻常的声音或振动。  </li>
<li>   在转子集成到更复杂的系统中的情况下,确保所有相关部件的正确操作和相互作用。  </li>
</ul>

<p>  遵循此程序可以实现精确的平衡,最大限度地减少振动,并延长设备的使用寿命。  </p>




Instagram: https://www.instagram.com/vibromera_ou/
Youtube :  https://youtu.be/guA6XJ-ArZM?si=vmkuX7RILzKBl0zL  
我们的网站  <a href="https://vibromera.eu  
"> 平衡架 </a>

RayfordTof

Lv.1 @回复 沙发

第一借錢

https://168cash.com.tw/

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