PO VO DTO 转换神器替代BeanUtils 了(一)

简介: PO VO DTO 转换神器替代BeanUtils 了

在我们日常开发的程序中,为了各层之间解耦,一般会定义不同的对象用来在不同层之间传递数据,比如xxxDTO、xxxVO、xxxQO,当在不同层之间传输数据时,不可避免地经常需要将这些对象进行相互转换。

今天给大家介绍一个对象转换工具MapStruct,代码简洁安全、性能高,强烈推荐。

1. MapStruct简介

MapStruct是一个代码生成器,它基于约定优于配置,极大地简化了Java Bean类型之间映射的实现。特点如下:

1.基于注解

2.在编译期自动生成映射转换代码

3.类型安全、高性能、无依赖性、易于理解阅读

2.0 MapStruct入门

2.0.1 简易demo

Car.java

public class Car {
    private String make;
    private int numberOfSeats;
    private CarType type;
    //constructor, getters, setters etc.
}

CarDto .java

public class CarDto {
    private String make;
    private int seatCount;
    private String type;
    //constructor, getters, setters etc.
}
@Mapper (1:下面对此解释)
public interface CarMapper {
    CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); (3:下面对此解释)
    @Mapping(source = "numberOfSeats", target = "seatCount")
    CarDto carToCarDto(Car car); (2:下面对此解释)
}

注释@Mapper(1)将接口标记为映射接口,并允许 MapStruct 处理器在编译期间启动。

实际的映射方法2期望源对象作为参数并返回目标对象。它的名字可以自由选择。

对于源对象和目标对象中具有不同名称的属性,可以使用注释来配置名称。@Mapping

在需要和可能的情况下,将为源和目标中具有不同类型的属性执行类型转换,例如,属性将从枚举类型转换为字符串。type

当然,一个接口中可以有多个映射方法,所有这些方法的实现都将由MapStruct生成。

可以从类中检索接口实现的实例。按照惯例,接口声明一个成员MappersINSTANCE 3,为客户端提供对映射器实现的访问。

shouldMapCarToDto.java

@Test
public void shouldMapCarToDto() {
    //given
    Car car = new Car( "Morris", 5, CarType.SEDAN );
    //when
    CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
    //then
    assertThat( carDto ).isNotNull();
    assertThat( carDto.getMake() ).isEqualTo( "Morris" );
    assertThat( carDto.getSeatCount() ).isEqualTo( 5 );
    assertThat( carDto.getType() ).isEqualTo( "SEDAN" );
}

如想了解更多请查看官网内容(https://mapstruct.org/

2.1. 引入依赖

这里使用Gradle构建

dependencies {
    implementation 'org.mapstruct:mapstruct:1.4.2.Final'
    annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'
}

maven仓库地址;

// https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor
implementation group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'

maven地址:

<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor -->
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <version>1.4.2.Final</version>
</dependency>

2.2. 需要转换的对象

创建两个示例对象(EG: 将Demo对象转换为DemoDto对象)

保证对象之间的值是相同的;

/**
 * 源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private Integer id;
    private String name;
}

2.3. 创建转换器

只需要创建一个转换器接口类,并在类上添加 @Mapper 注解即可(官方示例推荐以 xxxMapper 格式命名转换器名称)

@Mapper
public interface DemoMapper {
    //使用Mappers工厂获取DemoMapper实现类
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    //定义接口方法,参数为来源对象,返回值为目标对象
    DemoDto toDemoDto(Demo demo);
}

2.4. 验证

public static void main(String[] args) {
    Demo demo = new Demo();
    demo.setId(111);
    demo.setName("hello");
    DemoDto demoDto = DemoMapper.INSTANCE.toDemoDto(demo);
    System.out.println("目标对象demoDto为:" + demoDto);
    //输出结果:目标对象demoDto为:DemoDto(id=111, name=hello)
}

测试结果如下:

目标对象demoDto为:DemoDto(id=111, name=hello)

达到了我们的预期结果。

2.5. 自动生成的实现类

为什么声明一个接口就可以转换对象呢?

我们看一下MapStruct在编译期间自动生成的实现类:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2022-09-01T17:54:38+0800",
    comments = "version: 1.4.2.Final, compiler: IncrementalProcessingEnvironment from gradle-language-java-7.3.jar, environment: Java 1.8.0_231 (Oracle Corporation)"
)
public class DemoMapperImpl implements DemoMapper {
    @Override
    public DemoDto toDemoDto(Demo demo) {
        if ( demo == null ) {
            return null;
        }
        DemoDto demoDto = new DemoDto();
        demoDto.setId( demo.getId() );
        demoDto.setName( demo.getName() );
        return demoDto;
    }
}

可以看到,MapStruct帮我们将繁杂的代码自动生成了,而且实现类中用的都是最基本的get、set方法,易于阅读理解,转换速度非常快。

3.0 MapStruct进阶

上面的例子只是小试牛刀,下面开始展示MapStruct的强大之处。

(限于篇幅,这里不展示自动生成的实现类和验证结果,大家可自行测试)

场景1:属性名称不同、(基本)类型不同

  • 属性名称不同: 在方法上加上 @Mapping 注解,用来映射属性
  • 属性基本类型不同: 基本类型和String等类型会自动转换

关键字:@Mapping注解

/**
 * 来源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private String id;
    private String fullname;
}
/**
 * 转换器
 */
@Mapper
public interface DemoMapper {
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    @Mapping(target = "fullname", source = "name")
    DemoDto toDemoDto(Demo demo);
}

场景2:统一映射不同类型

下面例子中,time1、time2、time3都会被转换,具体说明看下面的注释:

/**
 * 来源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
    /**
     * time1、time2名称相同,time3转为time33
     * 这里的time1、time2、time33都是Date类型
     */
    private Date time1;
    private Date time2;
    private Date time3;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private String id;
    private String name;
    /**
     * 这里的time1、time2、time33都是String类型
     */
    private String time1;
    private String time2;
    private String time33;
}
/**
 * 转换器
 */
