Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】

news2024/12/27 12:34:51

文章目录

  • Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】
    • 1. 引入mapstruct依赖
    • 2. 数据准备
      • 2-1 准备一个子类,TestData
      • 2-2 准备两个类,SourceData,TargetData,属性完全一样
    • 3. 定义Mapper接口【注:这里的Mapper是mapstruct的,非mybatis的】
    • 4. 使用Mapstruct实现属性拷贝
      • 4-1 使用实例
      • 4-2 使用了Mapstruct,程序运行后,编译的目录会自动生成Mapper的实现类【了解即可】
    • 5 Mapstruct默认为浅拷贝,深拷贝的使用
      • 5-1 再新建一个Mapper,实现深拷贝
      • 5-2 编译目录下,自动生成Mapper的实现类如下,可以发现TestData 为new出来的
    • 6 集合的拷贝
      • 6-1 集合拷贝实例
    • 7 类型不一致
      • 7-1 类型不一致,拷贝机制如下
      • 7-2 上述问题,若想要在编译时就提示出来
      • 7-3 禁止隐式转换
    • 8 忽略指定字段
    • 9. 综合案例一 【自定义转换器的使用 @Named()+qualifiedByName = {} 】
      • 9-1 准备如下几个Class
        • 9-1-1 UserDao
        • 9-1-2 UserDao
        • 9-1-3 SexEnum
      • 9-2 Mapper
        • 9-2-1 自定义的UserMapper
        • 9-2-2 上述Mapper用到了两个自定义的转换器
      • 9-3 使用测试
      • 9-4 编译后Mapper接口的实现类代码【了解即可】
    • 10. 综合案例二【自定义转换器的使用 @Named()+qualifiedByName = "" 】
      • 10-1 准备如下几个Class
        • 10-1-1 People1
        • 10-1-2 People2
        • 10-1-3 Address
        • 10-1-4 SexEnum
      • 10-2 Mapper
        • 10-2-1 自定义的PeopleMapper
        • 10-2-2 上述Mapper用到的自定义转换器
      • 10-3 使用测试
      • 10-4 编译后Mapper接口的实现类代码【了解即可】
    • 11. 综合案例三【参数赋值、更新对象属性、映射反转、通过Spring依赖注入使用等】
      • 11-1 准备如下几个Class
      • 11-2 Mapper及测试示例
        • 11-2-1 自定义Mapper1 ⇒ 多参数赋值
        • 11-2-2 直接使用参数作为属性值
        • 11-2-3 更新对象属性【@Mapping+@MappingTarget】
        • 11-2-4 映射反转【@InheritInverseConfiguration和@InheritConfiguration】
          • 11-2-4-1 示例准备
          • 11-2-4-2 Mapper【@InheritInverseConfiguration】
          • 11-2-4-2 Mapper【@InheritConfiguration】 使用已有的映射更新对象属性
        • 11-2-5 使用Spring依赖注入
    • 12 MapStruct之@BeforeMapping和@AfterMapping注解的用法
      • 12-1 注解的作用
      • 12-2 注解的使用【Mapper接口中使用、通过@Mapper注解的uses属性指定使用、通过@Context参数的类型中实现】
        • 12-2-1 示例准备
        • 12-2-2 Mapper
        • 12-2-3 测试
        • 12-2-4 分析
        • 12-2-5 通过@Mapper注解的uses属性,指定的类型(类或接口)中声明@BeforeMapping和@AfterMapping ,如下:

Mapstruct的使用备忘【代替BeanUtils高效率属性拷贝】

1. 引入mapstruct依赖

		<!--mapstruct核心-->
		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct</artifactId>
			<version>1.6.0</version>
		</dependency>

		<!--mapstruct编译-->
		<dependency>
			<groupId>org.mapstruct</groupId>
			<artifactId>mapstruct-processor</artifactId>
			<version>1.6.0</version>
		</dependency>

2. 数据准备

2-1 准备一个子类,TestData

public class TestData {
    private String id;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
}

2-2 准备两个类,SourceData,TargetData,属性完全一样

public class SourceData {
  private String id;
  private String name;
  private TestData data;
  private Long createTime;

  public String getId() {
   return id;
  }
  public void setId(String id) {
   this.id = id;
  }
  public String getName() {
   return name;
  }
  public void setName(String name) {
   this.name = name;
  }
  public TestData getData() {
   return data;
  }
  public void setData(TestData data) {
   this.data = data;
  }
  public Long getCreateTime() {
   return createTime;
  }
  public void setCreateTime(Long createTime) {
   this.createTime = createTime;
  }
}
package cn.mediinfo.entity;

public class TargetData {
    private String id;
    private String name;
    private TestData data;
    private Long createTime;

    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public TestData getData() {
        return data;
    }
    public void setData(TestData data) {
        this.data = data;
    }
    public Long getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }
}

3. 定义Mapper接口【注:这里的Mapper是mapstruct的,非mybatis的】

@Mapper
public interface BeanMapper { 
 BeanMapper INSTANCE = Mappers.getMapper(BeanMapper.class);
 TargetData map(SourceData source);
}

4. 使用Mapstruct实现属性拷贝

4-1 使用实例

// 1. 准备一个SourceData类,设置好数据
SourceData source = new SourceData()
source.setId("123")
source.setName("abc")
source.setCreateTime(System.currentTimeMillis())
TestData testData = new TestData()
testData.setId("123")

// 2. 使用Mapstruct实现属性拷贝 
TargetData target = BeanMapper.INSTANCE.map(source)

// 3. 打印出目标类的属性值
System.out.println(target.getId() + ":" + target.getName() + ":" + target.getCreateTime())
//		这里可以看到Source和Target中的TestData属性是一样,即mapstruct默认是浅拷贝
System.out.println(source.getData() == target.getData())   // 结果为: true

4-2 使用了Mapstruct,程序运行后,编译的目录会自动生成Mapper的实现类【了解即可】

// 上述实例运行后,项目根目录下会出现编译后的文件,
//		target目录下打开BeanMapper(自己写的Mapper接口)路径,会看到编译后自动产生了实现类

在这里插入图片描述

public class BeanMapperImpl implements BeanMapper {
    public BeanMapperImpl() {
    }

    public TargetData map(SourceData source) {
        if (source == null) {
            return null;
        } else {
            TargetData targetData = new TargetData();
            targetData.setId(source.getId());
            targetData.setName(source.getName());
            targetData.setData(source.getData());
            targetData.setCreateTime(source.getCreateTime());
            return targetData;
        }
    }
}

5 Mapstruct默认为浅拷贝,深拷贝的使用

  • 查看上方编译后的源码,可以验证mapstruct默认是浅拷贝,所以上方实例打印结果为true
  • 若想实现深拷贝,在方法上添加注解@Mapping(mappingControl = DeepClone.class)即可

