自定义注解+反射封装实现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
2024-12-07 14:05<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
2024-11-19 22:58第一借錢
https://168cash.com.tw/