当前位置: 首页 > 工具软件 > Orika > 使用案例 >

对象映射框架MapStruct与orika的简单使用

白昊乾
2023-12-01

对象映射框架MapStruct与orika

1.介绍

1.1 MapStruct

MapStruct 是一个 Java注释处理器,用于生成类型安全的 bean 映射类。

您所要做的就是定义一个映射器接口,该接口声明任何所需的映射方法。在编译期间,MapStruct 将生成该接口的实现。这个实现使用普通的Java 方法调用来映射源对象和目标对象,即没有反射或类似的。

与手工编写映射代码相比,MapStruct 通过生成繁琐且容易出错的代码来节省时间。遵循配置方法的约定,MapStruct 使用合理的默认值,但在配置或实现特殊行为时会采取措施。

与动态映射框架相比,MapStruct 具有以下优点:

  • 通过使用普通方法调用而不是反射来快速执行

  • 编译时类型安全:只能映射相互映射的对象和属性,不能将订单实体意外映射到客户 DTO 等。

  • 在构建时清除错误报告,如果

    • 映射不完整(并非所有目标属性都已映射)
    • 映射不正确(找不到合适的映射方法或类型转换)

    MapStruct 注解的关键词
    @Mapper 只有在接口加上这个注解, MapStruct 才会去实现该接口
    @Mapper 里有个 componentModel 属性,主要是指定实现类的类型,一般用到两个
    default:默认,可以通过 Mappers.getMapper(Class) 方式获取实例对象
    spring:在接口的实现类上自动添加注解 @Component,可通过 @Autowired 方式注入
    @Mapping:属性映射,若源对象属性与目标对象名字一致,会自动映射对应属性
    source:源属性
    target:目标属性
    dateFormat:String 到 Date 日期之间相互转换,通过 SimpleDateFormat,该值为 SimpleDateFormat 的日期格式
    ignore: 忽略这个字段
    @Mappings:配置多个@Mapping
    @MappingTarget 用于更新已有对象
    @InheritConfiguration 用于继承配置

1.2Orika

Orika是java Bean映射框架,可以实现从一个对象递归拷贝数据至另一个对象。在开发多层应用程序中非常有用。在这些层之间交换数据时,通常为了适应不同API需要转换一个实例至另一个实例。

有很多方法可以实现:硬代码拷贝或Dozer实现bean映射等。总之,需要简化不同层对象之间映射过程。

Orika使用字节码生成器创建开销最小的快速映射,比其他基于反射方式实现(如,Dozer)更快。之前使用Bean Copy 性能非常慢,发现在这个领域业界还是有很多新秀的。 Orika 应该就算一个比较好的吧。

工具实现方式缺点说明
mapstructgetter/setter方法需要了解注解和配置项语法JSR269注解处理器在编译期自动生成Java Bean转换代码,支持可配置化,扩展性强
orika动态生成字节码首次调用耗时较久,性能适中采用javassist类库生成Bean映射的字节码,之后直接加载执行生成的字节码文件

2.MapStruct简单使用

官方文档: https://mapstruct.org/documentation/stable/reference/html/

案例教程:https://blog.csdn.net/Z143430039/article/details/111054479 https://blog.csdn.net/zhige_me/article/details/80699784


    <properties>
        <mapstruct.version>1.4.1.Final </mapstruct.version>
        <lombok.version>1.18.12</lombok.version>
    </properties>

    <dependencies>
          <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${mapstruct.version}</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        
<!--     commons工具-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
<!--     测试框架-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
    </dependencies>

<!--     mapstruct和lombok整合配置-->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                    <encoding>UTF-8</encoding>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>
@NoArgsConstructor
@AllArgsConstructor
@Data
public class Person {
    private Long id;
    private String name;
    private String email;
    private Date birthday;
    private User user;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
    private Integer age;
}