5-1 再新建一个Mapper,实现深拷贝

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

	// 使用@Mapping注解,加上mappingControl = DeepClone.class标识即可实现深拷贝
    @Mapping(source = "data", target = "data", mappingControl = DeepClone.class)
    TargetData map(SourceData source);
}
// 测试
		// 1. 准备一个SourceData类,设置好数据
		SourceData source = new SourceData();
        source.setId("123345");
        source.setName("abcderf");
        TestData testData1 = new TestData();
        testData1.setId("TTTTT");
        source.setData(testData1);
        source.setCreateTime(System.currentTimeMillis() );
		
		// 2. 使用Mapstruct实现属性拷贝  【这里BeanMapper2即实现了深拷贝】
        TargetData target = BeanMapper2.INSTANCE.map(source);
        System.out.println(target.getId() + ":" + target.getName() + ":" + target.getCreateTime());
        //		输出:false,  说明对象的子类为不通的实例,即实现了深拷贝
        System.out.println(source.getData() == target.getData());

5-2 编译目录下,自动生成Mapper的实现类如下,可以发现TestData 为new出来的

public class BeanMapper2Impl implements BeanMapper2 {
    public BeanMapper2Impl() {
    }

    public TargetData map(SourceData source) {
        if (source == null) {
            return null;
        } else {
            TargetData targetData = new TargetData();
            targetData.setData(this.testDataToTestData(source.getData()));
            targetData.setId(source.getId());
            targetData.setName(source.getName());
            targetData.setCreateTime(source.getCreateTime());
            return targetData;
        }
    }

    protected TestData testDataToTestData(TestData testData) {
        if (testData == null) {
            return null;
        } else {
            TestData testData1 = new TestData();
            testData1.setId(testData.getId());
            return testData1;
        }
    }
}

6 集合的拷贝

  • 在Mapper接口中,添加一个如下的方法即可;
List<TestData> map(List<TestData> source);   

6-1 集合拷贝实例

// 实例
// 1. Mapper中声明集合拷贝方法即可
@Mapper
public interface BeanMapper3 {
    BeanMapper3 INSTANCE = Mappers.getMapper(BeanMapper3.class);
    
    List<TestData> map(List<TestData> source);
}

// 2. 测试实例
public class TestMapstructDemo{
    public static void main(String[] args) {
        TestData t1 = new TestData("1");
        TestData t2 = new TestData("3");
        TestData t3 = new TestData("4");
        List<TestData> sourceList = List.of(t1, t2, t3);
		
		// 实现集合的拷贝
        List<TestData> targetList = BeanMapper3.INSTANCE.map(sourceList);
        // 打印targetList,便可以看到各个元素的值了
        System.out.println( targetList );
        // 		这里输出:  true   说明集合中的每个TestData对象实例,  和sourceList中是一样的,即浅拷贝
        System.out.println( sourceList.get(0) == targetList.get(0) );
    }
}

7 类型不一致

7-1 类型不一致,拷贝机制如下

  • 假设将TargetData的createTime改成int类型,编译后,生成代码如下:
    在这里插入图片描述

7-2 上述问题,若想要在编译时就提示出来

可以看到它会默认帮我们转换,这是个隐藏的问题!!!
在Mapper注解上指定一些类型转换的策略,如:@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)

@Mapper(typeConversionPolicy = ReportingPolicy.ERROR)

这样编译即会提示错误

java: Can

7-3 禁止隐式转换

如果将TargetData的createTime类型再改成string呢,编译又正常了,生成代码如下:
在这里插入图片描述
对于string和其它基础类型的包装类,它会隐式帮我们转换,这也是个隐藏问题,
如果希望在编译时就提示,可以自定义一个注解,并在Mapper中指定它,如下:

// 自定义注解
@Retention(RetentionPolicy.CLASS)
@MappingControl(MappingControl.Use.DIRECT)
@MappingControl(MappingControl.Use.MAPPING_METHOD)
@MappingControl(MappingControl.Use.COMPLEX_MAPPING)
public @interface ConversationMapping {
}
 
// 使用 ==>  mappingControl = ConversationMapping.class
@Mapper(typeConversionPolicy = ReportingPolicy.ERROR, mappingControl = ConversationMapping.class)

重新编译会提示报错!!!

java: Can't map property "Long createTime" to "String createTime". Consider to declare/implement a mapping method: "String map(Long value)".

8 忽略指定字段

// 使用Mapping注解的ignore属性
@Mapping(target = "id", ignore = true)
// 如果想忽略某些字段,并且复用起来,可以定义一个IgnoreFixedField注解,然后打在方法上
@Mapping(target = "id", ignore = true)
@Mapping(target = "createTime", ignore = true)
@Mapping(target = "updateTime", ignore = true)
@Target(METHOD)
@Retention(RUNTIME)
@Documented
@interface IgnoreFixedField {
}
 
@IgnoreFixedField   // 加上这个自定义注解后,就自动忽略id,createTime,updateTime几个属性了
@Mapping(target = "data", mappingControl = DeepClone.class)
TargetData map(SourceData source);

9. 综合案例一 【自定义转换器的使用 @Named()+qualifiedByName = {} 】

9-1 准备如下几个Class

9-1-1 UserDao
import lombok.Data;
import java.sql.Timestamp;

@Data
public class UserDAO {

    // 主键
    private Long id;

    // 姓名
    private String name;

    // 性别
    private Integer sex;

    // 描述
    private String remark;

    // 创建时间
    private Timestamp createTime;
}
9-1-2 UserDao
import com.gougou.mapstruct.enums.SexEnum;
import lombok.Data;
import java.io.Serializable;

/**
 * dto:网络传输对象
 */
@Data
public class UserDTO implements Serializable {

    private static final long serialVersionUID = -2767215193284523251L;

    // 主键
    private String id;

    // 姓名
    private String name;

    // 性别
    private SexEnum sex;

    // 描述
    private String desc;

    // 创建时间
    private String createTime;
}
9-1-3 SexEnum
import lombok.Getter;
import lombok.Setter;

public enum SexEnum {
    man(1, "男"),
    woman(0, "女");

    @Setter
    @Getter
    private Integer code;

    @Setter
    @Getter
    private String name;

    SexEnum(Integer code, String name) {
        this.code = code;
        this.name = name;
    }

    public static SexEnum of(Integer code){
        for(SexEnum sexEnum:SexEnum.values()){
            if(sexEnum.code.equals(code)){
                return sexEnum;
            }
        }
        return null;
    }
}

9-2 Mapper

9-2-1 自定义的UserMapper
import com.gougou.mapstruct.dao.UserDAO;
import com.gougou.mapstruct.dto.UserDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

import java.util.List;

/**
 * UserDTO与UserDAO之间的转化类
 */
@Mapper(uses = {
        SexEnumIntegerMapper.class,
        StringTimestampMapper.class
})
public interface UserMapper {

    UserMapper INSTANCE = Mappers.getMapper(UserMapper.class);

