background

Our story begins on a sunny afternoon!

Today, outsource han writes code in place ~ outsource han according to the following definition

  • **PO(Persistant Object):** persistent object, which can be thought of as a Java object that maps to a table in a database. The simplest PO is a record in a table in a database.
  • VO(View Object):** View object, used in the presentation layer, which is used to encapsulate all data for a given page (or component).
  • **BO(Business Object):** Business object, which encapsulates business logic as an object. This object can contain one or more other objects.
  • DTO, DO(omitted……)

Sort the beans one by one! For example, a table called CAR_tb has two classes, one called CarPo, with identical attributes and table fields. The other, called CarVo, is for Car display on the page! However, when outsourcing Han does CarPo to CarVo conversion, the code is written like this, the pseudo-code is as follows:

CarPo carPo = this.carDao.selectById(1L); CarVo carVo = new CarVo(); carVo.setId(carPo.getId()); carVo.setName(carPo.getName()); // omit a bunch of return carVo;Copy the code

* Voiceover :* Isn’t it nice to see this list of code? I took over a bunch of outsourced code, and that’s how it was written, a mountain of shit! A class of several thousand lines, half of which are in the set property.

By chance, A Xiong hit the water! Chicken thief a xiong took a look at the outsourcing Korea’s screen, see this series of outsourcing Korea code! Go up for an education, feel not elegant enough! Ah Hsiung felt that he should use beanutils.copyProperties to simplify writing, like this!

CarPo carPo = this.carDao.selectById(1L);
CarVo carVo = new CarVo();
BeanUtils.copyProperties(carPo, carVo);
return carVo;
Copy the code

However, Han looked at the code and said, “The Internet says reflection is slow. If you write this way, there is no performance problem.” “If you use Apache’s BeanUtil class, it does have a big performance problem,” xiong said. “Like Alibaba’s code scan plugin, you can’t use this class, as shown below!”

“However, if you use a class like Spring’s BeanUtils, you need to call it enough times before it becomes noticeable.” “Achung added.

“Wow, Xiong is great!” Outsourcing Han excited!

Look at the gay romance in this office. Side is mopping the cleaners —— sweep the floor smoke, he decided to no longer silent.

See sweeping smoke threw away the hands of the mop, proud of the said, “we do not consider performance. Look at it from the perspective of extension! BeanUtils still have a lot of problems!”

  • When copying objects, the field type is inconsistent, causing the assignment to fail. How do you solve this problem? Expand yourself?
  • When the object is copied, the field name is inconsistent. For example, the field name in CarPo is carName, and the field name in CarVo is name. Expand yourself?
  • What do you do if it’s a copy of a collection class, like a List to a List?

(omitted ten thousand words….)

“What is to be done?” Listen to the description of sweeping the floor smoke, the outsourcing Han doubt asked!

“Very simple, in fact, during the process of converting beans, the set logic is fixed, the only change is the transformation rule. Therefore, if we only need to write the conversion rules, the conversion code is automatically generated by the system according to the rules, it is much more convenient! Using the example above, CarPo is called carName, CarVo is called name, and the attribute names are inconsistent. Let’s just pass a note

@mapping (source = "carName", target = "name"),Copy the code

Specify the corresponding transformation rule. The system recognizes the annotation and generates code

carVo.setName(carPo.getCarName())
Copy the code

If the set code can be generated automatically by the system in such a way, there will be much more flexibility in the bean conversion logic, and there will be no performance issues!” “Added the sweeping smoke!

“What tool generates the set logic?” Outsourcing Han and A xiong asked together!

“The name of the tool is MapStruct!”

Ok, that’s the end of the story! Don’t need to ask the end, the end is only one, outsourcing Han and a xiong happy… (omitted 10,000 words)… So let’s start talking specifically about MapStruct!

MapStruct tutorial

Here is an introduction to the plugin from three aspects: usage, principle, and advantages. For detailed tutorials, please refer to the official documentation.

usage

Import poM files as follows

<dependency> <groupId>org.mapstruct</groupId> <! Mapstruct -jdk8</artifactId> <version> 1.2.0.final </version> </dependency> <dependency> <groupId>org.mapstruct</groupId> <artifactId>mapstruct-processor</artifactId> < version > 1.2.0. Final < / version > < / dependency >Copy the code

In preparing the two entity classes, I used the Lombok plug-in for demonstration purposes. Prepare two entity classes, one for CarPo

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarPo {
    private Integer id;
    private String brand;
}
Copy the code

Another one is CarVo

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarVo {
    private Integer id;
    private String brand;
}
Copy the code