@NoArgsConstructor
@AllArgsConstructor
@Data
public class PersonDTO {
    private Long id;
    private String name;
    /**
     * 对应 Person.user.age
     */
    private Integer age;
    private String email;
    /**
     * 与 DO 里面的字段名称(birthDay)不一致
     */
    private Date birth;
    /**
     * 对 DO 里面的字段(birthDay)进行拓展,dateFormat 的形式
     */
    private String birthDateFormat;
    /**
     * 对 DO 里面的字段(birthDay)进行拓展,expression 的形式
     */
    private String birthExpressionFormat;
}


@Mapper
public interface PersonConverter {

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

    @Mappings({
            @Mapping(source = "birthday", target = "birth"),
            @Mapping(source = "birthday", target = "birthDateFormat", dateFormat = "yyyy-MM-dd HH:mm:ss"),
            @Mapping(target = "birthExpressionFormat", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),\"yyyy-MM-dd HH:mm:ss\"))"),
            @Mapping(source = "user.age", target = "age"),
            @Mapping(target = "email", ignore = true)
    })
    PersonDTO domain2dto(Person person);

    List<PersonDTO> domain2dto(List<Person> people);

    /**
     *自定义实现(如果你想手写)
     */
    default LocalDate map(LocalDateTime time) {
        return time.toLocalDate();
    }
}
@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-04-07T15:07:34+0800",
    comments = "version: 1.4.1.Final, compiler: javac, environment: Java 1.8.0_40 (Oracle Corporation)"
)
public class PersonConverterImpl implements PersonConverter {

    @Override
    public PersonDTO domain2dto(Person person) {
        if ( person == null ) {
            return null;
        }

        PersonDTO personDTO = new PersonDTO();

        personDTO.setBirth( person.getBirthday() );
        if ( person.getBirthday() != null ) {
            personDTO.setBirthDateFormat( new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( person.getBirthday() ) );
        }
        personDTO.setAge( personUserAge( person ) );
        personDTO.setId( person.getId() );
        personDTO.setName( person.getName() );

        personDTO.setBirthExpressionFormat( org.apache.commons.lang3.time.DateFormatUtils.format(person.getBirthday(),"yyyy-MM-dd HH:mm:ss") );

        return personDTO;
    }

    @Override
    public List<PersonDTO> domain2dto(List<Person> people) {
        if ( people == null ) {
            return null;
        }

        List<PersonDTO> list = new ArrayList<PersonDTO>( people.size() );
        for ( Person person : people ) {
            list.add( domain2dto( person ) );
        }

        return list;
    }

    private Integer personUserAge(Person person) {
        if ( person == null ) {
            return null;
        }
        User user = person.getUser();
        if ( user == null ) {
            return null;
        }
        Integer age = user.getAge();
        if ( age == null ) {
            return null;
        }
        return age;
    }
}

3.Orika简单使用

官方文档: http://orika-mapper.github.io/orika-docs/mappings-via-classmapbuilder.html

//处理不同属性的映射
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
mapperFactory.classMap(User.class, UserVo.class)
.field("name", "userName")
.field("age", "ageOne")
.byDefault().register();
UserVo userVo = mapperFactory.getMapperFacade().map(User, UserVo.class);
		<dependency>
            <groupId>ma.glasnost.orika</groupId>
            <artifactId>orika-core</artifactId>
            <version>1.4.6</version>
        </dependency>

public enum MapperUtils {

    /**
     * 实例
     */
    INSTANCE;

    /**
     * 默认字段工厂
     */
    private static final MapperFactory MAPPER_FACTORY = new DefaultMapperFactory.Builder().build();

    /**
     * 默认字段实例
     */
    private static final MapperFacade MAPPER_FACADE = MAPPER_FACTORY.getMapperFacade();

    /**
     * 默认字段实例集合
     */
    private static Map<String, MapperFacade> CACHE_MAPPER_FACADE_MAP = new ConcurrentHashMap<>();

