java反射+注解实现Entity类与Dto类相互转换

Posted Programmer To Architect

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java反射+注解实现Entity类与Dto类相互转换相关的知识,希望对你有一定的参考价值。

 序言

  近期在工作中管理代码时发现,在项目中从Dao层到Service层数据传递中通过大量的get(),set()方法去一个一个的去拿值去赋值,导致代码篇幅过长,对此甚是讨厌,并且严重消耗开发时间。起初找过些关于这块的资料,现在大部分都是Entity类和Dto类的属性名相同的前提下,利用反射实现,太局限了,如果要改成同名,按目前项目的程度去整改工作量太大,不现实。后面看了Spring注解的实现,然后结合找到反射实现资料,突想奇发尝试着用自定义注解+反射方式的去实现,事实证明这方法是可行的。故分享至此,希望能帮到大家。


整体实现三步骤:1.自定义注解 2.工具类方法实现反射 3.使用(测试)

1.自定义注解

 1 import java.lang.annotation.*;
 2 
 3 @Target({ElementType.FIELD,ElementType.TYPE}) //Target 注解的使用域,FIELD表示使用在属性上面,TYPE表示使用在类上面
 4 @Retention(RetentionPolicy.RUNTIME) //Retention 设置注解的生命周期 ,这里定义为RetentionPolicy.RUNTIME 非常关键
 5 @Documented
 6 public @interface RelMapper {
 7     //自定义属性
 8     String value() default ""; 
 9     String type() default "";  // value : status(标记属性值为Y/N的属性) / date(标记属性类型为时间) 
10 }