Let’s have a conversion interface

@Mapper
public interface CarCovertBasic {
    CarCovertBasic INSTANCE = 
    Mappers.getMapper(CarCovertBasic.class);
    
    CarVo toConvertVo(CarPo source);
}
Copy the code

The test code is as follows:

CarPo = carpo.builder ().id(1).brand("BMW").build(); CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo); System.out.println(carVo);Copy the code

The output is as follows

CarVo(id=1, brand=BMW)
Copy the code

As you can see, carPo’s property values are copied to carVo. Of course, in this case, the functionality is pretty much the same as BeanUtils, no advantage! Well, let’s talk about it later. Let’s talk about the principle first!

The principle of

The MapStruct plugin recognizes our interface, generates an implementation class, and in the implementation class, implements set logic for us! For example, in the example above, an implementation class CarCovertBasicImpl is implemented for the CarCovertBasic interface. We can use the decompiler tool to see the source code as shown below

Now, let’s talk about advantages

advantage

(1) The attributes of the two types are inconsistent

One of the properties of CarPo is carName, while the corresponding property of CarVo is name!

We can add the corresponding relationship on the interface, as shown below

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

@Mapping(source = "carName", target = "name")
CarVo toConvertVo(CarPo source);
}
Copy the code

The test code is as follows

CarPo = carpo.builder ().id(1).brand("BMW").carname ("BMW").build(); CarVo carVo = CarCovertBasic.INSTANCE.toConvertVo(carPo); System.out.println(carVo);Copy the code

The output is as follows

CarVo(id=1, brand=BMW, name= BMW)Copy the code

You can see that carVo has recognized the carName attribute in carPo and assigned it successfully. The decompiled diagram is shown below

Voice-over: If you have more than one @mappings, you can use the @mappings annotation and nest more than one @mapping annotation.

(2) Set type conversion

What if we want to convert from List to List? Simple, add a method to the interface

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

    @Mapping(source = "carName", target = "name")
    CarVo toConvertVo(CarPo source);

    List<CarVo> toConvertVos(List<CarPo> source);
}
Copy the code

As shown in the code, we’ll just add a toConvertVos method, and when mapStruct generates the code, it’ll loop through the toConvertVo method for us, just to show you the decompiled code, so you can see it at a sight

(3) Inconsistent types

Add a createTime of type Date to CarPo and a createTime of type String to CarVo, and the code looks like this

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarPo {
    private Integer id;
    private String brand;
    private String carName;
    private Date createTime;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarVo {
    private Integer id;
    private String brand;
    private String name;
    private String createTime;
}

Copy the code

The interface could be written like this

@Mapper
public interface CarCovertBasic {
    CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);
    @Mappings({
        @Mapping(source = "carName", target = "name"),
        @Mapping(target = "createTime", expression = "java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")
    })
    CarVo toConvertVo(CarPo source);

    List<CarVo> toConvertVos(List<CarPo> source);
}
Copy the code

In this way, you can solve the type inconsistency problem in your code! When the set method is generated, the DateUtil class is automatically called for conversion. Since it is relatively simple, I will not post the decomcompiled graph!

(4) to another

In a real business situation, we sometimes map two beans to one Bean. Suppose we have another class called AtrributePo, and we want to map both CarPo and AttributePo to CarBo

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AttributePo {
    private double price;
    private String color;
}

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class CarBo {
    private Integer id;
    private String brand;
    private String carName;
    private Date createTime;
    private double price;
    private String color;
}

Copy the code

The interface changes as follows

@Mapper
public interface CarCovertBasic {
    CarCovertBasic INSTANCE = Mappers.getMapper(CarCovertBasic.class);
    @Mappings({
        @Mapping(source = "carName", target = "name"),
        @Mapping(target = "createTime", expression = "java(com.guduyan.util.DateUtil.dateToStr(source.getCreateTime()))")
    })
    CarVo toConvertVo(CarPo source);

    List<CarVo> toConvertVos(List<CarPo> source);

    CarBo toConvertBo(CarPo source1, AttributePo source2);
}
Copy the code

Add the interface directly, and the plugin will automatically assemble the code as it generates it, as shown in the decompiled code below.

(5) other

There are many other advanced features of MapStruct that I won’t go through. We can refer to the following documents, in use of their own browsing can! Address: document mapstruct.org/documentati…

conclusion

This article introduces how to elegantly transform beans in a project. I hope you have learned something! If you want to hear other stories about Xiong, be sure to check out “Smoke Alone!”