MapStruct 总结


title: MapStruct 总结
date: 2022-03-22 13:51:27
tags:

  • Java
    categories:
  • 开发技术及框架
    cover: https://cover.png
    feature: false

1. 概念

在系统工程开发过程中,会有各个层之间的对象转换,比如 VO、DTO、PO、DO 等,如果都是手动 setter、getter 特别浪费时间,还可能操作错误,所以选择一个自动化工具会更加方便。常见的有 json2Json、Apache 和 Spring 的 BeanUtils.copyProperties()、BeanCopier、JMapper 等

对象属性转换的操作无非是基于反射、AOP、CGlib、ASM、Javassist 在编译时和运行期进行处理,再有好的思路就是在编译前生成出对应的 setter、getter,就像手写出来的一样。MapStruct 就是直接在编译期生成对应的 setter、getter,性能更好、使用方便

2. POM

导入依赖

<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct</artifactId>
	<version>1.5.0.RC1</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-processor</artifactId>
	<version>1.5.0.RC1</version>
</dependency>
<dependency>
	<groupId>org.mapstruct</groupId>
	<artifactId>mapstruct-jdk8</artifactId>
	<version>1.5.0.RC1</version>
</dependency>

可以选择在 IDEA 中下载其插件
在这里插入图片描述

3. 实体类

定义几个 DO 和 DTO

// UserDO
@Data
@Builder
public class UserDO {
    private Integer id;
    private String name;
    private Integer age;
    private String address;
    private String birthday;
    private String phone;
    private String userDOGender;
    private Date createTime;
    private Map<String, String> map;
    private List<PersonDO> personList;
}
// UserDTO
@Data
@Builder
public class UserDTO {
    private Integer id;
    private String name;
    private Integer age;
    private String address;
    private String userDTOGender;
    private Date createTime;
    private Map<String, String> map;
    private List<PersonDO> personList;
}
// PersonDO
@Data
@Builder
public class PersonDO {
    private Integer id;
    private String name;
    private String job;
    private String remark;
    private Date createTime;
}
// PersonDTO
@Data
@Builder
public class PersonDTO {
    private Integer id;
    private String name;
    private String job;
}

3. 映射器

  • 如果 DTO 和实体类中的字段名称是一致的,只需要写方法签名即可
  • 如果参数名称有变化,需要使用 @Maping 注解,source 为原参数名称,target 为转换后的类的参数名称
  • 隐式类型转换
    在许多情况下,MapStruct 会自动处理类型转换。如在 source 中是 int 类型但在 target 中是 String 类型,会自动进行转换。以及所有 Java 基本数据类型及其相应的包装类型,如 int 和 Integer,boolean 和 Boolean 等。还有所有 Java Number类型和包装器类型之间,如 int 和 long 或 byte 和 Integer
@Mapper // 直接使用
@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {

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

    @Mapping(source = "userDOGender", target = "userDTOGender")
    UserDTO userDOToUserDTO(UserDO userDO);

    @Mapping(source = "userDTOGender", target = "userDOGender")
    UserDO UserDTOToUserDO(UserDTO userDTO);

    PersonDTO personDOToPersonDTO(PersonDO personDO);

    PersonDO PersonDTOToPersonDO(PersonDTO personDTO);
}

编译后,会在同级目录生成实现类:
在这里插入图片描述

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-03-22T12:16:06+0800",
    comments = "version: 1.5.0.RC1, compiler: javac, environment: Java 1.8.0_131 (Oracle Corporation)"
)
public class MapStructImpl implements MapStruct {

    @Override
    public UserDTO userDOToUserDTO(UserDO userDO) {
        if ( userDO == null ) {
            return null;
        }

        UserDTO.UserDTOBuilder userDTO = UserDTO.builder();

        userDTO.userDTOGender( userDO.getUserDOGender() );
        userDTO.id( userDO.getId() );
        userDTO.name( userDO.getName() );
        userDTO.age( userDO.getAge() );
        userDTO.address( userDO.getAddress() );
        userDTO.createTime( userDO.getCreateTime() );
        Map<String, String> map = userDO.getMap();
        if ( map != null ) {
            userDTO.map( new LinkedHashMap<String, String>( map ) );
        }
        List<PersonDO> list = userDO.getPersonList();
        if ( list != null ) {
            userDTO.personList( new ArrayList<PersonDO>( list ) );
        }

        return userDTO.build();
    }