自定义属性,大家可以根据自己项目中的需求增加不同的属性。

 2.工具类方法实现

 1 import java.lang.reflect.Field;
 2 import java.lang.reflect.Method;
 3 import java.sql.Timestamp;
 4 import java.util.Date;
 5 import org.apache.commons.lang.StringUtils;
 6 import com.ctccbs.common.annotation.RelMapper;
 7 
 8 public class RelationMapperUtils {
 9 /**
10      * Entity and Dto Mapper  
11      * @param entry
12      * @param dto
13      * @param enToDto  
14      *             ture  : Entity To Dto (defult)
15      *             false : Dto To Entry
16      *     Rule: 
17      *         实现相互转换前提: Dto field name(dto和entry的field name相同并且 类上有@RelMapper) 或 field的@RelMapper(value="Entity field name") 满足其一即可转换  
18      * @return
19      * @throws Exception
20      */
21     public static Object entryAndDtoMapper(Object entity, Object dto) throws Exception{
22         return EnAndDtoMapper(entity, dto,true);
23     }
24     
25     public static Object entryAndDtoMapper(Object entity, Object dto,boolean enToDto) throws Exception{
26         return EnAndDtoMapper(entity, dto,false);
27     }
28     //last version 
29     public static Object EnAndDtoMapper(Object entry, Object dto,boolean enToDto) throws Exception{
30         if(enToDto == true ? entry == null : dto == null){ return null;}
31         Class<? extends Object> entryclazz = entry.getClass();    //获取entity类
32         Class<? extends Object> dtoclazz = dto.getClass();    //获取dto类
33         boolean dtoExistAnno = dtoclazz.isAnnotationPresent(RelMapper.class);    //判断类上面是否有自定义注解
34         Field [] dtofds = dtoclazz.getDeclaredFields();    //dto fields 
35         Field [] entryfds = entryclazz.getDeclaredFields();    //entity fields
36         Method entrys[] = entryclazz.getDeclaredMethods();    //entity methods
37         Method dtos[] = dtoclazz.getDeclaredMethods();    //dto methods
38         String mName,fieldName,dtoFieldType=null,entFieldType=null,dtoMapName = null,dtoFieldName =null;Object value = null;
39         for(Method m : (enToDto ? dtos : entrys)) {    //当 enToDto=true 此时是Entity转为Dto,遍历dto的属性
40             if((mName=m.getName()).startsWith("set")) {    //只进set方法
41                 fieldName = mName.toLowerCase().charAt(3) + mName.substring(4,mName.length());  //通过set方法获得dto的属性名
42                 tohere:
43                 for(Field fd: dtofds) {
44                     fd.setAccessible(true);    //setAccessible是启用和禁用访问安全检查的开关
45                     if(fd.isAnnotationPresent(RelMapper.class)||dtoExistAnno){    //判断field上注解或类上面注解是否存在
46                         //获取与Entity属性相匹配的映射值(两种情况:1.该field上注解的value值(Entity的field name 和Dto 的field name 不同)  2.该field本身(本身则是Entity的field name 和Dto 的field name 相同))
47                         dtoMapName = fd.isAnnotationPresent(RelMapper.class) ? (fd.getAnnotation(RelMapper.class).value().toString().equals("")?fd.getName().toString():fd.getAnnotation(RelMapper.class).value().toString()):fd.getName().toString();
48                         if(((enToDto ? fd.getName() : dtoMapName)).toString().equals(fieldName)) { 
49                             dtoFieldType = fd.getGenericType().toString().substring(fd.getGenericType().toString().lastIndexOf(".") + 1); // 获取dto属性的类型(如 private String field 结果 = String)
50                             for(Field fe : entryfds) {
51                                 fe.setAccessible(true);
52                                 if(fe.getName().toString().equals(enToDto ? dtoMapName : fieldName) ) {//遍历Entity类的属性与dto属性注解中的value值匹配
53                                     entFieldType = fe.getGenericType().toString().substring(fe.getGenericType().toString().lastIndexOf(".") + 1); //获取Entity类属性类型
54                                     dtoFieldName = enToDto ? dtoMapName : fd.getName().toString();
55                                     break tohere;
56                                 }
57                             }
58                         }
59                     }
60                 }
61                 if(dtoFieldName!= null && !dtoFieldName.equals("null")) {
62                     for(Method md : (enToDto ? entrys : dtos)) {
63                         if(md.getName().toUpperCase().equals("GET"+dtoFieldName.toUpperCase())){
64                             dtoFieldName = null; 
65                             if(md.invoke(enToDto ? entry : dto) == null) { break;} //去空操作
66                             //Entity类field 与Dto类field类型不一致通过TypeProcessor处理转换
67                             value = (entFieldType.equals(dtoFieldType))? md.invoke(enToDto ? entry : dto) :TypeProcessor(entFieldType, dtoFieldType,md.invoke(enToDto ? entry : dto),enToDto ? true : false);
68                             m.invoke(enToDto ? dto : entry, value); //得到field的值 通过invoke()赋值给要转换类的对应属性
69                             value = null;
70                             break;
71                         }
72                     }
73                 }
74             }
75         }
76         return enToDto ? dto : entry;
77     }
78     
79     //类型转换处理
80     public static Object TypeProcessor(String entFieldType,String dtoFieldType, Object obj,boolean enToDto) {
81         if(entFieldType.equals(dtoFieldType)) return obj;
82         
83         if(!entFieldType.equals(dtoFieldType)) {
84             switch(entFieldType) {
85                 case "Date":
86                     return (enToDto)?TypeConverter.dateToString((Date) obj):TypeConverter.stringToDate(obj.toString());
87                 case "Timestamp":
88                     return TypeConverter.timestampToTimestampString((Timestamp)obj);
89                 case "Integer":
90                     return (enToDto) ? obj.toString() : Integer.parseInt((String)obj) ;
91             }
92         }
93         return obj;
94     }

 上面EnAndDtoMapper()方法的实现是Entity和Dto之间互相转换结合在一起,enToDto = true 表示的是Entity转Dto实现,false则相反。

3. 如何使用?

  1)Entity类 与 Dto类对应

  2)调用

public static void main(String[] args) {
        //Entity数据转成Dto数据集
        Person person = dao.getPersonRecord();
        RelationMapperUtils.entryAndDtoMapper(person,new PersonDto());
        //Dto数据转成Entity数据
        RelationMapperUtils.entryAndDtoMapper(new Person(),personDto,false);
    }

以上便能自动实现数据的转换,大量减少get,set的代码,省事!!! 
大家如果还有其他的需求都可以往方法中添加,来达到适合项目的需求,整体下来扩展性算还不错。

希望对大家有所帮助,有不解或不足的代码欢迎点出。大家一起进步

 


 

最后,本博主是入博不久,会定期更新所学所感所获,希望各位爷喜欢,一起成长!!!喜欢的可以关注喔

-------------------------------

喜欢老夫的点波关注呗^ ^ ,Thank ! ! ! !

 

以上是关于java反射+注解实现Entity类与Dto类相互转换的主要内容,如果未能解决你的问题,请参考以下文章

Java自定义注解注解实现实体类与数据库表字段的映射

DO-DTO相互转换时的性能优化

java中的映射是怎样实现的?

反射读取注解信息

JPA-基本注解

学习笔记 - MapStruct 映射工具