    @Mappings({
            @Mapping(source = "sex", target = "sex",qualifiedByName = {"SexEnumIntegerMapper", "integerBySexEnum"}),
            @Mapping(source = "desc", target = "remark"),
            @Mapping(source = "createTime", target = "createTime",qualifiedByName = {"StringTimestampMapper", "timestampByString"})
    })
    UserDAO toDO(UserDTO userDTO);

    List<UserDAO> toDOs(List<UserDTO> userDTOList);

    @Mappings({
            @Mapping(source = "sex", target = "sex",qualifiedByName = {"SexEnumIntegerMapper", "sexEnumByInteger"}),
            @Mapping(source = "remark", target = "desc"),
            @Mapping(source = "createTime", target = "createTime",qualifiedByName = {"StringTimestampMapper", "stringByTimestamp"})
    })
    UserDTO toDTO(UserDAO userDAO);

    List<UserDTO> toDTOs(List<UserDAO> userDAOList);
}
9-2-2 上述Mapper用到了两个自定义的转换器
import com.gougou.mapstruct.enums.SexEnum;
import org.mapstruct.Named;

/**
 * SexEnum与Integer之间的转化
 */
@Named("SexEnumIntegerMapper")
public class SexEnumIntegerMapper {

    @Named("sexEnumByInteger")
    public SexEnum sexEnumByInteger(Integer intParam){
        return SexEnum.of(intParam);
    }

    @Named("integerBySexEnum")
    public Integer integerBySexEnum(SexEnum sexEnum){
        return sexEnum.getCode();
    }
}
import org.mapstruct.Named;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;

/**
 * String与timestamp之间的转化
 */
@Named("StringTimestampMapper")
public class StringTimestampMapper {

    private static final String dateFormatStr = "yyyy-MM-dd HH:mm:ss";

    @Named("timestampByString")
    public Timestamp timestampByString(String strParam) {
        SimpleDateFormat sf = new SimpleDateFormat(dateFormatStr);
        java.util.Date date = null;
        try {
            date = sf.parse(strParam);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return new java.sql.Timestamp(date.getTime());
    }

    @Named("stringByTimestamp")
    public String stringByTimestamp(Timestamp timestamp) {
        DateFormat df = new SimpleDateFormat(dateFormatStr);
        return df.format(timestamp);
    }
}

9-3 使用测试

import com.gougou.mapstruct.dao.UserDAO;
import com.gougou.mapstruct.dto.UserDTO;
import com.gougou.mapstruct.enums.SexEnum;
import com.gougou.mapstruct.transfer.UserMapper;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

public class MainTest {

    private UserDTO userDTO = new UserDTO();

    private List<UserDTO> userDTOList = new ArrayList<>(2);

    private UserDAO userDAO = new UserDAO();

    private List<UserDAO> userDAOList = new ArrayList<>(2);

    @Before
    public void initUserDTO() {
        userDTO.setId("1122");
        userDTO.setDesc("这是张三");
        userDTO.setName("张三");
        userDTO.setSex(SexEnum.man);
        userDTO.setCreateTime("2020-05-06 19:00:00");

        userDAO.setId(3377L);
        userDAO.setRemark("这是李梅梅");
        userDAO.setName("李梅梅");
        userDAO.setSex(0);
        userDAO.setCreateTime(new java.sql.Timestamp(1588765009399L));

        UserDTO userDTO2 = new UserDTO();
        userDTO2.setId("2211");
        userDTO2.setDesc("这是张三2");
        userDTO2.setName("张三2");
        userDTO2.setSex(SexEnum.man);
        userDTO2.setCreateTime("2020-05-06 19:49:00");

        UserDAO userDAO2 = new UserDAO();
        userDAO2.setId(7733L);
        userDAO2.setRemark("这是李梅梅2");
        userDAO2.setName("李梅梅2");
        userDAO2.setSex(0);
        userDAO2.setCreateTime(new java.sql.Timestamp(1588766094618L));

        userDAOList.add(userDAO);
        userDAOList.add(userDAO2);

        userDTOList.add(userDTO);
        userDTOList.add(userDTO2);
    }

    /**
     * DAO -> DTO
     */
    @Test
    public void test1() {
        UserDTO userDTO1 = UserMapper.INSTANCE.toDTO(userDAO);
        // UserDTO(id=3377, name=李梅梅, sex=woman, desc=这是李梅梅, createTime=2020-05-06 19:36:49)
        System.out.println(userDTO1.toString());
    }

    /**
     * DTO -> DAO
     */
    @Test
    public void test2() {
        UserDAO userDAO1 = UserMapper.INSTANCE.toDO(userDTO);
        // UserDAO(id=1122, name=张三, sex=1, remark=这是张三, createTime=2020-05-06 19:00:00.0)
        System.out.println(userDAO1.toString());
    }

    /**
     * List<DAO> -> List<DTO>
     */
    @Test
    public void test3() {
        List<UserDTO> userDTOList1 = UserMapper.INSTANCE.toDTOs(userDAOList);
        /**
         * UserDTO(id=3377, name=李梅梅, sex=woman, desc=这是李梅梅, createTime=2020-05-06 19:36:49)
         * UserDTO(id=7733, name=李梅梅2, sex=woman, desc=这是李梅梅2, createTime=2020-05-06 19:54:54)
         */
        userDTOList1.stream().forEach(x -> System.out.println(x));
    }

    /**
     * List<DTO> -> List<DAO>
     */
    @Test
    public void test4() {
        List<UserDAO> userDAOList1 = UserMapper.INSTANCE.toDOs(userDTOList);
        /**
         * UserDAO(id=1122, name=张三, sex=1, remark=这是张三, createTime=2020-05-06 19:00:00.0)——————这里的格式是TimeStamp的toString方法默认的实现,与本次转换无关
         * UserDAO(id=2211, name=张三2, sex=1, remark=这是张三2, createTime=2020-05-06 19:49:00.0)
         */
        userDAOList1.stream().forEach(x -> System.out.println(x));
        userDAOList1.stream().forEach(x -> System.out.println(x.getCreateTime()));
    }

}

9-4 编译后Mapper接口的实现类代码【了解即可】

import com.gougou.mapstruct.dao.UserDAO;
import com.gougou.mapstruct.dto.UserDTO;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-05-06T19:40:20+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)"
)
public class UserMapperImpl implements UserMapper {

    private final SexEnumIntegerMapper sexEnumIntegerMapper = new SexEnumIntegerMapper();
    private final StringTimestampMapper stringTimestampMapper = new StringTimestampMapper();

    @Override
    public UserDAO toDO(UserDTO userDTO) {
        if ( userDTO == null ) {
            return null;
        }

        UserDAO userDAO = new UserDAO();

        userDAO.setRemark( userDTO.getDesc() );
        userDAO.setCreateTime( stringTimestampMapper.timestampByString( userDTO.getCreateTime() ) );
        userDAO.setSex( sexEnumIntegerMapper.integerBySexEnum( userDTO.getSex() ) );
        if ( userDTO.getId() != null ) {
            userDAO.setId( Long.parseLong( userDTO.getId() ) );
        }
        userDAO.setName( userDTO.getName() );

        return userDAO;
    }