@Mapper
public interface DemoMapper {
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    @Mapping(target = "time33", source = "time3")
    DemoDto toDemoDto(Demo demo);
    //MapStruct会将所有匹配到的:
    //源类型为Date、目标类型为String的属性,
    //按以下方法进行转换
    static String date2String(Date date) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String strDate = simpleDateFormat.format(date);
        return strDate;
    }
}

场景3:固定值、忽略某个属性、时间转字符串格式

一个例子演示三种用法,具体说明看注释,很容易理解:

关键字:ignore、constant、dateFormat

/**
 * 来源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
    private Date time;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private String id;
    private String name;
    private String time;
}
/**
 * 转换器
 */
@Mapper
public interface DemoMapper {
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    //id属性不赋值
    @Mapping(target = "id", ignore = true)
    //name属性固定赋值为“hello”
    @Mapping(target = "name", constant = "hello")
    //time属性转为yyyy-MM-dd HH:mm:ss格式的字符串
    @Mapping(target = "time", dateFormat = "yyyy-MM-dd HH:mm:ss")
    DemoDto toDemoDto(Demo demo);
}

场景4:为某个属性指定转换方法

场景2中,我们是按照某个转换方法,统一将一种类型转换为另外一种类型;而下面这个例子,是为某个属性指定方法:

关键字:@Named注解、qualifiedByName

/**
 * 来源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private String id;
    private String name;
}
/**
 * 转换器
 */
@Mapper
public interface DemoMapper {
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    //为name属性指定@Named为convertName的方法进行转换
    @Mapping(target = "name", qualifiedByName = "convertName")
    DemoDto toDemoDto(Demo demo);
    @Named("convertName")
    static String aaa(String name) {
        return "姓名为:" + name;
    }
}

场景5:多个参数合并为一个对象

如果参数为多个的话,@Mapping注解中的source就要指定是哪个参数了,用点分隔:

关键字:点(.)

/**
 * 来源对象
 */
@Data
public class Demo {
    private Integer id;
    private String name;
}
/**
 * 目标对象
 */
@Data
public class DemoDto {
    private String fullname;
    private String timestamp;
}
/**
 * 转换器
 */
@Mapper
public interface DemoMapper {
    DemoMapper INSTANCE = Mappers.getMapper(DemoMapper.class);
    //fullname属性赋值demo对象的name属性(注意这里.的用法)
    //timestamp属性赋值为传入的time参数
    @Mapping(target = "fullname", source = "demo.name")
    @Mapping(target = "timestamp", source = "time")
    DemoDto toDemoDto(Demo demo, String time);
}


相关文章
|
1月前
|
存储 前端开发 Java
Java:PO、VO、BO、DO、DAO、DTO、POJO
Java:PO、VO、BO、DO、DAO、DTO、POJO
85 0
|
前端开发 数据库 微服务
JavaWeb - 我们的开发规范(VO、DTO、BO、PO、DO、POJO)
JavaWeb - 我们的开发规范(VO、DTO、BO、PO、DO、POJO)
1426 0
JavaWeb - 我们的开发规范(VO、DTO、BO、PO、DO、POJO)
|
1月前
|
设计模式 开发框架 前端开发
VO、PO、DTO的区别
VO、PO、DTO的区别
32 3
|
Java 数据库
POJO、PO、DTO、DAO、BO、VO需要搞清楚的概念
POJO 全称为:Plain Ordinary Java Object,即简单普通的java对象。一般用在数据层映射到数据库表的类,类的属性与表字段一一对应。 PO 全称为:Persistant Object,即持久化对象。
30860 1
|
7月前
|
Java 数据库
详述 PO VO BO DTO DAO 和 POJO 的概念及区别
详述 PO VO BO DTO DAO 和 POJO 的概念及区别
139 0
|
10月前
|
设计模式 前端开发 Java
Java开发中PO、VO、DAO、BO、DTO、POJO 含义
可以看成是与数据库中的表相映射的java对象。使用 Mybatis 来生成 PO 是不错的选择。
223 0
|
11月前
|
Java Spring
PO VO DTO 转换神器替代BeanUtils 了(三)
PO VO DTO 转换神器替代BeanUtils 了
137 0
|
11月前
|
安全 Java Maven
PO VO DTO 转换神器替代BeanUtils 了(二)
PO VO DTO 转换神器替代BeanUtils 了
72 0
|
设计模式 存储 开发框架
Java各种对象(PO,BO,VO,DTO,POJO,DAO,Entity,JavaBean,JavaBeans)的区分
Java各种对象(PO,BO,VO,DTO,POJO,DAO,Entity,JavaBean,JavaBeans)的区分
456 0
|
消息中间件 JavaScript 小程序
别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?