Stop writing full screen get & set, too Low! Try MapStruct advanced gameplay!

Next, if you don't know what MapStruct is, I suggest you take a look at what the stack leader shared before< Kill BeanUtils! Try this Bean automatic mapping tool. It's really powerful!! >You know.

The first part introduces the basic concept of mapstructure and the mapping practice of single object and object list. After reading the first part, the stack leader has some messages. Of course, radishes and cabbage have their own love. They can be used if they like or not. There is no need to argue. Whether the tool is good or not is not necessarily suitable for everyone. Just be happy.

Here are some advanced mapping methods. Don't write a full screen get set. It's too Low! MapStruct advanced gameplay, this stack leader will take you to the right path!

1. Custom mapping

When mapping DTO, if the mapping configuration of MapStruct for some parameter values cannot meet the requirements, you can use a custom method.

Two new DTO classes are added:

The UserCustomDTO class contains the UserExtDTO object.

/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@Data
public class UserCustomDTO {

    private String name;

    private int sex;

    private boolean married;

    private String birthday;

    private String regDate;

    private UserExtDTO userExtDTO;

    private String memo;


}
/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@Data
public class UserExtDTO {

    private String regSource;

    private String favorite;

    private String school;

    private int kids;

    private String memo;

}

Custom mapping:

If the UserExtDTO object does not want to use the default mapping, you can add a custom mapping method for this parameter.

/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@Mapper(componentModel = "spring")
public interface UserCustomStruct {

    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userExtDO", target = "userExtDTO")
    @Mapping(target = "memo", ignore = true)
    UserCustomDTO toUserCustomDTO(UserDO userDO);

    default UserExtDTO toUserExtDTO(UserExtDO userExtDO) {
        UserExtDTO userExtDTO = new UserExtDTO();
        userExtDTO.setKids(userExtDO.getKids());
        userExtDTO.setFavorite(userExtDO.getFavorite());

        // Override these two values
        userExtDTO.setRegSource("Default source");
        userExtDTO.setSchool("Default school");

        return userExtDTO;
    }

}

When the UserExtDTO object is mapped, the custom toUserExtDTO method in the interface will be called automatically to complete the custom mapping.

Let's look at the generated implementation class source code:

@Component
public class UserCustomStructImpl implements UserCustomStruct {
    public UserCustomStructImpl() {
    }

    public UserCustomDTO toUserCustomDTO(UserDO userDO) {
        if (userDO == null) {
            return null;
        } else {
            UserCustomDTO userCustomDTO = new UserCustomDTO();
            if (userDO.getBirthday() != null) {
                userCustomDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
            }

            userCustomDTO.setUserExtDTO(this.toUserExtDTO(userDO.getUserExtDO()));
            userCustomDTO.setName(userDO.getName());
            userCustomDTO.setSex(userDO.getSex());
            userCustomDTO.setMarried(userDO.isMarried());
            userCustomDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));
            return userCustomDTO;
        }
    }
}

Yes, the setUserExtDTO method calls this.toUserExtDTO custom method mapping.

Spring Boot basics will not be introduced in this article. A series of basic tutorials and sample source codes can be seen here: https://github.com/javastacks/spring-boot-best-practice

Test:

/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCustomStructTest {

    @Autowired
    private UserCustomStruct userCustomStruct;

    @Test
    public void test1() {
        UserExtDO userExtDO = new UserExtDO();
        userExtDO.setRegSource("Official account: Java Technology stack");
        userExtDO.setFavorite("Write code");
        userExtDO.setSchool("Social University");
        userExtDO.setKids(1);

        UserDO userDO = new UserDO();
        userDO.setName("Stack length customization method");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");
        userDO.setUserExtDO(userExtDO);

        UserCustomDTO userCustomDTO = userCustomStruct.toUserCustomDTO(userDO);
        System.out.println("=====Custom method=====");
        System.out.println(userCustomDTO);
    }
}

Output results:

You can see the two values overwritten by the custom method, and the result is verified successfully.

2. Multi parameter mapping

In the mapping method described earlier, there is only one parameter. If there are multiple parameters mapped into a DTO, what should I do?

For example, there are two separate DO S. UserDO and UserAddressDO are mapped to UserMultiDTO.

Direct code:

/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@Mapper(componentModel = "spring")
public interface UserMultiStruct {