    @Override
    public List<UserDAO> toDOs(List<UserDTO> userDTOList) {
        if ( userDTOList == null ) {
            return null;
        }

        List<UserDAO> list = new ArrayList<UserDAO>( userDTOList.size() );
        for ( UserDTO userDTO : userDTOList ) {
            list.add( toDO( userDTO ) );
        }

        return list;
    }

    @Override
    public UserDTO toDTO(UserDAO userDAO) {
        if ( userDAO == null ) {
            return null;
        }

        UserDTO userDTO = new UserDTO();

        userDTO.setCreateTime( stringTimestampMapper.stringByTimestamp( userDAO.getCreateTime() ) );
        userDTO.setSex( sexEnumIntegerMapper.sexEnumByInteger( userDAO.getSex() ) );
        userDTO.setDesc( userDAO.getRemark() );
        if ( userDAO.getId() != null ) {
            userDTO.setId( String.valueOf( userDAO.getId() ) );
        }
        userDTO.setName( userDAO.getName() );

        return userDTO;
    }

    @Override
    public List<UserDTO> toDTOs(List<UserDAO> userDAOList) {
        if ( userDAOList == null ) {
            return null;
        }

        List<UserDTO> list = new ArrayList<UserDTO>( userDAOList.size() );
        for ( UserDAO userDAO : userDAOList ) {
            list.add( toDTO( userDAO ) );
        }

        return list;
    }
}

10. 综合案例二【自定义转换器的使用 @Named()+qualifiedByName = “” 】

10-1 准备如下几个Class

10-1-1 People1
@Data
@NoArgsConstructor
@AllArgsConstructor
public class People1 {
    //主键
    private String id;
    //名称
    private String name1;
    //编号
    private String no1;
    //年龄
    private String age1;
    //身高(cm)
    private Long height1;
    //体重(kg)
    private Integer weight1;
    //性别
    private SexEnum sex1;
    //创建时间
    private Date createTime1;
    //血型
    private Character bloodType1;
    //工资卡
    private Double salary1;
    //小金库
    private String caseDough1;
    //地址
    private Address address;
}
10-1-2 People2
@Data
@NoArgsConstructor
@AllArgsConstructor
public class People2 {
    //主键
    private String id;
    //名称
    private String name2;
    //编号
    private String no2;
    //年龄
    private Integer age2;
    //身高(m)
    private Double height2;
    //体重(斤)
    private String weight2;
    //性别(1:男,0:女)
    private Integer sex2;
    //创建时间
    private String createTime2;
    //血型
    private String bloodType2;
    //工资卡
    private String salary2;
    //小金库
    private Integer caseDough2;
    //地址(所在省)
    private String province;
}
10-1-3 Address
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
    //省
    private String province;
    //市
    private String city;
    //县
    private String county;
    //街道
    private String street;
}
10-1-4 SexEnum
public enum SexEnum {
    MAN(1, "男"),
    WOMAN(0, "女");

    private Integer code;
    private String desc;

    SexEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
    public Integer getCode() {
        return code;
    }
    public void setCode(Integer code) {
        this.code = code;
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        this.desc = desc;
    }
}

10-2 Mapper

10-2-1 自定义的PeopleMapper
@Mapper(uses={WeightMapper.class})
public interface PeopleMapper {
    PeopleMapper INSTANCE = Mappers.getMapper(PeopleMapper.class);
    
    @Mappings({
            @Mapping(source = "name1", target = "name2"),
            @Mapping(source = "no1", target = "no2"),
            @Mapping(source = "age1", target = "age2", ignore = true),
            @Mapping(source = "height1", target = "height2", qualifiedByName = "heightConvert"),
            @Mapping(source = "weight1", target = "weight2", qualifiedByName = {"WeightMapper", "WeightConvert"}),
            @Mapping(source = "sex1.code",target = "sex2")
    })
    People2 toPeople2(People1 people1);
    
    @Named("heightConvert")
    default Double heightConvert(Long height) {
        return height * 1.0 / 100;
    }
}
10-2-2 上述Mapper用到的自定义转换器
@Component
@Named("WeightMapper")
public class WeightMapper {
    @Named("WeightConvert")
    public String weightConvert(Integer weight){
        //kg —— 斤
        return (weight*2)+"斤";
    }
}

10-3 使用测试

public class PeopleTest {
    /**
     * 输出结果:
     * People1(id=0001, name1=张三, no1=001, age1=18, height1=183, weight1=70, sex1=MAN, createTime1=Thu Jul 22 10:19:36 CST 2010, bloodType1=A, salary1=28.09888888888, caseDough1=1.08888888888, address=Address(province=安徽省, city=安庆市, county=太湖县, street=123街道))
     * People2(id=0001, name2=张三, no2=001, age2=null, height2=1.83, weight2=140斤, sex2=1, createTime2=2010-07-22 10:19:36:000, bloodType2=A型血, salary2=28.099, caseDough2=1, province=安徽省)
     */
    @Test
    public void test1() {
        People2 people2 = PeopleMapper.INSTANCE.toPeople2(setPeople1());
        System.out.println(people2);
    }
    
    private People1 setPeople1() {
        People1 people1 = new People1();
        people1.setId("0001");
        people1.setName1("张三");
        people1.setNo1("001");
        people1.setAge1("18");
        people1.setHeight1(183L);
        people1.setWeight1(70);
        people1.setSex1(SexEnum.MAN);
        people1.setCreateTime1(new Date(1279765176000L));
        people1.setBloodType1('A');
        people1.setSalary1(28.09888888888);
        people1.setCaseDough1("1.08888888888");
        people1.setAddress(new Address("安徽省", "安庆市", "太湖县", "123街道"));
        System.out.println(people1);
        return people1;
    }
}

10-4 编译后Mapper接口的实现类代码【了解即可】

import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import javax.annotation.Generated;

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-07-23T11:04:13+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)"
)
public class PeopleMapperImpl implements PeopleMapper {
    private final WeightMapper weightMapper = new WeightMapper();

    @Override
    public People2 toPeople2(People1 people1) {
        if ( people1 == null ) {
            return null;
        }

        People2 people2 = new People2();

        people2.setNo2( people1.getNo1() );
        people2.setHeight2( heightConvert( people1.getHeight1() ) );
        String province = people1AddressProvince( people1 );
        if ( province != null ) {
            people2.setProvince( province );
        }
        Integer code = people1Sex1Code( people1 );
        if ( code != null ) {
            people2.setSex2( code );
        }
        if ( people1.getSalary1() != null ) {
            people2.setSalary2( new DecimalFormat( "0.000" ).format( people1.getSalary1() ) );
        }
        try {
            if ( people1.getCaseDough1() != null ) {
                people2.setCaseDough2( new DecimalFormat( "0." ).parse( people1.getCaseDough1() ).intValue() );
            }
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
        people2.setName2( people1.getName1() );
        people2.setWeight2( weightMapper.weightConvert( people1.getWeight1() ) );
        if ( people1.getCreateTime1() != null ) {
            people2.setCreateTime2( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).format( people1.getCreateTime1() ) );
        }
        people2.setId( people1.getId() );

        people2.setBloodType2( "A型血" );

        return people2;
    }