    @Override
    public UserDO UserDTOToUserDO(UserDTO userDTO) {
        if ( userDTO == null ) {
            return null;
        }

        UserDO.UserDOBuilder userDO = UserDO.builder();

        userDO.userDOGender( userDTO.getUserDTOGender() );
        userDO.id( userDTO.getId() );
        userDO.name( userDTO.getName() );
        userDO.age( userDTO.getAge() );
        userDO.address( userDTO.getAddress() );
        userDO.createTime( userDTO.getCreateTime() );
        Map<String, String> map = userDTO.getMap();
        if ( map != null ) {
            userDO.map( new LinkedHashMap<String, String>( map ) );
        }
        List<PersonDO> list = userDTO.getPersonList();
        if ( list != null ) {
            userDO.personList( new ArrayList<PersonDO>( list ) );
        }

        return userDO.build();
    }

    @Override
    public PersonDTO personDOToPersonDTO(PersonDO personDO) {
        if ( personDO == null ) {
            return null;
        }

        PersonDTO.PersonDTOBuilder personDTO = PersonDTO.builder();

        personDTO.id( personDO.getId() );
        personDTO.name( personDO.getName() );
        personDTO.job( personDO.getJob() );

        return personDTO.build();
    }

    @Override
    public PersonDO PersonDTOToPersonDO(PersonDTO personDTO) {
        if ( personDTO == null ) {
            return null;
        }

        PersonDO.PersonDOBuilder personDO = PersonDO.builder();

        personDO.id( personDTO.getId() );
        personDO.name( personDTO.getName() );
        personDO.job( personDTO.getJob() );

        return personDO.build();
    }
}

4. 测试

public class Test {
    public static void main(String[] args) {
        UserDO userDO = UserDO.builder().id(1)
                .name("张三")
                .age(18)
                .birthday("2003-01-04")
                .phone("12343")
                .userDOGender("男")
                .createTime(new Date(System.currentTimeMillis()))
                .map(new HashMap<String, String>(){
                    {
                        this.put("key", "value");
                    }
                })
                .personList(new ArrayList<PersonDO>(){
                    {
                        this.add(PersonDO.builder().id(11).build());
                    }
                }).build();
        UserDTO userDTO = Mappers.getMapper(MapStruct.class).userDOToUserDTO(userDO);
        System.out.println(userDTO);

        PersonDTO personDTO = PersonDTO.builder().id(11)
                .name("李四")
                .job("开发").build();
        PersonDO personDO = MapStruct.INSTANCE.PersonDTOToPersonDO(personDTO);
        System.out.println(personDO);
    }
}

在这里插入图片描述

5. 自定义转换规则

MapStruct 只提供了隐式类型转换和默认的一些转换,假如需要特定的类型转换,如将 java.sql.Timestamp 日期转换为只保留年月日的字符串等,可以自定义转换规则

5.1 使用 expression 表达式

1、创建一个转换规则类

public class MapStructRule {
    public static String toDate(Timestamp date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        return simpleDateFormat.format(date);
    }
}

2、在映射器对应的转换方法上使用 @Mapping 注解的 expression 属性标识转换规则,这里的属性值为需要写 全类名.方法名(参数),使用了 expression 就不需要 source 属性

@Mapper(componentModel = "spring") // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getCreateTime()))")
    @Mapping(target = "updateTime", expression = "java(fan.fanblog.utils.MapStructRule.toDate(menuDO.getUpdateTime()))")
    MenuVO MenuDOToMenuVO(MenuDO menuDO);
}

3、测试类

public class Demo {
    public static void main(String[] args) {
        MenuDO menuDO = new MenuDO();

        menuDO.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        System.out.println(menuDO);

        MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO);
        System.out.println(menuVO);
    }
}

在这里插入图片描述

5.2 使用 @Named 注解

1、创建一个转换规则类,在对应的转换方法上标注 @Named 注解表示转换方法名

public class MapStructRule {
    @Named("toDate")
    public static String toDate(Timestamp date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return simpleDateFormat.format(date);
    }
}

2、在映射器上使用 @Mapperuses 属性,属性值为转换规则类的 Class 对象,然后再映射器对应的方法上使用 @Mapping 注解的 qualifiedByName 属性,属性值为转换规则类 @Named 定义的方法名

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", source = "createTime", qualifiedByName = "toDate")
    @Mapping(target = "updateTime", source = "updateTime", qualifiedByName = "toDate")
    MenuVO MenuDOToMenuVO(MenuDO menuDO);
}

3、测试类

public class Demo {
    public static void main(String[] args) {
        MenuDO menuDO = new MenuDO();

        menuDO.setCreateTime(new Timestamp(System.currentTimeMillis()));
        menuDO.setUpdateTime(new Timestamp(System.currentTimeMillis()));
        System.out.println(menuDO);

        MenuVO menuVO = MapStruct.INSTANCE.MenuDOToMenuVO(menuDO);
        System.out.println(menuVO);
    }
}

