1. 程式人生 > >告別set和get,兩大利器輕鬆搞定model轉換

告別set和get,兩大利器輕鬆搞定model轉換

場景一:一般我們遇到需要新建model,常規做法就是建立一個類,老老實實的定義好model中的所有屬性,一般來說屬性對應的set方法和get方法都是少不了的,有時候還需要toString甚至equals和hashCode方法。

現在的IDE已經很成熟了,一般不會手寫set和get方法,採用IDE自帶的快捷方式自動生成居多。如下圖所示

該方式相對手寫方法來說,效率已經有了很大的提升,但還是有進一步的提升空間(下文會介紹)。而且該種方式維護性較差,當需要修改某個屬性名稱或者屬性型別時,對應的set和get方法以及toString都需要調整。

場景二:大部分時候,我們都是基於當前流行的微服務架構和SSM(Spring+Spring MVC + Mybatis)框架進行開發,這時候我個人經常遇到一個問題就是model的轉換問題。

不同層會有不同的model,比如DAO層的model,service層的model,對外API介面的model,還有更上層的controller層的model以及提供給前端的View model。

為了能夠是介面正常呼叫,我們不得不處理這些model的轉換,沒有一個稱手的工具,我們只能手寫轉換類,通過一個又一個的set和get方法來完成model的轉換。

有時候,我們在測試介面的時候發現有些屬性沒有值,除錯半天才發現,是因為其中一個屬性忘記寫set方法了。我們明知道這些工作並不需要太多的思考,但是卻不得不小心翼翼的對待。

那麼,是否有更加優雅的處理方式,請看下文介紹

Lombok

這是一個外掛,能夠很好的解決場景一

中的難題

下載安裝

我用的IDE是Intellij idea,可以在Preferences->Plugins中找到相應的外掛安裝並重啟即可。

該外掛的實現已經放在github上,有興趣可以到https://github.com/mplushnikov/lombok-intellij-plugin檢視

lombok使用

新增jar包依賴

在你需要的專案的pom檔案中新增如下的依賴


<dependency>  <groupId>org.projectlombok</groupId>  <artifactId>lombok</artifactId>  <version>1.16.18</version>  </dependency>
編寫model

package com.jackie.wowjava.best.practice.java.orika.model;

import java.util.Date;

public class AuthorDTO {   private String name;

    private Date birthday;
}
新增需要的註解

Lombok可以通過註解的方式實現你需要新增的方法,比如你需要新增這些屬性對應的set方法,那麼只要在model類上添加註解@Setter即可,相應的,如果需要get方法新增@Getter

此外還有@ToString@NoArgsConstructor@AllArgsConstructor等方便使用的註解。

事實上,我們真的只需要這樣添加註解的方式,就能夠實現輕鬆呼叫set和get方法的需要。這樣,以後如果model的屬性有改變,我們只需要直接改相應的屬性即可,不再需要做任何一點多餘的操作。

將註解還原為具體方法

Lombok為我們提供可以將對應註解還原為對應方法的功能。

點選Refactor->Delombok選擇想還原的方法即可

是不是很好用?

Orika

Orika是一個簡單快速的model拷貝框架。

Orika使用

新增jar包依賴

在需要使用的專案的pom檔案中新增如下依賴


<dependency>  <groupId>ma.glasnost.orika</groupId>  <artifactId>orika-core</artifactId>  <version>1.5.2</version>  </dependency>
建立兩個需要轉換的model

BookEntity


package com.jackie.wowjava.best.practice.java.orika.model;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.Date;

@Setter @Getter @AllArgsConstructor @NoArgsConstructor public class BookEntity {   private String bookName;

    private String authorName;

    private Date authorBirthday;

    private String bookInformation;

    private Integer type;

} 

BookDTO


package com.jackie.wowjava.best.practice.java.orika.model;

import lombok.Getter;
import lombok.Setter;

@Setter @Getter public class BookDTO {   private String bookName;

    private AuthorDTO author;

    private BookType bookType;

    private BookInfo bookInfo;
}

備註:這裡的AuthorDTO、BookType和BookInfo如下


package com.jackie.wowjava.best.practice.java.orika.model;

import lombok.*;

import java.util.Date;

@Setter @Getter @ToString @NoArgsConstructor @AllArgsConstructor public class AuthorDTO {   private String name;

    private Date birthday;
} 

package com.jackie.wowjava.best.practice.java.orika.model;

public enum BookType {
 NOVEL(1),
    ESSAY(2);

    private int value;

    BookType(int value) {
 this.value = value;
    }   public static BookType getBookType(int value) {
 BookType bookType = null;

        switch (value) {
 case 1:
                bookType = NOVEL;
                break;
            case 2:
                bookType = ESSAY;
                break;
            default:
                break;
        }
 return bookType;
    }   public int getValue() {
 return value;
    } } 

package com.jackie.wowjava.best.practice.java.orika.model;

import lombok.Getter;
import lombok.Setter;

@Setter @Getter public class BookInfo {   private String ISBN;

    private int page;
}

你沒看錯,就是BookDTO和BookEntity這兩個model,需要相互轉換,Orika可以幫你搞定,具體看下面是如何實現的。

model轉換