    private String people1AddressProvince(People1 people1) {
        if ( people1 == null ) {
            return null;
        }
        Address address = people1.getAddress();
        if ( address == null ) {
            return null;
        }
        String province = address.getProvince();
        if ( province == null ) {
            return null;
        }
        return province;
    }

    private Integer people1Sex1Code(People1 people1) {
        if ( people1 == null ) {
            return null;
        }
        SexEnum sex1 = people1.getSex1();
        if ( sex1 == null ) {
            return null;
        }
        Integer code = sex1.getCode();
        if ( code == null ) {
            return null;
        }
        return code;
    }
}

11. 综合案例三【参数赋值、更新对象属性、映射反转、通过Spring依赖注入使用等】

11-1 准备如下几个Class

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student1 {
    //主键
    private long id;
    //编号
    private String no;
    //名称
    private String name1;
    //年龄
    private String age1;
    //生日
    private Date birthday1;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student2{
    //主键
    private long id;
    //编号
    private String no;
    //名称
    private String name2;
    //年龄
    private Integer age2;
    //生日
    private String birthday2;
    //地址(市)
    private String addresCity;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Address {
    //省
    private String province;
    //市
    private String city;
    //县
    private String county;
    //街道
    private String street;
}

11-2 Mapper及测试示例

11-2-1 自定义Mapper1 ⇒ 多参数赋值
@Mapper
public abstract class StudentMapper {
    public static final StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mappings({
            @Mapping(source = "address.city",target = "addresCity"),
            @Mapping(source = "student1.name1",target = "name2")
    })
    abstract Student2 toStudent2(Student1 student1,Address address);
}
@Test
public void test1(){
    Student1 student1 = new Student1();
    student1.setName1("张三");
    Address address = new Address("安徽省","安庆市","太湖县","mapStruct街道");
    Student2 student2 = StudentMapper.INSTANCE.toStudent2(student1,address);
    System.out.println(student2);
}
11-2-2 直接使用参数作为属性值
@Mapper
public abstract class StudentMapper {
    public static final StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);
    @Mappings({
            @Mapping(source = "city",target = "addresCity"),
    })
    abstract Student2 toStudent2(Student1 student1,String city);
}
@Test
public void test3(){
    Student1 student1 = new Student1();
    student1.setName1("王五");
    Student2 student2 = StudentMapper.INSTANCE.toStudent2(student1,"天津市");
    System.out.println(student2);
}
11-2-3 更新对象属性【@Mapping+@MappingTarget】
@Mapper
public abstract class StudentMapper {
    public static final StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

	// 更新对象属性主要通过@MappingTarget实现,方法无返回值
    @Mapping(source = "name1",target = "name2")
    abstract void updateStudent2(Student1 student1, @MappingTarget Student2 student2);
}
/**
     * 转化前Student2(id=0, no=null, name2=null, age2=null, birthday2=null, addresCity=北京市)
     * 转化后Student2(id=0, no=null, name2=王五, age2=null, birthday2=null, addresCity=北京市)
     */
    @Test
    public void test2(){
        Student2 student2 = new Student2();
        student2.setAddresCity("北京市");
        System.out.println("转化前"+student2);
        Student1 student1 = new Student1();
        student1.setName1("王五");
        StudentMapper.INSTANCE.updateStudent2(student1,student2);
        System.out.println("转化后"+student2);
    }
11-2-4 映射反转【@InheritInverseConfiguration和@InheritConfiguration】

在实际的项目中,
经常需要将A ——> B,
还有就是将B——> A
根据我们目前所学到的mapStruct知识,我们是这样写的, 以下是一个示例。
在这里插入图片描述
写的很繁琐,很累赘!!!
实际上在people1转化为people2的时候,规则都配置了(下文称:原映射);
而people2转people的时候,这些规则又写了一遍(下文称:新映射),
很明显原映射与新映射是一对互逆的操作,
所以如果程序智能一点,在原映射存在的前提下,people2转people1的时候应该我就不用配置了,
或者基于原映射稍微修改就好了,这是最理想的状况。
那mapStruct能做到吗?能,当然能,不然mapStruct怎么能说强大呢,
使用@InheritInverseConfiguration。

它能满足我们的需求。该注解需要注意三点:
1)有一个注解@InheritConfiguration【该注解一般配合@MappingTarget实现更新对象属性时用】, 它与该注解长得很像,请注意区分,虽然他们的功能有点相似,但是他们是不同的注解;
2)该注解虽然可以实现反转赋值,但是有一种情况需要手动加强——原映射的Mapping中没有source(典型的使用了expression、constant属性的都属于)。对于有这种情况的属性,原映射与新映射都需要指定出来;
3)该注解只有一个属性name,它的值是原映射名。
你可以把它理解为原映射对应的方法的名字,即方法的名字就是映射名。
如上面的例子,规则名是"toPeople2"。那该注解可以这样写@InheritInverseConfiguration(name=“toPeople2”)。

11-2-4-1 示例准备
@Data
@NoArgsConstructor
@AllArgsConstructor
class Student1 {
    //主键
    private Long id;
    //名称
    private String name1;
    //年龄
    private String age1;
    //创建时间
    private Date createTime1;
    //体重
    private Double weight1;
    //身高
    private String bloodType1;
    //性别
    private SexEnum sex1;
}


@Data
@NoArgsConstructor
@AllArgsConstructor
class Student2 {
    //主键
    private Long id;
    //名称
    private String name2;
    //年龄
    private Integer age2;
    //创建时间
    private String createTime2;
    //体重
    private String weight2;
    //身高
    private String bloodType2;
    //性别(0:女,1:男)
    private Integer sex2;
}

enum SexEnum {
    MAN(1, "男"),
    WOMAN(0, "女");

    @Setter
    @Getter
    private Integer code;
    @Setter
    @Getter
    private String desc;