    @Mapping(source = "userDO.birthday", target = "birthday", dateFormat = "yyyy-MM-dd")
    @Mapping(target = "userDO.regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(user.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")
    @Mapping(source = "userAddressDO.postcode", target = "postcode")
    @Mapping(source = "userAddressDO.address", target = "address")
    @Mapping(target = "memo", ignore = true)
    UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO);

}

Directly use the specified object name. Attribute name to map.

Let's look at the generated implementation class source code:

@Component
public class UserMultiStructImpl implements UserMultiStruct {
    public UserMultiStructImpl() {
    }

    public UserMultiDTO toUserMultiDTO(UserDO userDO, UserAddressDO userAddressDO) {
        if (userDO == null && userAddressDO == null) {
            return null;
        } else {
            UserMultiDTO userMultiDTO = new UserMultiDTO();
            if (userDO != null) {
                if (userDO.getBirthday() != null) {
                    userMultiDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));
                }

                userMultiDTO.setName(userDO.getName());
                userMultiDTO.setSex(userDO.getSex());
                userMultiDTO.setMarried(userDO.isMarried());
                if (userDO.getRegDate() != null) {
                    userMultiDTO.setRegDate((new SimpleDateFormat()).format(userDO.getRegDate()));
                }
            }

            if (userAddressDO != null) {
                userMultiDTO.setPostcode(userAddressDO.getPostcode());
                userMultiDTO.setAddress(userAddressDO.getAddress());
            }

            return userMultiDTO;
        }
    }
}

Test:

/**
 * WeChat official account: Java technology stack
 * @author Stack length
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMultiStructTest {

    @Autowired
    private UserMultiStruct userMultiStruct;

    @Test
    public void test1() {
        UserDO userDO = new UserDO();
        userDO.setName("Multi parameter mapping");
        userDO.setSex(1);
        userDO.setAge(18);
        userDO.setBirthday(new Date());
        userDO.setPhone("18888888888");
        userDO.setMarried(true);
        userDO.setRegDate(new Date());
        userDO.setMemo("666");

        UserAddressDO userAddressDO = new UserAddressDO();
        userAddressDO.setProvince("Guangdong Province");
        userAddressDO.setCity("Shenzhen City");
        userAddressDO.setPostcode("666666");
        userAddressDO.setAddress("001 Avenue Java Technical stack official account");
        userAddressDO.setMemo("Address information");

        UserMultiDTO userMultiDTO = userMultiStruct.toUserMultiDTO(userDO, userAddressDO);
        System.out.println("=====Multi parameter mapping=====");
        System.out.println(userMultiDTO);
    }
}

Output results:

The personal information and address information are output, and the result is verified successfully.

The full version of the actual source code of this article has been uploaded:

https://github.com/javastacks/spring-boot-best-practice

3. Nested mapping

If the values in a DTO are mapped from multiple nested objects in an object, if you don't want to write mapping one by one, the target can be represented by.

Direct code:

/** * WeChat official account: Java technology stack * @author stack length */@Mapper(componentModel = "spring")public interface UserNestedStruct {    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userNestedDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")    @Mapping(source = "userAddressDO", target = ".")    @Mapping(source = "userExtDO", target = ".")    @Mapping(source = "userExtDO.memo", target = "memo")    UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO);}

If there are mapping conflicts with duplicate names in nested objects, you can manually specify which nested object to source from.

Let's look at the generated implementation class source code:

@Componentpublic class UserNestedStructImpl implements UserNestedStruct {    public UserNestedStructImpl() {    }    public UserNestedDTO toUserNestedDTO(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserNestedDTO userNestedDTO = new UserNestedDTO();            if (userNestedDO.getBirthday() != null) {                userNestedDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userNestedDO.getBirthday()));            }            userNestedDTO.setMemo(this.userNestedDOUserExtDOMemo(userNestedDO));            userNestedDTO.setCity(this.userNestedDOUserAddressDOCity(userNestedDO));            userNestedDTO.setAddress(this.userNestedDOUserAddressDOAddress(userNestedDO));            userNestedDTO.setRegSource(this.userNestedDOUserExtDORegSource(userNestedDO));            userNestedDTO.setFavorite(this.userNestedDOUserExtDOFavorite(userNestedDO));            userNestedDTO.setSchool(this.userNestedDOUserExtDOSchool(userNestedDO));            userNestedDTO.setName(userNestedDO.getName());            userNestedDTO.setSex(userNestedDO.getSex());            userNestedDTO.setMarried(userNestedDO.isMarried());            userNestedDTO.setRegDate(DateFormatUtils.format(userNestedDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));            return userNestedDTO;        }    }    private String userNestedDOUserExtDOMemo(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String memo = userExtDO.getMemo();                return memo == null ? null : memo;            }        }    }    private String userNestedDOUserAddressDOCity(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();            if (userAddressDO == null) {                return null;            } else {                String city = userAddressDO.getCity();                return city == null ? null : city;            }        }    }    private String userNestedDOUserAddressDOAddress(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserAddressDO userAddressDO = userNestedDO.getUserAddressDO();            if (userAddressDO == null) {                return null;            } else {                String address = userAddressDO.getAddress();                return address == null ? null : address;            }        }    }    private String userNestedDOUserExtDORegSource(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String regSource = userExtDO.getRegSource();                return regSource == null ? null : regSource;            }        }    }    private String userNestedDOUserExtDOFavorite(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String favorite = userExtDO.getFavorite();                return favorite == null ? null : favorite;            }        }    }    private String userNestedDOUserExtDOSchool(UserNestedDO userNestedDO) {        if (userNestedDO == null) {            return null;        } else {            UserExtDO userExtDO = userNestedDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String school = userExtDO.getSchool();                return school == null ? null : school;            }        }    }}

