反射+註解實現Entity類與Dto類相互轉換

 個人博客請訪問 http://www.x0100.top        

序言

近期在工作中管理代碼時發現,在項目中從Dao層到Service層數據傳遞中通過大量的get(),set()方法去一個一個的去拿值去賦值,導致代碼篇幅過長,對此甚是討厭,並且嚴重消耗開發時間。

起初找過些關於這塊的資料,現在大部分都是Entity類和Dto類的屬性名相同的前提下,利用反射實現,太侷限了,如果要改成同名,按目前項目的程度去整改工作量太大,不現實。

後面看了Spring註解的實現,然後結合找到反射實現資料,突想奇發嘗試着用自定義註解+反射方式的去實現,事實證明這方法是可行的。故分享至此,希望能幫到大家。

整體實現三步驟:

  1. 自定義註解

  2. 工具類方法實現反射

  3. 使用(測試)

1.自定義註解

import java.lang.annotation.*;

@Target({ElementType.FIELD,ElementType.TYPE}) //Target 註解的使用域,FIELD表示使用在屬性上面,TYPE表示使用在類上面
@Retention(RetentionPolicy.RUNTIME) //Retention 設置註解的生命週期 ,這裏定義爲RetentionPolicy.RUNTIME 非常關鍵
@Documented
public @interface RelMapper {
    //自定義屬性
    String value() default ""; 
    String type() default "";  // value : status(標記屬性值爲Y/N的屬性) / date(標記屬性類型爲時間) 
}

自定義屬性,大家可以根據自己項目中的需求增加不同的屬性。

2.工具類方法實現

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import org.apache.commons.lang.StringUtils;
import com.ctccbs.common.annotation.RelMapper;

public class RelationMapperUtils {
/**
     * Entity and Dto Mapper  
     * @param entry
     * @param dto
     * @param enToDto  
     *             ture  : Entity To Dto (defult)
     *             false : Dto To Entry
     *     Rule: 
     *         實現相互轉換前提: Dto field name(dto和entry的field name相同並且 類上有@RelMapper) 或 field的@RelMapper(value="Entity field name") 滿足其一即可轉換  
     * @return
     * @throws Exception
     */
    public static Object entryAndDtoMapper(Object entity, Object dto) throws Exception{
        return EnAndDtoMapper(entity, dto,true);
    }

    public static Object entryAndDtoMapper(Object entity, Object dto,boolean enToDto) throws Exception{
        return EnAndDtoMapper(entity, dto,false);
    }
    //last version 
    public static Object EnAndDtoMapper(Object entry, Object dto,boolean enToDto) throws Exception{
        if(enToDto == true ? entry == null : dto == null){ return null;}
        Class<? extends Object> entryclazz = entry.getClass();    //獲取entity類
        Class<? extends Object> dtoclazz = dto.getClass();    //獲取dto類
        boolean dtoExistAnno = dtoclazz.isAnnotationPresent(RelMapper.class);    //判斷類上面是否有自定義註解
        Field [] dtofds = dtoclazz.getDeclaredFields();    //dto fields 
        Field [] entryfds = entryclazz.getDeclaredFields();    //entity fields
        Method entrys[] = entryclazz.getDeclaredMethods();    //entity methods
        Method dtos[] = dtoclazz.getDeclaredMethods();    //dto methods
        String mName,fieldName,dtoFieldType=null,entFieldType=null,dtoMapName = null,dtoFieldName =null;Object value = null;
        for(Method m : (enToDto ? dtos : entrys)) {    //當 enToDto=true 此時是Entity轉爲Dto,遍歷dto的屬性
            if((mName=m.getName()).startsWith("set")) {    //只進set方法
                fieldName = mName.toLowerCase().charAt(3) + mName.substring(4,mName.length());  //通過set方法獲得dto的屬性名
                tohere:
                for(Field fd: dtofds) {
                    fd.setAccessible(true);    //setAccessible是啓用和禁用訪問安全檢查的開關
                    if(fd.isAnnotationPresent(RelMapper.class)||dtoExistAnno){    //判斷field上註解或類上面註解是否存在
                        //獲取與Entity屬性相匹配的映射值(兩種情況:1.該field上註解的value值(Entity的field name 和Dto 的field name 不同)  2.該field本身(本身則是Entity的field name 和Dto 的field name 相同))
                        dtoMapName = fd.isAnnotationPresent(RelMapper.class) ? (fd.getAnnotation(RelMapper.class).value().toString().equals("")?fd.getName().toString():fd.getAnnotation(RelMapper.class).value().toString()):fd.getName().toString();
                        if(((enToDto ? fd.getName() : dtoMapName)).toString().equals(fieldName)) { 
                            dtoFieldType = fd.getGenericType().toString().substring(fd.getGenericType().toString().lastIndexOf(".") + 1); // 獲取dto屬性的類型(如 private String field 結果 = String)
                            for(Field fe : entryfds) {
                                fe.setAccessible(true);
                                if(fe.getName().toString().equals(enToDto ? dtoMapName : fieldName) ) {//遍歷Entity類的屬性與dto屬性註解中的value值匹配
                                    entFieldType = fe.getGenericType().toString().substring(fe.getGenericType().toString().lastIndexOf(".") + 1); //獲取Entity類屬性類型
                                    dtoFieldName = enToDto ? dtoMapName : fd.getName().toString();
                                    break tohere;
                                }
                            }
                        }
                    }
                }
                if(dtoFieldName!= null && !dtoFieldName.equals("null")) {
                    for(Method md : (enToDto ? entrys : dtos)) {
                        if(md.getName().toUpperCase().equals("GET"+dtoFieldName.toUpperCase())){
                            dtoFieldName = null; 
                            if(md.invoke(enToDto ? entry : dto) == null) { break;} //去空操作
                            //Entity類field 與Dto類field類型不一致通過TypeProcessor處理轉換
                            value = (entFieldType.equals(dtoFieldType))? md.invoke(enToDto ? entry : dto) :TypeProcessor(entFieldType, dtoFieldType,md.invoke(enToDto ? entry : dto),enToDto ? true : false);
                            m.invoke(enToDto ? dto : entry, value); //得到field的值 通過invoke()賦值給要轉換類的對應屬性
                            value = null;
                            break;
                        }
                    }
                }
            }
        }
        return enToDto ? dto : entry;
    }

    //類型轉換處理
    public static Object TypeProcessor(String entFieldType,String dtoFieldType, Object obj,boolean enToDto) {
        if(entFieldType.equals(dtoFieldType)) return obj;

        if(!entFieldType.equals(dtoFieldType)) {
            switch(entFieldType) {
                case "Date":
                    return (enToDto)?TypeConverter.dateToString((Date) obj):TypeConverter.stringToDate(obj.toString());
                case "Timestamp":
                    return TypeConverter.timestampToTimestampString((Timestamp)obj);
                case "Integer":
                    return (enToDto) ? obj.toString() : Integer.parseInt((String)obj) ;
            }
        }
        return obj;
    }

上面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的代碼,省事!!!

大家如果還有其他的需求都可以往方法中添加,來達到適合項目的需求,整體下來擴展性算還不錯。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章