    SexEnum(Integer code, String desc) {
        this.code = code;
        this.desc = desc;
    }
}
11-2-4-2 Mapper【@InheritInverseConfiguration】
@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mappings({
            @Mapping(source = "name1", target = "name2",defaultValue = "某某人"),
            @Mapping(target = "age2", expression = "java(java.lang.Integer.valueOf(student1.getAge1()))"),
            @Mapping(source = "createTime1", target = "createTime2", dateFormat = "yyyy-MM-dd HH:mm:ss:SSS"),
            @Mapping(source = "weight1", target = "weight2", numberFormat = "0.00"),
            @Mapping(target = "bloodType2", constant = "A 型血"),
            @Mapping(source = "sex1",target = "sex2",qualifiedByName = {"getBySexEnum"})
    })
    Student2 toStudent2(Student1 student1);

	// 这里使用@InheritInverseConfiguration注解,直接把上面映射关系匹配了,不用重新写一遍!
    @InheritInverseConfiguration(name = "toStudent2")
    @Mappings({
            @Mapping(target = "age1", expression = "java(java.lang.String.valueOf(student2.getAge2()))"),
            @Mapping(target = "bloodType1", constant = "YY 型血")
    })
    Student1 toStudent1(Student2 student2);
  //==========================================================================================
    @Named("getByCode")
    default SexEnum getByCode(Integer code){
        SexEnum[] sexEnums = SexEnum.values();
        for (SexEnum item:sexEnums){
            if(item.getCode().equals(code)){
                return item;
            }
        }
        return null;
    }

    @Named("getBySexEnum")
    default Integer getBySexEnum(SexEnum sexEnum){
        return sexEnum.getCode();
    }
}

编译后生成的实现类

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-07-24T16:19:37+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)"
)
public class StudentMapperImpl implements StudentMapper {
    @Override
    public Student2 toStudent2(Student1 student1) {
        if ( student1 == null ) {
            return null;
        }
        Student2 student2 = new Student2();
        student2.setSex2( getBySexEnum( student1.getSex1() ) );
        if ( student1.getName1() != null ) {
            student2.setName2( student1.getName1() );
        }
        else {
            student2.setName2( "某某人" );
        }
        if ( student1.getCreateTime1() != null ) {
            student2.setCreateTime2( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).format( student1.getCreateTime1() ) );
        }
        if ( student1.getWeight1() != null ) {
            student2.setWeight2( new DecimalFormat( "0.00" ).format( student1.getWeight1() ) );
        }
        student2.setId( student1.getId() );
        student2.setBloodType2( "A 型血" );
        student2.setAge2( java.lang.Integer.valueOf(student1.getAge1()) );
        return student2;
    }


    @Override
    public Student1 toStudent1(Student2 student2) {
        if ( student2 == null ) {
            return null;
        }
        Student1 student1 = new Student1();
        try {
            if ( student2.getCreateTime2() != null ) {
                student1.setCreateTime1( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).parse( student2.getCreateTime2() ) );
            }
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
        student1.setName1( student2.getName2() );
        student1.setSex1( getByCode( student2.getSex2() ) );
        try {
            if ( student2.getWeight2() != null ) {
                student1.setWeight1( new DecimalFormat( "0.00" ).parse( student2.getWeight2() ).doubleValue() );
            }
        }
        catch ( ParseException e ) {
            throw new RuntimeException( e );
        }
        student1.setId( student2.getId() );
        student1.setBloodType1( "YY 型血" );
        student1.setAge1( java.lang.String.valueOf(student2.getAge2()) );
        return student1;
    }
}
11-2-4-2 Mapper【@InheritConfiguration】 使用已有的映射更新对象属性
@Mapper
public interface StudentMapper {
    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mappings({
            @Mapping(source = "name1", target = "name2",defaultValue = "某某人"),
            @Mapping(target = "age2", expression = "java(java.lang.Integer.valueOf(student1.getAge1()))"),
            @Mapping(source = "createTime1", target = "createTime2", dateFormat = "yyyy-MM-dd HH:mm:ss:SSS"),
            @Mapping(source = "weight1", target = "weight2", numberFormat = "0.00"),
            @Mapping(target = "bloodType2", constant = "A 型血"),
            @Mapping(source = "sex1",target = "sex2",qualifiedByName = {"getBySexEnum"})
    })
    Student2 toStudent2(Student1 student1);

    @InheritConfiguration(name = "toStudent2")
    void updateStudent2(Student1 student1, @MappingTarget Student2 student2);

    @Named("getBySexEnum")
    default Integer getBySexEnum(SexEnum sexEnum){
        return sexEnum.getCode();
    }
}

编译后生成的实现类

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2020-07-24T16:19:37+0800",
    comments = "version: 1.2.0.Final, compiler: javac, environment: Java 1.8.0_201 (Oracle Corporation)"
)
public class StudentMapperImpl implements StudentMapper {
    @Override
    public Student2 toStudent2(Student1 student1) {
        if ( student1 == null ) {
            return null;
        }

        Student2 student2 = new Student2();
        student2.setSex2( getBySexEnum( student1.getSex1() ) );
        if ( student1.getName1() != null ) {
            student2.setName2( student1.getName1() );
        }
        else {
            student2.setName2( "某某人" );
        }
        if ( student1.getCreateTime1() != null ) {
            student2.setCreateTime2( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).format( student1.getCreateTime1() ) );
        }
        if ( student1.getWeight1() != null ) {
            student2.setWeight2( new DecimalFormat( "0.00" ).format( student1.getWeight1() ) );
        }
        student2.setId( student1.getId() );
        student2.setBloodType2( "A 型血" );
        student2.setAge2( java.lang.Integer.valueOf(student1.getAge1()) );
        return student2;
    }

    @Override
    public void updateStudent2(Student1 student1, Student2 student2) {
        if ( student1 == null ) {
            return;
        }
        student2.setSex2( getBySexEnum( student1.getSex1() ) );
        if ( student1.getName1() != null ) {
            student2.setName2( student1.getName1() );
        }
        else {
            student2.setName2( "某某人" );
        }
        if ( student1.getCreateTime1() != null ) {
            student2.setCreateTime2( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss:SSS" ).format( student1.getCreateTime1() ) );
        }
        if ( student1.getWeight1() != null ) {
            student2.setWeight2( new DecimalFormat( "0.00" ).format( student1.getWeight1() ) );
        }
        student2.setId( student1.getId() );
        student2.setBloodType2( "A 型血" );
        student2.setAge2( java.lang.Integer.valueOf(student1.getAge1()) );
    }
}
11-2-5 使用Spring依赖注入
// 在自定义的Mapper上添加如下固定内容:  
//			@Mapper(componentModel = "spring")
//			注:上方注解内的名称固定为  "spring" !!!
@Mapper(componentModel = "spring")
public interface CarMapper {
    CarDto carToCarDto(Car car);
}

//使用时注入即可
@Autowired
private CarMapper mapper;

12 MapStruct之@BeforeMapping和@AfterMapping注解的用法

12-1 注解的作用

作用

@BeforeMapping注解作用于方法上, 标记要在生成的映射方法开始时调用的方法。
@AfterMapping注解作用于方法之上,标记要在生成的映射方法的末尾调用的方法。

该两个注解指定的方法)既可以在抽象映射器类中实现,
	也可以通过@Mapper注解的uses属性指定的类型(类或接口)声明,
	还可以在用@Context参数的类型中实现,以便在映射方法中使用。

@BeforeMapping@AfterMapping注解都没有任何属性!

12-2 注解的使用【Mapper接口中使用、通过@Mapper注解的uses属性指定使用、通过@Context参数的类型中实现】