As can be seen from the source code, a new method will be added to judge the values from nested objects to avoid empty assignments.

Test:

/** * WeChat official account: Java technology stack * @author stack length */@RunWith(SpringRunner.class)@SpringBootTestpublic class UserNestedStructTest {    @Autowired    private UserNestedStruct userNestedStruct;    @Test    public void test1() {        UserExtDO userExtDO = new UserExtDO();        userExtDO.setRegSource("Official account: Java Technology stack");        userExtDO.setFavorite("Write code");        userExtDO.setSchool("Social University");        userExtDO.setKids(1);        userExtDO.setMemo("Extended information");        UserAddressDO userAddressDO = new UserAddressDO();        userAddressDO.setProvince("Guangdong Province");        userAddressDO.setCity("Shenzhen City");        userAddressDO.setPostcode("666666");        userAddressDO.setAddress("001 Avenue Java Technical stack official account");        userAddressDO.setMemo("Address information");        UserNestedDO userNestedDO = new UserNestedDO();        userNestedDO.setName("Stack length nested mapping");        userNestedDO.setSex(1);        userNestedDO.setAge(18);        userNestedDO.setBirthday(new Date());        userNestedDO.setPhone("18888888888");        userNestedDO.setMarried(true);        userNestedDO.setRegDate(new Date());        userNestedDO.setMemo("666");        userNestedDO.setUserExtDO(userExtDO);        userNestedDO.setUserAddressDO(userAddressDO);        UserNestedDTO userNestedDTO = userNestedStruct.toUserNestedDTO(userNestedDO);        System.out.println("=====Nested mapping=====");        System.out.println(userNestedDTO);    }}

Output results:

You can see the value of the nested object, and the memo is also from the specified nested object. The result is verified successfully.

4. Map existing instances

The above description is to map and generate a new DTO instance. If it is an existing DTO instance, how to map it?

Direct code:

/** * WeChat official account: Java technology stack * @author stack length */@Mapper(componentModel = "spring")public interface UserExistStruct {    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd")    @Mapping(target = "regDate", expression = "java(org.apache.commons.lang3.time.DateFormatUtils.format(userDO.getRegDate(),\"yyyy-MM-dd HH:mm:ss\"))")    @Mapping(source = "userExtDO.regSource", target = "registerSource")    @Mapping(source = "userExtDO.favorite", target = "favorite")    @Mapping(target = "name", ignore = true)    @Mapping(target = "memo", ignore = true)    void toUserShowDTO(@MappingTarget UserShowDTO userShowDTO, UserDO userDO);}

Add DTO object parameters on the method and decorate it with @ MappingTarget object. The parameter positions can be changed.

Let's look at the generated implementation class source code:

@Componentpublic class UserExistStructImpl implements UserExistStruct {    public UserExistStructImpl() {    }    public void toUserShowDTO(UserShowDTO userShowDTO, UserDO userDO) {        if (userDO != null) {            if (userDO.getBirthday() != null) {                userShowDTO.setBirthday((new SimpleDateFormat("yyyy-MM-dd")).format(userDO.getBirthday()));            } else {                userShowDTO.setBirthday((String)null);            }            userShowDTO.setRegisterSource(this.userDOUserExtDORegSource(userDO));            userShowDTO.setFavorite(this.userDOUserExtDOFavorite(userDO));            userShowDTO.setSex(userDO.getSex());            userShowDTO.setMarried(userDO.isMarried());            userShowDTO.setRegDate(DateFormatUtils.format(userDO.getRegDate(), "yyyy-MM-dd HH:mm:ss"));        }    }    private String userDOUserExtDORegSource(UserDO userDO) {        if (userDO == null) {            return null;        } else {            UserExtDO userExtDO = userDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String regSource = userExtDO.getRegSource();                return regSource == null ? null : regSource;            }        }    }    private String userDOUserExtDOFavorite(UserDO userDO) {        if (userDO == null) {            return null;        } else {            UserExtDO userExtDO = userDO.getUserExtDO();            if (userExtDO == null) {                return null;            } else {                String favorite = userExtDO.getFavorite();                return favorite == null ? null : favorite;            }        }    }}

