mapstruct 快速使用

編碼語言Codelang發表於2020-10-25

mapstruct 快速使用

mapstruct 主要的作用則是用來複制物件欄位使用,功能非常的強大。在沒有使用 mapstruct 之前可能都在使用 BeanUtils ,但是 BeanUtils 其實問題比較多,只能處理同型別的欄位並且同名稱的欄位,對於複雜的情況沒有辦法處理,其實就是 BeanUtils 使用的反射,效能較差。而 mapstruct 使用的是傳統的 setter 與 getter 方式,只需要提供介面在編譯的時候會生成相應的實現。

1. 新增依賴

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-jdk8</artifactId>
    <version>1.3.1.Final</version>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.3.1.Final</version>
</dependency>

2. 簡單的情況處理

下面的程式碼中為了縮小程式碼量,使用了 lombok 相關注解。

2.1 定義 User

@Data
@Builder
public class User {
    private String name;

    private Date createTime;
}

2.2 定義 UserDto

@Data
@Builder
public class UserDto {
    private String name;

    private String sex; // 故意多了一個sex

    private String createTime;
}

2.3 定義 Converter,其實就是 Mapper

此 Mapper 不是 Mybatis 的 Mapper ,是 Mapstruct 的 Mapper(org.mapstruct.Mapper)。

@Mapper
public interface UserConverter {
    UserConverter INSTANCE = Mappers.getMapper(UserConverter.class);

    UserDto toUserDto(User user);
}

2.4 測試一下

public class Main {
    public static void main(String[] args) {
        User user = User.builder().name("張三").createTime(new Date()).build();
        UserDto userDto = UserConverter.INSTANCE.toUserDto(user);
        System.out.println(userVO);
    }
}

執行後,會在 target/classes 對應的包中會產生一個 UserConverterImpl.class 內容反編譯後如下:

public class UserConverterImpl implements UserConverter {
    public UserConverterImpl() {
    }

    public UserDto toUserDto(User user) {
        if (user == null) {
            return null;
        } else {
            UserDtoBuilder userDto = UserDto.builder();
            userDto.name(user.getName());
            userDto.createTime(user.getCreateTime());
            return userDto.build();
        }
    }
}

3. 複雜的情況處理

複雜的情況,可能有相同欄位但是不同的型別,也有可能是相同型別但是名稱不同,或者是在處理的過程中需要呼叫一些方法進行處理等。

3.1 定義 UserComplex

@Data
@Builder
public class UserComplex {
    private String name;

    private Integer status;

    private Date createTime;
}

3.2 定義 UserComplexDto

@Data
@Builder
public class UserComplexDto {
    private String userName; //在UserComplex中叫name

    private String status; //在UserComplex中是Integer型別

    private String createTime; //在UserComplex中是Date型別
}

3.3 定義 Converter,其實就是 Mapper

@Mapper
public interface UserComplexConverter {
    UserComplexConverter INSTANCE = Mappers.getMapper(UserComplexConverter.class);

    @Mappings({
            @Mapping(target = "createTime",dateFormat="yyyy-MM-dd HH:mm")
    })
    UserComplexDto toUserComplexDto1(UserComplex user);

    @Mappings({
            @Mapping(target = "userName",source = "name"),
            @Mapping(target = "status",expression = "java(java.lang.String.valueOf(user.getStatus()))"),
            @Mapping(target = "createTime",dateFormat="yyyy-MM-dd HH:mm")
    })
    UserComplexDto toUserComplexDto2(UserComplex user);
}

3.4 測試一下

public class Main {
    public static void main(String[] args) {
        UserComplex user = UserComplex.builder().name("張三").status(10).createTime(new Date()).build();
        UserComplexDto userVO = UserComplexConverter.INSTANCE.toUserComplexDto2(user);
        System.out.println(userVO);
    }
}

輸入內容如下:

UserComplexDto(userName=null, status=10, createTime=2020-09-18 16:14)
UserComplexDto(userName=張三, status=10, createTime=2020-09-18 16:14)

在使用 toUserComplexDto1 方法處理時,因為我們指定了 @Mappings 處理不匹配的欄位,這裡只指定了 createTime,實際上他也給我們處理了 status。但是名稱不匹配的他就不知道處理了,這也是正常的,肯定不知道如何匹配呀。

在使用 toUserComplexDto2 方法處理時,這一次通過 @Mappings 將所有的欄位都明確指定了,顯然都處理成功了。

注意:在使用 mapstruct 的時候,如果有型別不匹配,你沒有顯示的指定 @Mappings,那麼會報錯,如果你顯示的指定了一個,其餘的他也會以預設的處理方式處理。

本文主要用於個人記錄筆記!

相關文章