12-2-1 示例准备
import lombok.Data;
import java.time.LocalDate;

@Data
public class Student {
    private Long id;
    private String name;
    private String idCard;
    private LocalDate birthday;
}
import lombok.Data;
import java.time.LocalDate;

@Data
public class StudentDTO {
    private Long userId;
    private String userName;
    private String idNumber;
    private LocalDate dateOfBirth;
}
12-2-2 Mapper
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;
import org.mapstruct.AfterMapping;
import org.mapstruct.BeforeMapping;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(implementationName = "StudentConvertUtil",
        implementationPackage = "com.ylz.mapstruct.domain.convert.impl")
public interface IStudentConvert {
    IStudentConvert INSTANCE = Mappers.getMapper(IStudentConvert.class);

    @Mapping(source = "id", target = "userId")
    @Mapping(source = "name", target = "userName")
    @Mapping(source = "name", target = "idNumber")
    @Mapping(source = "birthday", target = "dateOfBirth")
    StudentDTO student2StudentDTO(Student student);

    @BeforeMapping
    default void preProcess() {
        System.out.println("执行前置处理......");
    }

    @AfterMapping
    default void postProcess() {
        System.out.println("执行后置处理......");
    }
}

编译后的实现类代码

import com.ylz.mapstruct.domain.convert.IStudentConvert;
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;

public class StudentConvertUtil implements IStudentConvert {
    public StudentConvertUtil() {
    }

    public StudentDTO student2StudentDTO(Student student) {
        this.preProcess();
        if (student == null) {
            return null;
        } else {
            StudentDTO studentDTO = new StudentDTO();
            studentDTO.setUserId(student.getId());
            studentDTO.setUserName(student.getName());
            studentDTO.setIdNumber(student.getName());
            studentDTO.setDateOfBirth(student.getBirthday());
            this.postProcess();
            return studentDTO;
        }
    }
}
12-2-3 测试
import java.time.LocalDate;

import com.ylz.mapstruct.domain.convert.impl.StudentConvertUtil;
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;

class IStudentConvertTest {

    @Test
    void test() {
        Student student = new Student();
        student.setId(1111L);
        student.setName("jack");
        student.setIdCard("33333333333");
        student.setBirthday(LocalDate.now());

        System.out.println(StudentConvertUtil.INSTANCE.student2StudentDTO(student));
    }
}

测试结果
在这里插入图片描述

12-2-4 分析

在IStudentConvert接口中,定义了三个方法,
分别是:
用@Mapping注解标注的映射方法student2StudentDTO,
用@BeforeMapping注解标注的前置处理方法preProcess,
用@AfterMapping注解标注的后置处理方法postProcess。
在这里插入图片描述
通过生成的实现类代码可以发现:
1、@BeforeMapping注解标注的方法会在映射方法中首先执行。
2、@AfterMapping注解标注的方法会在映射方法的最后一个返回语句之前执行。
3、还有一点需要注意:如果传入的参数是null,
会执行@BeforeMapping注解标注的方法,但不会执行@AfterMapping注解标注的方法!!!
4、可以同时用@BeforeMapping注解标注多个方法,
也可以同时用@AfterMapping注解标注多个方法。
在这种情况下,在生成的实现类中,这些方法会按照我们在接口或者抽象类中定义的顺序执行,如下图。
在这里插入图片描述

12-2-5 通过@Mapper注解的uses属性,指定的类型(类或接口)中声明@BeforeMapping和@AfterMapping ,如下:
import com.ylz.mapstruct.StudentFactory;
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;

@Mapper(implementationName = "StudentConvertUtil",
		// 这里通过uses属性指定StudentFactory类,该类中声明了很多 @BeforeMapping和@AfterMapping方法
        uses = StudentFactory.class,		
        implementationPackage = "com.ylz.mapstruct.domain.convert.impl")
public interface IStudentConvert {
    IStudentConvert INSTANCE = Mappers.getMapper(IStudentConvert.class);

    @Mapping(source = "id", target = "userId")
    @Mapping(source = "name", target = "userName")
    @Mapping(source = "name", target = "idNumber")
    @Mapping(source = "birthday", target = "dateOfBirth")
    StudentDTO student2StudentDTO(Student student);
}

StudentFactory

import org.mapstruct.AfterMapping;
import org.mapstruct.BeforeMapping;

public class StudentFactory {
    @BeforeMapping
    public static void preProcess() {
        System.out.println("执行preProcess方法......");
    }

    @BeforeMapping
    public static void preProcess2() {
        System.out.println("执行preProcess2方法......");
    }

    @AfterMapping
    public static void postProcess2() {
        System.out.println("执行postProcess2方法......");
    }

    @AfterMapping
    public static void postProcess() {
        System.out.println("执行postProcess方法......");
    }
}

IStudentConvert生成的实现类代码

import com.ylz.mapstruct.StudentFactory;
import com.ylz.mapstruct.domain.convert.IStudentConvert;
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;

public class StudentConvertUtil implements IStudentConvert {
    public StudentConvertUtil() {
    }

    public StudentDTO student2StudentDTO(Student student) {
        StudentFactory.preProcess();
        StudentFactory.preProcess2();
        if (student == null) {
            return null;
        } else {
            StudentDTO studentDTO = new StudentDTO();
            studentDTO.setUserId(student.getId());
            studentDTO.setUserName(student.getName());
            studentDTO.setIdNumber(student.getName());
            studentDTO.setDateOfBirth(student.getBirthday());
            StudentFactory.postProcess2();
            StudentFactory.postProcess();
            return studentDTO;
        }
    }
}

测试

import com.ylz.mapstruct.domain.convert.impl.StudentConvertUtil;
import com.ylz.mapstruct.domain.dto.StudentDTO;
import com.ylz.mapstruct.domain.entity.Student;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

class IStudentConvertTest {

    @Test
    void test() {
        Student student = new Student();
        student.setId(1111L);
        student.setName("jack");
        student.setIdCard("33333333333");
        student.setBirthday(LocalDate.now());

        System.out.println(StudentConvertUtil.INSTANCE.student2StudentDTO(student));
    }
}