userShowDTO is passed in as a method parameter, not newly created.

Test:

/** * WeChat official account: Java technology stack * @author stack length */@RunWith(SpringRunner.class)@SpringBootTestpublic class UserExistStructTest {    @Autowired    private UserExistStruct userExistStruct;    @Test    public void test1() {        UserExtDO userExtDO = new UserExtDO();        userExtDO.setRegSource("Official account: Java Technology stack");        userExtDO.setFavorite("Write code");        userExtDO.setSchool("Social University");        UserDO userDO = new UserDO();        userDO.setName("Stack length");        userDO.setSex(1);        userDO.setAge(18);        userDO.setBirthday(new Date());        userDO.setPhone("18888888888");        userDO.setMarried(true);        userDO.setRegDate(new Date());        userDO.setMemo("666");        userDO.setUserExtDO(userExtDO);        System.out.println("=====Before mapping an existing instance=====");        UserShowDTO userShowDTO = new UserShowDTO();        userShowDTO.setName("Stack length NAME");        userShowDTO.setMemo("Stack length MEMO");        System.out.println(userShowDTO);        System.out.println("=====After mapping an existing instance=====");        userExistStruct.toUserShowDTO(userShowDTO, userDO);        System.out.println(userShowDTO);    }}

Output results:

You can see the value of the existing DTO object and the value of the new mapping. The result is verified successfully.

Note: the default mapping is to overwrite the original value. If you want to keep the original XX value, use ignore to ignore it

summary

In this article, the stack leader introduced the four advanced playing methods of MapStruct, which are enough to deal with various Bean class mappings. In fact, there are many complex and personalized usages, which are difficult to write. The stack leader will sort them out later and share them with you one after another.

Interested parties can also refer to the official documents:

https://mapstruct.org/documentation/reference-guide/

In addition, the stack leader has always introduced the mapping of do -- > DTO. In fact, DTO -- > do and BO are the same, but the object names are different and the mapping usage is the same. In this way, when service A receives DTO data from service B, it can perform another reflection mapping for business use.

The full version of the actual source code of this article has been uploaded:

https://github.com/javastacks/spring-boot-best-practice

Welcome to Star. The following Spring Boot examples will be provided here!

Well, today's sharing is here. The stack will share more interesting Java technology and latest technology information. We will pay attention to the Java push for the official account. I will also organize the main Java interview questions and reference answers. I will reply to the key words in the official account.

Finally, I feel that if my article is useful to you, use your small hand to read and forward it. It is not easy to be original. The stack leader needs your encouragement.

Copyright notice: This article is the public number "Java technology stack" original, original is not easy, reprint, quote the content of this article please indicate the source, all the plagiarism official account + complaint, and retain the right to pursue its legal responsibility.

Recent hot article recommendations:

1.1000 + Java interview questions and answers (2021 latest version)

2.Stop playing if/ else on the full screen. Try the strategy mode. It's really fragrant!!

3.what the fuck! What is the new syntax of xx ≠ null in Java?

4.Spring Boot 2.5 heavy release, dark mode is too explosive!

5.Java development manual (Songshan version) is the latest release. Download it quickly!

Feel good, don't forget to like + forward!

Tags: Java

Posted on Thu, 14 Oct 2021 01:31:18 -0400 by Mistah Roth