我們看到兩個model中包含了多種情況

  • 屬性名稱完全一樣的,比如bookName

  • 一個屬性對應一個物件的,BookDTO中的AuthorDTO對應BookEntity中的authorName以及authorBirthday

  • 列舉型別的,比如BookEntity的type和BookDTO的BookType

  • JSON型別的,比如BookEntity的bookInformation和BookDTO的bookInfo

1、屬性名稱完全一樣的屬性拷貝

新建測試類OrikaTest


package com.jackie.wowjava.best.practice.java.orika;

import com.alibaba.fastjson.JSON;
import com.jackie.wowjava.best.practice.java.orika.model.BookDTO;
import com.jackie.wowjava.best.practice.java.orika.model.BookEntity;
import com.jackie.wowjava.best.practice.java.orika.model.BookInfo;
import com.jackie.wowjava.best.practice.java.orika.model.BookType;
import ma.glasnost.orika.MapperFactory;
import ma.glasnost.orika.MappingContext;
import ma.glasnost.orika.converter.BidirectionalConverter;
import ma.glasnost.orika.impl.DefaultMapperFactory;
import ma.glasnost.orika.metadata.Type;

import java.time.LocalDate;
import java.time.Month;
import java.time.ZoneId;
import java.util.Date;

public class OrikaTest {   private static final MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

    public static void main(String[] args) {   BookEntity bookEntity = new BookEntity(
 "銀河系漫遊指南",
                "道格拉斯·亞當斯",
                Date.from(LocalDate.of(1952, Month.MARCH, 11).atStartOfDay(ZoneId.systemDefault()).toInstant()),
                "{\"ISBN\": \"9787532754687\", \n \"page\": 279\n }",
                1);

        BookDTO bookDTO = mapperFactory.getMapperFacade().map(bookEntity, BookDTO.class);

        System.out.println(JSON.toJSONString(bookDTO));
    } } 

執行結果如下


{"bookName":"銀河系漫遊指南"}

沒錯,只有名稱相同的屬性被轉換了。

從程式碼來看,我們不需要做任何特殊化處理就能做到這一點,因為Orika預設就是按照名稱相同就拷貝進行處理的。

2、一個屬性對應一個物件的屬性拷貝

這裡我們是想BookDTO中的AuthorDTO對應BookEntity中的authorName以及authorBirthday。

我們只需要新增如下程式碼即可實現。


mapperFactory.classMap(BookDTO.class, BookEntity.class)
 .field("author.name", "authorName")
 .field("author.birthday", "authorBirthday")
 .byDefault()
 .register();

執行結果如下


{"author":{"birthday":-562060800000,"name":"道格拉斯·亞當斯"},"bookName":"銀河系漫遊指南"}

3、列舉型別的屬性拷貝

這時候我們需要新建一個轉換器並註冊到mapperFactory上。

註冊轉換器程式碼如下


mapperFactory.getConverterFactory().registerConverter("bookTypeConvert", new BidirectionalConverter<BookType, Integer>() {
 @Override
 public Integer convertTo(BookType bookType, Type<Integer> type, MappingContext mappingContext) {
 return bookType.getValue();
    }   @Override
 public BookType convertFrom(Integer value, Type<BookType> type, MappingContext mappingContext) {
 return BookType.getBookType(value);
    } });

註冊轉換器程式碼如下


mapperFactory.classMap(BookDTO.class, BookEntity.class)
 .field("author.name", "authorName")
 .field("author.birthday", "authorBirthday")
 .fieldMap("bookType", "type").converter("bookTypeConvert").add()
 .byDefault()
 .register();

執行結果如下:


{"author":{"birthday":-562060800000,"name":"道格拉斯·亞當斯"},"bookName":"銀河系漫遊指南","bookType":"NOVEL"}

4、JSON型別的屬性轉換

該屬性的轉換原理和上述的列舉型別轉換相同,需要建立轉換器並註冊使用


mapperFactory.getConverterFactory().registerConverter("bookInfoConvert", new BidirectionalConverter<BookInfo, String>() {
 @Override
 public String convertTo(BookInfo bookInfo, Type<String> type, MappingContext mappingContext) {
 return JSON.toJSONString(bookInfo);
    }   @Override
 public BookInfo convertFrom(String s, Type<BookInfo> type, MappingContext mappingContext) {
 return JSON.parseObject(s, BookInfo.class);
    } });



mapperFactory.classMap(BookDTO.class, BookEntity.class)
 .field("author.name", "authorName")
 .field("author.birthday", "authorBirthday")
 .fieldMap("bookType", "type").converter("bookTypeConvert").add()
 .fieldMap("bookInfo", "bookInformation").converter("bookInfoConvert").add()
 .byDefault()
 .register();

執行結果如下


{"author":{"birthday":-562060800000,"name":"道格拉斯·亞當斯"},"bookInfo":{"iSBN":"9787532754687","page":279},"bookName":"銀河系漫遊指南","bookType":"NOVEL"}

是不是很神奇?

總結

相信有了這兩大神奇Lombok和Orika,基本上實現了和set和get的真正告別。

如果您覺得閱讀本文對您有幫助,請點一下“推薦”按鈕,您的“推薦”將是我最大的寫作動力!如果您想持續關注我的文章,請掃描二維碼,關注JackieZheng的微信公眾號,我會將我的文章推送給您,並和您一起分享我日常閱讀過的優質文章。