在这里插入图片描述
相对于第一种场景而言,
本场景将@BeforeMapping和@AfterMapping注解标注的方法移到了单独的类中,
然后在IStudentConvert接口中,
通过@Mapper注解的uses属性指定对应类的类型实现(可以同时指定多个类型

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2212519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AIGC毕设项目分享:基于RAG的数字人对话系统及其应用

本研究的主要目标是设计并实现一个基于检索增强生成&#xff08;RAG&#xff09;技术的数字人对话系统&#xff0c;旨在提升数字人系统在多轮对话中的上下文管理、情境感知能力以及动态内容生成效果。系统结合了深度学习中的最新大语言模型技术&#xff0c;通过引入RAG框架来增…

概率论基础01_事件概率

目录 一、事件 1、概率 1.1基本事件 1.3必然事件 1.4不可能事件 1.5样本空间 1.6样本点 2、事件间的关系 2.1包含关系 2.2并集 2.3交集 2.4差集 2.5互斥事件 2.6对立事件 2.7完备事件组 3、运算律 3.1交换律 3.2结合律 3.3分配律 3.4对偶律 二、概率 1、…

10.MySql全局参数优化

从上图可以看出SQL及索引的优化效果是最好的&#xff0c;而且成本最低&#xff0c;所以工作中我们要在这块花更多时间。 一、全局参数 配置文件my.ini(windows)或my.cnf(mac)的全局参数&#xff1a; 假设服务器配置为&#xff1a; CPU&#xff1a;32核 内存&#xff1a;64G…

2025推荐选题|基于MVC的农业病虫害防治平台的设计与实现

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

机器视觉AI场景为什么用Python比C++多?

好多开发者在讨论机在机器视觉人工智能领域的时候&#xff0c;纠结到底是用Python还是C&#xff0c;实际上&#xff0c;Python 和 C 都有广泛的应用&#xff0c;选择 Python而不是 C 可能有以下一些原因&#xff1a; 语言易学性和开发效率 语法简洁&#xff1a; Python 语法简…

【部署篇】Redis-01介绍‌

一、Redis介绍‌ 1、什么是Redis&#xff1f; ‌Redis&#xff0c;英文全称是Remote Dictionary Server&#xff08;远程字典服务&#xff09;&#xff0c;Redis是一个开源的、使用‌ANSI C语言编写的‌Key-Value存储系统&#xff0c;支持网络、可基于内存亦可持久化。‌ 它提…

Android Compose 控件基本属性

本文的代码由上一篇文章的Demo进一步书写完成, 传送门:Android Compose的基本使用-CSDN博客 _____________________________________________________________________________ 以下代码分别列举了控件的: 内边距,外边距,内容居中,渐变自定义边框,宽度权重,string资源引用等…

走进面向对象编程的奇妙世界

在当今的编程领域&#xff0c;面向对象编程&#xff08;Object - Oriented Programming&#xff0c;简称 OOP&#xff09;犹如一颗璀璨的明星&#xff0c;照亮了软件开发的道路。 面向对象编程是一种基于对象概念的编程范式。那么&#xff0c;什么是对象呢&#xff1f;对象可以…

Windows系统C盘爆满了,如何清理?

Windows系统C盘爆满了&#xff0c;如何清理&#xff1f; 大家好&#xff0c;我是秋意零。 相信使用过Windows系统的朋友&#xff0c;都见过C盘那道靓丽的 “红色风景线” &#xff01; 我自己的Win10系统&#xff0c;已经使用了4-5年时间了。最近频频出现"红色风景线&q…

免费送源码:Java+Springboot+MySQL 水环境检测系统的设计与实现 计算机毕业设计原创定制

摘 要 在我国,水源的污染是不可忽视的问题。对于水质监测进行数据的采集工作,目前主要通过人工实现。因此,部分地区的采集工作,实施起来难度很大,比如恶劣环境和偏僻山区等地。所以,目前对于水质监测的研究,主导方向是建立更加高效完善,智能化的水质监测系统。近几年,无线传感器…

OpenUAV:首个专为现实无人机视觉语言导航设计的大规模轨迹数据集,由大约 12k 个轨迹组成,涵盖了多种环境和复杂的飞行动态。

2024-10-10&#xff0c;由北京航空航天大学人工智能研究所、香港中文大学MMLab以及感知与交互智能中心共同创建了OpenUAV数据集&#xff0c;首个专为现实无人机&#xff08;UAV&#xff09;视觉语言导航&#xff08;VLN&#xff09;任务设计的大型轨迹数据集&#xff0c;该数据…

java数据库操作-cnblog

创建lib目录&#xff0c;填入jar包 选择 libraries添加lib目录 package nb;import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException;public class JDBCtest {private static final String url "jdbc:mysql://localhost:3306/test?c…

ubuntu下打开摄像头

ubuntu下打开摄像头 在Ubuntu下,你可以使用cheese,这是一个开源的摄像头应用程序。如果你还没有安装它,可以通过以下命令安装: sudo apt-get updatesudo apt-get install cheese 安装完成后,你可以通过命令行启动它: cheese 或者,你也可以使用ffmpeg来打开摄像头并进…

navicate可视化数据库操作-cnblog

1 连接数据库 点击链接&#xff0c;自定义名称&#xff0c;输入root密码 2 准备按照图例创建数据库demo 3 新建数据库

使用 Visual Studio Installer Projects 打包 C# WinForms 程序的教程

前言 在开发完成一个 C# WinForms 程序后&#xff0c;打包成安装程序是发布和分发软件的重要步骤之一。通过使用 Visual Studio Installer Projects&#xff0c; 可以轻松创建一个 .exe 或 .msi 格式的安装包供用户安装。本文将详细介绍如何使用 Visual Studio Installer Proj…

springcloud之基于github webhook动态刷新服务配置

前言 在实际开发中经常会有一个叫做配置中心的服务&#xff0c;这个服务经过变更参数来动态刷新线上业务数据行为配置。比如&#xff1b;行为开关、活动数据、黑白名单、本地/预发/线上环境切换等等&#xff0c;这些配置信息往往需要在我们不重启系统的时候就可以被更新执行。那…

2.1 使用点对点信道的数据链路层

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言1 通信信道类型2 数据链路3 帧4 透明传输5 差错检测 前言 在计算机网络通信中&#xff0c;数据链路层起着关键作用。它为直接相连的网络设备之间提供可靠的数据传输服务。…

Mysql高级篇(下)——数据库设计范式

Mysql高级篇&#xff08;下&#xff09;——数据库设计范式 一、键和相关属性概念二、关系型数据库中常见的六种设计范式1. 第一范式&#xff08;1NF&#xff09;- 消除重复列&#xff0c;保证每列都是原子值2. 第二范式&#xff08;2NF&#xff09;- 消除部分依赖3. 第三范式&…

数据结构 ——— C语言实现带哨兵位双向循环链表

目录 前言 无哨兵位单向不循环链表的缺陷 带哨兵位双向循环链表的概念 带哨兵位双向循环链表的结构 带哨兵位双向循环链表逻辑结构示意图​编辑 实现带哨兵位双向循环链表的准备工作 实现带哨兵位双向循环链表 1. 创建新节点 2. 初始化哨兵位 3. 定义哨兵位指针 4. …

【最新华为OD机试E卷-支持在线评测】考勤信息(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)

🍭 大家好这里是春秋招笔试突围 ,一枚热爱算法的程序员 💻 ACM金牌🏅️团队 | 大厂实习经历 | 多年算法竞赛经历 ✨ 本系列打算持续跟新华为OD-E/D卷的多语言AC题解 🧩 大部分包含 Python / C / Javascript / Java / Cpp 多语言代码 👏 感谢大家的订阅➕ 和 喜欢�…