diff --git a/LICENSE b/LICENSE index 02c44e5d1d3b93e8c12f7317eff6caef721a710d..3f8600041bc2bfec8de757f16bbd1a41769a011f 100644 --- a/LICENSE +++ b/LICENSE @@ -191,11 +191,9 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and - limitations under the License. + limitations under the License. diff --git a/README.md b/README.md index e15aae9f3049f10fc0b6ad9b7060d6d43027ef3e..f9de87cf3b890ccedc7439f83a94a528c5fde914 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,39 @@ -# mapstruct-demo +--- -#### 介绍 -mapstruct笔记 +# MapStruct + +## 介绍 + +#### MapStruct 学习笔记 + +MapStruct 是一款强大的 Java 工具,专注于解决不同对象间的高效复制问题,如 PO(持久对象)、DTO(数据传输对象)、VO(视图对象)以及 QueryParam 之间的转换。相较于基于反射机制的 BeanUtils 类库,MapStruct 利用编译器生成针对性的方法,显著提升了转换性能。 + +#### JavaBean 转换问题背景 + +在实际开发中,我们常常面临众多 JavaBean 间的相互转换需求。过去,常见的处理方式包括: + +- ##### **拷贝技术** + + - 使用 Apache Commons 的 PropertyUtils.copyProperties 或 BeanUtils.copyProperties + - Spring Framework 的 BeanUtils.copyProperties +- CGLIB 的 BeanCopier 类进行快速拷贝 + +- ##### **手动 get/set** + + - 部分开发者借助 IDE 插件自动填充 set 方法实现对象复制,但这种方法可能并不为所有开发者熟知 + - 即使如此,当新增字段时仍需手动更新,并且存在代码冗余和开发效率较低的问题 + +#### MapStruct 解决方案 + +MapStruct 是一款基于 Java 注解处理器的框架,它能够自动生成类型安全的 Bean 映射类。开发者只需定义一个映射器接口并声明所需的方法,编译阶段,MapStruct 就会创建对应的接口实现。这个实现利用简单的 Java get、set 方法执行源对象到目标对象的映射操作,而非依赖反射。 + +#### MapStruct 优势概述 + +1. **提高开发效率**:通过自动生成复杂的映射代码,避免了手写过程中可能出现的错误和繁琐劳动。 +2. **编译时类型安全**:确保只有能互相匹配的对象及属性才能完成映射,有效防止诸如将订单实体误映射至客户 DTO 等错误发生。 +3. **即时编译反馈**:当映射不完全(即未映射所有目标属性)或映射不正确(例如找不到适当的映射方法或类型转换时),编译器会在编译阶段就发出警告提示。 + +## 使用 #### 引入maven依赖 ``` @@ -53,7 +85,7 @@ mapstruct笔记 ... ``` -#### Mapper配置项 +#### @Mapper配置项 | 选择 | 目的 | 违约 | | :----------------------------------------------- | :----------------------------------------------------------- | :-------- | @@ -101,4 +133,606 @@ mappingControl:指定映射控制实现类。 - conditionQualifiedBy与qualifiedBy类似,只适用于Condition方法 - conditionQualifiedByName同上 - ***@BeanMapping(resultType= Class)*** -- \ No newline at end of file + + + +## 定义映射器 + +#### 1.基本映射 + +```JAVA +import com.fjg.mapstructdemo.dto.UserInfoNew; +import com.fjg.mapstructdemo.dto.UserInfoOld; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 基础映射 + * @author fengjianguo + * @date 2023/12/29 13:42 + */ +@Mapper +public interface UserInfoConvert { + + UserInfoConvert INSTANCE = Mappers.getMapper(UserInfoConvert.class); + + /** + * 旧的实体类转换为新的实体类 + * @param userInfoOld + * @return + */ + @Mapping(source = "name",target = "userName") + @Mapping(source = "sex",target = "userSex") + @Mapping(source = "age",target = "userAge") + @Mapping(source = "address",target = "userAddress") + UserInfoNew convert(UserInfoOld userInfoOld); + + /** + * 转换 + * + * @param userInfoOlds 用户信息旧版 + * @return {@link List}<{@link UserInfoNew}> + */ + List convert(List userInfoOlds); + +} +``` + +##### 说明: + +1. @Mapper注解会导致MapStruct代码生成器在构建时创建CarMapper接口的实现。 +2. 在生成的方法实现中,源类型(例如UserInfoOld)的所有可读属性将被复制到目标类型(例如UserInfoNew)的相应属性中。 +3. 当属性与目标实体的属性同名时,它们将被隐式映射。 当属性在目标实体中具有不同的名称时,可以通过@Mapping注解指定其名称。 +4. 必须在@Mapping注解中指定属性名称,该名称定义在JavaBeans规范中,例如对于具有访问器方法getName()和setName()的属性,名称为name。可以结合Lombok使用。 + +#### 2.映射合成 + +```JAVA +import org.mapstruct.Mapping; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 元注解 + * ElementType.TYPE:类、接口、枚举等类型。 + * ElementType.FIELD:字段(成员变量)。 + * ElementType.METHOD:方法。 + * ElementType.PARAMETER:方法参数。 + * ElementType.CONSTRUCTOR:构造函数。 + * ElementType.LOCAL_VARIABLE:局部变量。 + * ElementType.ANNOTATION_TYPE:注解类型。 + * ElementType.PACKAGE:包。 + * ElementType.TYPE_PARAMETER:泛型类型参数。 + * ElementType.TYPE_USE:类型使用。 + * RetentionPolicy.CLASS,表示该注解将在编译期间保留 + * RetentionPolicy.SOURCE:表示该注解只会在源代码中存在,在编译后会被丢弃,不能被读取到。 + * RetentionPolicy.RUNTIME:表示该注解将在运行期间保留,并能够通过反射机制访问到。 + * @author fengjianguo + * @date 2023/12/29 14:08 + */ +@Retention(RetentionPolicy.CLASS) +@Mapping(target = "id", expression = "java(java.util.UUID.randomUUID().toString())") +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) +public @interface BaseEntity { + +} + +``` + +```JAVA +import com.fjg.mapstructdemo.dto.UserInfoNew; +import com.fjg.mapstructdemo.dto.UserInfoOld; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 基础映射 + * @author fengjianguo + * @date 2023/12/29 13:42 + */ +@Mapper +public interface UserInfoConvert { + + UserInfoConvert INSTANCE = Mappers.getMapper(UserInfoConvert.class); + + /** + * 旧的实体类转换为新的实体类 + * @param userInfoOld + * @return + */ + @BaseEntity + @Mapping(source = "name",target = "userName") + @Mapping(source = "sex",target = "userSex") + @Mapping(source = "age",target = "userAge") + @Mapping(source = "address",target = "userAddress") + UserInfoNew convert(UserInfoOld userInfoOld); + + /** + * 转换 + * + * @param userInfoOlds 用户信息旧版 + * @return {@link List}<{@link UserInfoNew}> + */ + List convert(List userInfoOlds); + +} +``` + +##### 说明: + +1. MapStruct 支持使用元注解,可以将通用的映射部分抽取出来 + +#### 3.向映射器添加自定义方法 + +```JAVA + /** + * 旧的实体类转换为新的实体类 + * + * @param userInfoOld + * @return + */ + @BaseEntity + @Mapping(source = "name", target = "userName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress", qualifiedByName = "getAddress") + UserInfoNew convert(UserInfoOld userInfoOld); + + + /** + * 自定义默认方法 + * + * @param address 地址 + * @return {@link String } + */ + @Named("getAddress") + default String getAddress(String address) { + return address; + } +``` + +##### 说明: + +1. 通过qualifiedByName与@Named进行绑定,在进行转换时找寻到该方法。 + +#### 4.多参数映射 + +```JAVA + /** + * 旧的实体类转换为新的实体类 + * + * @param userInfoOld 用户信息旧 + * @param userName 用户名 + * @return {@link UserInfoNew } + */ + @Mapping(source = "userName", target = "userName") + @Mapping(source = "userInfoOld.sex", target = "userSex") + @Mapping(source = "userInfoOld.age", target = "userAge") + @Mapping(source = "userInfoOld.address", target = "userAddress") + UserInfoNew convert(UserInfoOld userInfoOld, String userName); + +``` + +##### 说明: + +1. 如果多个源对象都使用相同的属性名称进行定义,必须使用@Mapping注解来指定从哪个源参数获取属性,就像示例中的userName属性一样。如果不解决这种歧义,将引发错误。对于在给定的源对象中只存在一次的属性,可以选择性地指定源参数的名称,因为它可以自动确定。 +2. 在使用@Mapping注解时,指定属性所在的参数是强制的。 +3. 具有多个源参数的映射方法在所有源参数都为null的情况下将返回null。否则,将实例化目标对象,并将提供的参数的所有属性传播到目标对象。 + +#### 5.嵌套bean映射 + +```JAVA +@Mapping(source="userInfoOld", target = ".") +UserInfoOld toConvert(UserInfoNew userInfoNew); +``` + +##### 说明: + +1. 生成的代码将直接UserInfoNew.userInfoOld的每个属性映射到UserInfoOld,无需手动命名它们。 +2. @Mapping(source="userInfoOld", target = ".")的意思是将 UserInfoNew对象的userInfoOld属性的所有属性映射到UserInfoOld对象本身的属性中,而不是将其映射到 UserInfoOld对象的一个名为 userInfoOld的属性上。 + +#### 6.更新现有的bean + +```JAVA + @Mapping(source = "userName", target = "userName") + @Mapping(source = "userInfoOld.sex", target = "userSex") + @Mapping(source = "userInfoOld.age", target = "userAge") + @Mapping(source = "userInfoOld.address", target = "userAddress") + void voidConvert(@MappingTarget UserInfoNew userInfoNew, UserInfoOld userInfoOld, String userName); +``` + +##### 说明: + +1. 在某些情况下,可能需要进行映射操作,而不是创建目标类型的新实例,而是更新现有的实例。这种类型的映射可以通过添加一个目标对象参数,并使用@MappingTarget注解标记该参数来实现。 + +#### 7.反向映射 + +```JAVA + /** + * 旧的实体类转换为新的实体类 + * + * @param userInfoOld + * @return + */ + @BaseEntity + @Mapping(source = "name", target = "userName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress", qualifiedByName = "getAddress") + UserInfoNew convert(UserInfoOld userInfoOld); + + /** + * 新的实体类转换为旧的实体类 + * + * @param userInfoNew 用户信息新 + * @return {@link UserInfoOld } + */ + @InheritInverseConfiguration + UserInfoOld convert(UserInfoNew userInfoNew); +``` + +##### 说明: + +1. 为了减少重复编码,如果以及设置了正向映射(UserInfoOld => UserInfoNew)关系,可以直接通过@InheritInverseConfiguration进行反向映射(UserInfoNew => UserInfoOld )。 + +#### 8.map映射到bean + +```JAVA + @BeanMapping(ignoreByDefault = true) + @Mapping(source = "name", target = "userName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress") + UserInfoNew mapToBean(Map map); +``` + +##### 注意: + +1. 如果目标对象存在嵌套的bean必须对嵌套的bean设置映射关系,或者将嵌套的bean进行忽略。 + +## 检索映射器(配置注入方式) + +#### 1.工厂注入 + +```JAVA +@Mapper +public interface UserInfoConvert { + + UserInfoConvert INSTANCE = Mappers.getMapper(UserInfoConvert.class); + UserInfoNew convert(UserInfoOld userInfoOld); +} +``` + +##### 说明: + +1. 当不使用 DI 框架时,可以通过类检索 Mapper 实例。只需调用该方法,将映射器的接口类型传递给 return:`org.mapstruct.factory.Mappers``getMapper()` + +#### 2.指定注入策略 + +```java +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING + , uses = EngineMapper.class + , injectionStrategy = InjectionStrategy.CONSTRUCTOR) +public interface UserInfoConvert { + + UserInfoConvert INSTANCE = Mappers.getMapper(UserInfoConvert.class); + + /** + * 旧的实体类转换为新的实体类 + * + * @param userInfoOld + * @return + */ + @BaseEntity + @Mapping(source = "name", target = "userName",qualifiedByName = "getEngineName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress", qualifiedByName = "getAddress") + UserInfoNew convert(UserInfoOld userInfoOld); +} + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface EngineMapper { + + /** + * 获取引擎名称 + * + * @param engineName 发动机名称 + * @return {@link String } + */ + @Named("getEngineName") + default String getEngineName(String engineName) { + return engineName + " engine"; + } +} +``` + +##### 说明: + +1. componentModel:指定了MapStruct生成的映射器实例应该被Spring管理。 +2. uses:表示此映射器在进行映射时可能会用到`EngineMapper`类中的映射方法。有助于复用已定义的映射逻辑。 +3. injectionStrategy:指定了MapStruct在注入依赖时应使用的策略为构造器注入。CONSTRUCTOR意味着MapStruct在生成映射器实现时,会优先通过构造器来注入依赖项,而不是使用字段注入或其他方式。 + +## 数据类型转换 + +#### 1.基本类型转换 + +mapstruct会自动完成基本类型的一些隐式转换(包括:boolean、byte、char、int、long、float、double、String及其包装类等) + +相同基本类型可以直接转换 + +StringBuilder与String可以直接转换 + +大数类型与基本类型及其包装类、String可以直接转换 + +```JAVA +@Mapping(target = "intValue", source = "intValue") +@Mapping(target = "stringValue", source = "intValue") +@Mapping(target = "stringValue", source = "booleanValue") +// 强制转换,长字节类型转短字节类型会发生截断,注意数据溢出,精度丢失等问题。 +@Mapping(target = "byteValue", source = "intValue") +// 基本类型与其包装类型可以直接转换,且会自动生成判空代码 +@Mapping(target = "intValue", source = "integerValue") +``` + +#### 2.时间类型转换 + +隐式转换规则 + +1. java.time.Instant与java.util.Date直接转换 +2. java.sql.Date与java.util.Date直接转换 +3. java.sql.Time 与java.util.Date直接转换 +4. java.sql.Timestamp 与java.util.Date直接转换 + +```JAVA +@Mapper +public interface TimeConvert { + + TimeConvert INSTANCE = Mappers.getMapper(TimeConvert.class); + @Mapping(source = "localDateTime", target = "localDateTime", dateFormat = "yyyy-MM-dd HH:mm:ss") + @Mapping(source = "date", target = "date", dateFormat = "yyyy-MM-dd HH:mm:ss") + TimeNew convert(TimeOld timeOld); +} + +@Data +public class TimeNew { + private String localDateTime; + private String date; +} + +@Data +public class TimeOld { + private LocalDateTime localDateTime; + private Date date; +} +``` + +#### 3.集合类型转换 + +```JAVA + @BaseEntity + @Mapping(source = "name", target = "userName",qualifiedByName = "getEngineName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress", qualifiedByName = "getAddress") + UserInfoNew convert(UserInfoOld userInfoOld); + + + /** + * 转换 + * + * @param userInfoOlds 用户信息旧版 + * @return {@link List}<{@link UserInfoNew}> + */ + List convert(List userInfoOlds); +``` + +#### 4.枚举类型转换 + +##### 4.1 枚举和枚举映射 + +```JAVA +// AgeEnum +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + + +@Getter +@AllArgsConstructor +public enum AgeEnum { + + /** + * 年龄 + */ + ZERO("0", "100"), + ONE("1", "18"), + TWO("2", "40"), + THREE("3", "81"); + + private final String code; + + private final String desc; +} + +// NameEnum +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum NameEnum { + /** + * 名字 + */ + ZERO("0", "未知"), + ONE("1", "张三"), + TWO("2", "李四"), + THREE("3", "王五"); + + private final String code; + + private final String desc; +} + + +// 转换类 +package com.fjg.mapstructdemo.convert; + +import com.fjg.mapstructdemo.enums.AgeEnum; +import com.fjg.mapstructdemo.enums.NameEnum; +import org.mapstruct.*; + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface NameEnumConvert { + /** + * 转换 + * + * @param ageEnum 年龄枚举 + * @return {@link NameEnum } + */ + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "ONE" ), + @ValueMapping( source = "THREE", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "THREE" ) + }) + NameEnum convert(AgeEnum ageEnum); +} + + +``` + +###### 说明: + +​ 1.@ValueMapping( source = MappingConstants.NULL, target = "ONE" ),表示如果ageEnum为null则返回AgeEnum.ONE + +​ 2.@ValueMapping( source = "THREE", target = MappingConstants.NULL),表示如果传入AgeEnum.THREE,则返回null + +​ 3.@ValueMapping( source = MappingConstants.ANY_REMAINING, target = "THREE" ),表示默认返回NameEnum.THREE + + + +##### 4.2枚举和字符串映射 + +```JAVA + /** + * 转换为str + * + * @param ageEnum 年龄枚举 + * @return {@link String } + */ + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "ONE" ), + @ValueMapping( source = "THREE", target = "THREE" ), + @ValueMapping( source = "ONE", target = "ONE" ), + @ValueMapping( source = "TWO", target = "TWO" ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "ZERO" ) + }) + String convertToStr(AgeEnum ageEnum); + + @InheritInverseConfiguration + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "ZERO") + AgeEnum convertToStr(String nameEnum); +``` + +###### 说明: + +​ 1.**MappingConstants.ANY_UNMAPPED**用于返回字符串,**MappingConstants.ANY_REMAINING**用于返回枚举 + + + +##### 4.3自定义名称转换 + +```JAVA +// CustomEnum +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum CustomEnum implements BaseEnum { + + /** + * 自定义枚举 + */ + ONE("1", "张三"), + TWO("2", "李四"), + THREE("3", "王五"); + private final String code; + private final String desc; + +} + +// CustomStateEnum +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public enum CustomStateEnum implements BaseEnum { + + /** + * 自定义枚举 + */ + ONE_STATE("1", "张三"), + TWO_STATE("2", "李四"), + THREE_STATE("3", "王五"); + private final String code; + private final String desc; + +} + + +// 转换类 +package com.fjg.mapstructdemo.convert; + +import com.fjg.mapstructdemo.enums.CustomEnum; +import com.fjg.mapstructdemo.enums.CustomStateEnum; +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface CustomEnumConvert { + /** + * 转换 + * + * @param customStateEnum 自定义状态枚举 + * @return {@link CustomEnum } + */ + @EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_SUFFIX_TRANSFORMATION, configuration = "_STATE") + CustomEnum convert(CustomStateEnum customStateEnum); + +} + +``` + +###### 说明: + +​ 1.@EnumMapping的策略 + +​ a.MappingConstants.SUFFIX_TRANSFORMATION (添加源后缀) + +​ b.MappingConstants.STRIP_SUFFIX_TRANSFORMATION (删除源后缀) + +​ c.MappingConstants.PREFIX_TRANSFORMATION(添加源前缀) + +​ d.MappingConstants.STRIP_PREFIX_TRANSFORMATION(删除源前缀) + diff --git a/src/main/java/com/fjg/mapstructdemo/convert/CustomEnumConvert.java b/src/main/java/com/fjg/mapstructdemo/convert/CustomEnumConvert.java new file mode 100644 index 0000000000000000000000000000000000000000..75d1e26fed03749b2b0e9ac866d04a7755ec5f18 --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/convert/CustomEnumConvert.java @@ -0,0 +1,24 @@ +package com.fjg.mapstructdemo.convert; + +import com.fjg.mapstructdemo.enums.CustomEnum; +import com.fjg.mapstructdemo.enums.CustomStateEnum; +import org.mapstruct.EnumMapping; +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; + +/** + * @author fengjianguo + * @date 2024/9/23 10:50 + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface CustomEnumConvert { + /** + * 转换 + * + * @param customStateEnum 自定义状态枚举 + * @return {@link CustomEnum } + */ + @EnumMapping(nameTransformationStrategy = MappingConstants.STRIP_SUFFIX_TRANSFORMATION, configuration = "_STATE") + CustomEnum convert(CustomStateEnum customStateEnum); + +} diff --git a/src/main/java/com/fjg/mapstructdemo/convert/EngineMapper.java b/src/main/java/com/fjg/mapstructdemo/convert/EngineMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..4d337801e9bae70a8eb0dab479aaefe7419d8ef9 --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/convert/EngineMapper.java @@ -0,0 +1,24 @@ +package com.fjg.mapstructdemo.convert; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import org.mapstruct.Named; + +/** + * @author fengjianguo + * @date 2024/8/30 14:21 + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface EngineMapper { + + /** + * 获取引擎名称 + * + * @param engineName 发动机名称 + * @return {@link String } + */ + @Named("getEngineName") + default String getEngineName(String engineName) { + return engineName + " engine"; + } +} diff --git a/src/main/java/com/fjg/mapstructdemo/convert/NameEnumConvert.java b/src/main/java/com/fjg/mapstructdemo/convert/NameEnumConvert.java new file mode 100644 index 0000000000000000000000000000000000000000..541eeb75af4666231ee703f8369b6dbb12fd3157 --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/convert/NameEnumConvert.java @@ -0,0 +1,45 @@ +package com.fjg.mapstructdemo.convert; + +import com.fjg.mapstructdemo.enums.AgeEnum; +import com.fjg.mapstructdemo.enums.NameEnum; +import org.mapstruct.*; + +/** + * @author fengjianguo + * @date 2024/8/30 14:57 + */ +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface NameEnumConvert { + /** + * 转换 + * + * @param ageEnum 年龄枚举 + * @return {@link NameEnum } + */ + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "ONE" ), + @ValueMapping( source = "THREE", target = MappingConstants.NULL ), + @ValueMapping( source = MappingConstants.ANY_REMAINING, target = "THREE" ) + }) + NameEnum convert(AgeEnum ageEnum); + + /** + * 转换为str + * + * @param ageEnum 年龄枚举 + * @return {@link String } + */ + @ValueMappings({ + @ValueMapping( source = MappingConstants.NULL, target = "ONE" ), + @ValueMapping( source = "THREE", target = "THREE" ), + @ValueMapping( source = "ONE", target = "ONE" ), + @ValueMapping( source = "TWO", target = "TWO" ), + @ValueMapping( source = MappingConstants.ANY_UNMAPPED, target = "ZERO" ) + }) + String convertToStr(AgeEnum ageEnum); + + @InheritInverseConfiguration + @ValueMapping(source = MappingConstants.ANY_REMAINING, target = "ZERO") + AgeEnum convertToStr(String nameEnum); + +} diff --git a/src/main/java/com/fjg/mapstructdemo/convert/NumberConvert.java b/src/main/java/com/fjg/mapstructdemo/convert/NumberConvert.java index 4db420fe649892d5c2490580aa80afa5bba2c17f..afcfbce2db329f4172ea6e3797987b4d62061783 100644 --- a/src/main/java/com/fjg/mapstructdemo/convert/NumberConvert.java +++ b/src/main/java/com/fjg/mapstructdemo/convert/NumberConvert.java @@ -2,6 +2,7 @@ package com.fjg.mapstructdemo.convert; import com.fjg.mapstructdemo.dto.NumberNew; import com.fjg.mapstructdemo.dto.NumberOld; +import org.mapstruct.BeforeMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.factory.Mappers; @@ -20,4 +21,8 @@ public interface NumberConvert { @Mapping(source = "floatNumber", target = "floatNumber", numberFormat = "#.##") @Mapping(source = "longNumber", target = "longNumber", numberFormat = "#.00") NumberNew convert(NumberOld numberOld); + @BeforeMapping() + default void before(NumberOld numberOld){ + System.out.println(numberOld); + } } diff --git a/src/main/java/com/fjg/mapstructdemo/convert/UserInfoConvert.java b/src/main/java/com/fjg/mapstructdemo/convert/UserInfoConvert.java index 45c8a8adab3054dbb7cf6bc6afd204943a0d54eb..b10bd02006e481b9a449895496b10a7c1ecfe561 100644 --- a/src/main/java/com/fjg/mapstructdemo/convert/UserInfoConvert.java +++ b/src/main/java/com/fjg/mapstructdemo/convert/UserInfoConvert.java @@ -2,34 +2,39 @@ package com.fjg.mapstructdemo.convert; import com.fjg.mapstructdemo.dto.UserInfoNew; import com.fjg.mapstructdemo.dto.UserInfoOld; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; +import org.mapstruct.*; import org.mapstruct.factory.Mappers; import java.util.List; +import java.util.Map; /** * 基础映射 + * * @author fengjianguo * @date 2023/12/29 13:42 */ -@Mapper +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING + , uses = EngineMapper.class + , injectionStrategy = InjectionStrategy.CONSTRUCTOR) public interface UserInfoConvert { UserInfoConvert INSTANCE = Mappers.getMapper(UserInfoConvert.class); /** * 旧的实体类转换为新的实体类 + * * @param userInfoOld * @return */ @BaseEntity - @Mapping(source = "name",target = "userName") - @Mapping(source = "sex",target = "userSex") - @Mapping(source = "age",target = "userAge") - @Mapping(source = "address",target = "userAddress") + @Mapping(source = "name", target = "userName",qualifiedByName = "getEngineName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress", qualifiedByName = "getAddress") UserInfoNew convert(UserInfoOld userInfoOld); + /** * 转换 * @@ -37,5 +42,54 @@ public interface UserInfoConvert { * @return {@link List}<{@link UserInfoNew}> */ List convert(List userInfoOlds); + /** + * 新的实体类转换为旧的实体类 + * + * @param userInfoNew 用户信息新 + * @return {@link UserInfoOld } + */ + @InheritInverseConfiguration + UserInfoOld convert(UserInfoNew userInfoNew); + + @Mapping(source = "userName", target = "userName") + @Mapping(source = "userInfoOld.sex", target = "userSex") + @Mapping(source = "userInfoOld.age", target = "userAge") + @Mapping(source = "userInfoOld.address", target = "userAddress") + void voidConvert(@MappingTarget UserInfoNew userInfoNew, UserInfoOld userInfoOld, String userName); + + @Mapping(source="userInfoOld", target = ".") + UserInfoOld toConvert(UserInfoNew userInfoNew); + + /** + * 旧的实体类转换为新的实体类 + * + * @param userInfoOld 用户信息旧 + * @param userName 用户名 + * @return {@link UserInfoNew } + */ + @Mapping(source = "userName", target = "userName") + @Mapping(source = "userInfoOld.sex", target = "userSex") + @Mapping(source = "userInfoOld.age", target = "userAge") + @Mapping(source = "userInfoOld.address", target = "userAddress") + UserInfoNew convert(UserInfoOld userInfoOld, String userName); + + @BeanMapping(ignoreByDefault = true) + @Mapping(source = "name", target = "userName") + @Mapping(source = "sex", target = "userSex") + @Mapping(source = "age", target = "userAge") + @Mapping(source = "address", target = "userAddress") + UserInfoNew mapToBean(Map map); + + /** + * 自定义默认方法 + * + * @param address 地址 + * @return {@link String } + */ + @Named("getAddress") + default String getAddress(String address) { + return address; + } + } diff --git a/src/main/java/com/fjg/mapstructdemo/dto/UserInfoNew.java b/src/main/java/com/fjg/mapstructdemo/dto/UserInfoNew.java index 55361167566a2237717af6a4c47c3e59cfb90b6f..4b4fbf180e3b25a065a1f30eb8adf213174b9320 100644 --- a/src/main/java/com/fjg/mapstructdemo/dto/UserInfoNew.java +++ b/src/main/java/com/fjg/mapstructdemo/dto/UserInfoNew.java @@ -1,11 +1,14 @@ package com.fjg.mapstructdemo.dto; +import com.fjg.mapstructdemo.enums.NameEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; /** + * 用户信息新 + * * @author fengjianguo * @date 2023/12/29 13:37 */ @@ -25,4 +28,8 @@ public class UserInfoNew { private String id; + private UserInfoOld userInfoOld; + + private NameEnum nameEnum; + } diff --git a/src/main/java/com/fjg/mapstructdemo/dto/UserInfoOld.java b/src/main/java/com/fjg/mapstructdemo/dto/UserInfoOld.java index eb41ba0e1caca78f9738923042f6b67d3da20850..29b528e1cd84c6802358d79244510a108feffe44 100644 --- a/src/main/java/com/fjg/mapstructdemo/dto/UserInfoOld.java +++ b/src/main/java/com/fjg/mapstructdemo/dto/UserInfoOld.java @@ -1,5 +1,6 @@ package com.fjg.mapstructdemo.dto; +import com.fjg.mapstructdemo.enums.NameEnum; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -22,4 +23,6 @@ public class UserInfoOld { private Integer age; private String address; + + private NameEnum nameEnum; } diff --git a/src/main/java/com/fjg/mapstructdemo/enums/AgeEnum.java b/src/main/java/com/fjg/mapstructdemo/enums/AgeEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..95d3dada1ebf3013795bcc103df7581bc8321a8b --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/enums/AgeEnum.java @@ -0,0 +1,25 @@ +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author fengjianguo + * @date 2024/8/30 14:58 + */ +@Getter +@AllArgsConstructor +public enum AgeEnum { + + /** + * 年龄 + */ + ZERO("0", "100"), + ONE("1", "18"), + TWO("2", "40"), + THREE("3", "81"); + + private final String code; + + private final String desc; +} diff --git a/src/main/java/com/fjg/mapstructdemo/enums/BaseEnum.java b/src/main/java/com/fjg/mapstructdemo/enums/BaseEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..e30d63cab48dd1999704f695b0b43ebfc2374439 --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/enums/BaseEnum.java @@ -0,0 +1,24 @@ +package com.fjg.mapstructdemo.enums; + +import lombok.Getter; + +/** + * @author fengjianguo + * @date 2024/9/23 10:39 + */ +public interface BaseEnum { + /** + * 获取代码 + * + * @return {@link String } + */ + String getCode(); + + /** + * 得到desc + * + * @return {@link String } + */ + String getDesc(); + +} diff --git a/src/main/java/com/fjg/mapstructdemo/enums/CustomEnum.java b/src/main/java/com/fjg/mapstructdemo/enums/CustomEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..2143764c03ce602a56ae9601e5de88adde641d4f --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/enums/CustomEnum.java @@ -0,0 +1,23 @@ +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author fengjianguo + * @date 2024/9/23 10:38 + */ +@Getter +@AllArgsConstructor +public enum CustomEnum implements BaseEnum { + + /** + * 自定义枚举 + */ + ONE("1", "张三"), + TWO("2", "李四"), + THREE("3", "王五"); + private final String code; + private final String desc; + +} diff --git a/src/main/java/com/fjg/mapstructdemo/enums/CustomStateEnum.java b/src/main/java/com/fjg/mapstructdemo/enums/CustomStateEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..1ad100b4d76ae751d9db19d9717d9a91a61c7c6a --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/enums/CustomStateEnum.java @@ -0,0 +1,23 @@ +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author fengjianguo + * @date 2024/9/23 10:38 + */ +@Getter +@AllArgsConstructor +public enum CustomStateEnum implements BaseEnum { + + /** + * 自定义枚举 + */ + ONE_STATE("1", "张三"), + TWO_STATE("2", "李四"), + THREE_STATE("3", "王五"); + private final String code; + private final String desc; + +} diff --git a/src/main/java/com/fjg/mapstructdemo/enums/NameEnum.java b/src/main/java/com/fjg/mapstructdemo/enums/NameEnum.java new file mode 100644 index 0000000000000000000000000000000000000000..a0c9548f70c317d58aff78f33e0e94e81d1167da --- /dev/null +++ b/src/main/java/com/fjg/mapstructdemo/enums/NameEnum.java @@ -0,0 +1,24 @@ +package com.fjg.mapstructdemo.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * @author fengjianguo + * @date 2024/8/30 14:56 + */ +@Getter +@AllArgsConstructor +public enum NameEnum { + /** + * 名字 + */ + ZERO("0", "未知"), + ONE("1", "张三"), + TWO("2", "李四"), + THREE("3", "王五"); + + private final String code; + + private final String desc; +}