在这里插入图片描述

6. 忽略属性不转换

@Mapper(componentModel = "spring", uses = MapStructRule.class) // 整合 Spring,设置 componentModel = "spring",需要使用的地方直接通过 @Resource 注入即可
public interface MapStruct {
    MapStruct INSTANCE = Mappers.getMapper(MapStruct.class);

    @Mapping(target = "createTime", ignore = true)
    @Mapping(target = "updateTime", ignore = true)
    MenuDO MenuVOToMenuDO(MenuVO menuVO);
}
<think>好的,我现在需要解决如何在项目中正确引入MapStruct依赖的问题。首先,我应该回忆一下MapStruct的基本信息。MapStruct是一个Java注解处理器,用于生成类型安全的Bean映射代码。所以,引入依赖的时候需要同时包含MapStruct的核心库和注解处理器。 接下来,我需要考虑不同的构建工具,比如Maven和Gradle,因为它们的依赖配置方式不同。对于Maven项目,通常是在pom.xml中添加依赖项。MapStruct的核心库是必须的,而注解处理器则需要在annotationProcessorPath中配置,或者在Maven Compiler插件中指定。记得版本号要统一,避免冲突。 然后,Gradle项目的配置方式可能稍有不同。在build.gradle文件中,需要添加MapStruct的依赖,并配置注解处理器。Gradle使用annotationProcessor来指定注解处理器,所以需要确保这部分正确配置。 另外,可能需要注意IDE的设置,比如IntelliJ IDEA或Eclipse是否启用了注解处理功能。有时候即使依赖正确,如果IDE没有启用注解处理,生成的代码也不会出现,导致编译错误。 还需要检查MapStruct与其他库的兼容性,比如使用的Java版本是否支持。MapStruct通常要求Java 8或更高版本。另外,如果项目中使用了Lombok,可能需要调整注解处理器的顺序,确保两者都能正常工作。 查看用户提供的引用内容,引用[1]和引用[2]提到了Mapper接口的创建和自定义转换器的使用,但并没有直接说明依赖配置。所以主要依据官方文档或常见做法来回答。 最后,总结步骤:确定构建工具,添加核心依赖和注解处理器,配置构建插件,检查IDE设置,处理可能的冲突。同时,给出示例代码,比如Maven和Gradle的配置片段,帮助用户更直观地理解。</think>### 如何在项目中正确引入MapStruct依赖? #### 1. **构建工具选择** MapStruct支持主流构建工具(Maven、Gradle)。核心依赖和注解处理器需同时配置。 #### 2. **Maven项目配置** 在`pom.xml`中添加以下内容: ```xml <properties> <org.mapstruct.version>1.5.5.Final</org.mapstruct.version> </properties> <dependencies> <!-- MapStruct核心库 --> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct</artifactId> <version>${org.mapstruct.version}</version> </dependency> </dependencies> <build> <plugins> <!-- 配置注解处理器 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <annotationProcessorPaths> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> </configuration> </plugin> </plugins> </build> ``` #### 3. **Gradle项目配置** 在`build.gradle`中添加: ```groovy plugins { id 'java' } ext { mapstructVersion = "1.5.5.Final" } dependencies { implementation "org.mapstruct:mapstruct:${mapstructVersion}" annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" } // 确保注解处理器在编译时运行 tasks.withType(JavaCompile) { options.compilerArgs += '-Amapstruct.defaultComponentModel=spring' // 可选,若需与Spring集成[^2] } ``` #### 4. **IDE设置** - **IntelliJ IDEA**:确保启用注解处理(`Settings > Build > Compiler > Annotation Processors`,勾选`Enable annotation processing`)。 - **Eclipse**:安装m2e-apt插件以支持自动生成代码。 #### 5. **常见问题处理** - **Lombok兼容性**:若项目同时使用Lombok,需在Maven/Gradle中优先配置Lombok的注解处理器。 ```xml <!-- Maven中Lombok+MapStruct配置示例 --> <annotationProcessorPaths> <path> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> </path> <path> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> <version>${org.mapstruct.version}</version> </path> </annotationProcessorPaths> ``` - **版本冲突**:确保所有MapStruct相关依赖版本一致。 #### 6. **验证引入成功** 创建Mapper接口并编译项目,检查`target/generated-sources`(Maven)或`build/generated/sources`(Gradle)目录下是否生成实现类。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GreyFable

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值