    /**
     * 映射实体(默认字段)
     *
     * @param toClass 映射类对象
     * @param data    数据(对象)
     * @return 映射类对象
     */
    public <E, T> E map(Class<E> toClass, T data) {
        return MAPPER_FACADE.map(data, toClass);
    }

    /**
     * 映射实体(自定义配置)
     *
     * @param toClass   映射类对象
     * @param data      数据(对象)
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    public <E, T> E map(Class<E> toClass, T data, Map<String, String> configMap) {
        MapperFacade mapperFacade = this.getMapperFacade(toClass, data.getClass(), configMap);
        return mapperFacade.map(data, toClass);
    }

    /**
     * 映射集合(默认字段)
     *
     * @param toClass 映射类对象
     * @param data    数据(集合)
     * @return 映射类对象
     */
    public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data) {
        return MAPPER_FACADE.mapAsList(data, toClass);
    }


    /**
     * 映射集合(自定义配置)
     *
     * @param toClass   映射类
     * @param data      数据(集合)
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    public <E, T> List<E> mapAsList(Class<E> toClass, Collection<T> data, Map<String, String> configMap) {
        T t = data.stream().findFirst().orElseThrow(() -> new ExceptionInInitializerError("映射集合,数据集合为空"));
        MapperFacade mapperFacade = this.getMapperFacade(toClass, t.getClass(), configMap);
        return mapperFacade.mapAsList(data, toClass);
    }

    /**
     * 获取自定义映射
     *
     * @param toClass   映射类
     * @param dataClass 数据映射类
     * @param configMap 自定义配置
     * @return 映射类对象
     */
    private <E, T> MapperFacade getMapperFacade(Class<E> toClass, Class<T> dataClass, Map<String, String> configMap) {
        String mapKey = dataClass.getCanonicalName() + "_" + toClass.getCanonicalName();
        MapperFacade mapperFacade = CACHE_MAPPER_FACADE_MAP.get(mapKey);
        if (Objects.isNull(mapperFacade)) {
            MapperFactory factory = new DefaultMapperFactory.Builder().build();
            ClassMapBuilder classMapBuilder = factory.classMap(dataClass, toClass);
            configMap.forEach(classMapBuilder::field);
            classMapBuilder.byDefault().register();
            mapperFacade = factory.getMapperFacade();
            CACHE_MAPPER_FACADE_MAP.put(mapKey, mapperFacade);
        }
        return mapperFacade;
    }
}

    @Test
    public void test1() {
        Person person = new Person(1L,"zhige","zhige.me@gmail.com",new Date(),new User(1));
        //默认映射
        PersonDTO userDTO = MapperUtils.INSTANCE.map(PersonDTO.class, person);;

        //自定义映射
        Map<String, String> config = new HashMap<>();
        // 自定义配置(birthday 转 birth)
        config.put("birthday", "birth");
        config.put("user.age", "age");
        PersonDTO userDomain = MapperUtils.INSTANCE.map(PersonDTO.class, person, config);
        System.out.println(userDomain);

//默认        PersonDTO(id=1, name=zhige, age=null, email=zhige.me@gmail.com, birth=null, birthDateFormat=null, birthExpressionFormat=null)
//加confi     PersonDTO(id=1, name=zhige, age=1, email=zhige.me@gmail.com, birth=Thu Apr 07 15:53:39 CST 2022, birthDateFormat=null, birthExpressionFormat=null)


        Person person2 = new Person(11L,"zhansan","zhansan.me@gmail.com",new Date(),new User(21));
        Person person3 = new Person(12L,"lisi","lisi.me@gmail.com",new Date(),new User(31));
        List<Person> userDOList =new ArrayList<>();
        userDOList.add(person);
        userDOList.add(person2);
        userDOList.add(person3);

        List<PersonDTO> userDTOS = MapperUtils.INSTANCE.mapAsList(PersonDTO.class,userDOList);

        List<PersonDTO> userDTOS1 = MapperUtils.INSTANCE.mapAsList(PersonDTO.class,userDOList,config);

    }
 类似资料: