Better do it once than say it ten times.

Background:

Recently, I began to take charge of a business module with a large amount of data, requiring all relevant data to be found out without pagination and to be organized into a tree structure. Data from DAO layer to Service is displayed to the front end by Entity object to Vo object. Copy then involves the object, at the start of the use of Spring’s BeanUtils do object conversion, there is no problem, then to the test, the increasing amount of data, found that the interface is more and more slow, thought database query problem, move the SQL to the database operation, found that is not slow, because the key fields of basic index have gone, It’s not very slow, and then step by step, it turns out to be BeanUtils time consuming, which leads to the following practice of copying objects in three different ways

Practices: Apache BeanUtils, Spring BeanUtils, Mapstruct

Many of you have probably only used Spring’s BeanUtils, and the other two have not, but that’s okay. Here’s a simple test

  • Maven dependencies are introduced, and junit is used to test SpringBoot projects that are created directly here
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional> </dependency> <! <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> <! --> <dependency> <groupId> Commons - BeanUtils </groupId> <artifactId> Commons - BeanUtils </artifactId> The < version > 1.8.3 < / version > < / dependency > <! --> <dependency> <groupId>org.mapstruct</groupId> <artifactId> < version > 1.2.0. Final < / version > < / dependency > < the dependency > < groupId > org. Mapstruct < / groupId > < artifactId > mapstruct - processor < / artifactId > < version > 1.2.0. The Final < / version > < / dependency > < / dependencies >Copy the code
  • Class structure

  • Four simple classes
/** * @description user entity * @author Wanm * @date 2020/7/8 21:37 */ @data @noargsconstructor @allargsconstructor public class User { private String id; private String name; private Integer age; private String address; private String sex; } /** * @description userVo * @author Wanm * @date 2020/7/8 21:37 */ @Data @NoArgsConstructor @AllArgsConstructor public  class UserVo { private String id; private String name; private Integer age; private String address; private String sex; } /** * @description UserTransfer * @author Wanm * @date 2020/7/8 21:37 */ @Mapper public interface UserTransfer { /** * Entity go to vo * @param user * @return*/ List<UserVo> entityToVo(List<User> user); } / Test class * * * * / @ SpringBootTest class MapstructApplicationTests {@ Test voidcontextLoadsList<User> userList = new ArrayList<User>();for(int i = 0; i < 1000000; i++) { User user = new User(UUID.randomUUID().toString(),UUID.randomUUID().toString(),i,UUID.randomUUID().toString(),UUID.randomUUID().toString ()); userList.add(user); } System.out.println(userList.get(0)); System.out.println("Start copying ---------------------------------------");
        testBeanUtils(userList);
        testSpringBeanUtils(userList);
        testMapStruct(userList);
    }

    /**
     *  Apache的BeanUtils
     * @param userList
     */
    public static void testBeanUtils(List<User> userList){ long start = System.currentTimeMillis(); List<UserVo> userVos = new ArrayList<>(); userList.forEach(item->{ UserVo userVo = new UserVo(); try { BeanUtils.copyProperties(userVo,item); userVos.add(userVo); } catch (Exception e) { e.printStackTrace(); }}); long end = System.currentTimeMillis(); System.out.println(userVos.get(0)); System.out.println("Validation of set size parameters"+userVos.size()+Apache BeanUtils time:+(end-start)+"ms");
    }

    /**
     * Spring的BeanUtils
     * @param userList
     */
    public static void testSpringBeanUtils(List<User> userList){ long start = System.currentTimeMillis(); List<UserVo> userVos = new ArrayList<>(); userList.forEach(item->{ UserVo userVo = new UserVo(); try { org.springframework.beans.BeanUtils.copyProperties(item,userVo); userVos.add(userVo); } catch (Exception e) { e.printStackTrace(); }}); long end = System.currentTimeMillis(); System.out.println(userVos.get(0)); System.out.println("Validation of set size parameters"+userVos.size()+"Spring BeanUtils time:"+(end-start)+"ms"); } /** * mapStruct copy @param userList */ public voidtestMapStruct(List<User> userList){
        long start = System.currentTimeMillis();
        List<UserVo> userVos = Mappers.getMapper(UserTransfer.class).entityToVo(userList);
        long end = System.currentTimeMillis();
        System.out.println(userVos.get(0));
        System.out.println("Validation of set size parameters"+userVos.size()+"MapStruct time:"+(end-start)+"ms"); }}Copy the code
  • In real development, the VO class attribute field will be much less than the entity class. This is just a test. Let’s look at the test results

  • Do you see that the time of MapStruct is 41ms, which is perfect? The larger the amount of data, the more difference can be seen. The figure below is specific analysis for the time of different data amounts:

Apache Spring MapStruct
1000 67ms 10ms 2ms
1w 174ms 35ms 3ms
10w 808ms 69ms 9ms
100w 5620ms 336ms 42ms

It can be seen that the larger the amount of data MapStruct>Spring>Apache, this performance advantage is becoming more and more obvious, daily development of object copy is only a small part of the logic in the code, if the amount of data is large, we still recommend using MapStruct to improve the performance of the interface. Spring’s BeanUtils will work if the data volume is not large, depending on the actual business scenario!!


MapStruct uses the annotation handler to generate the implementation class, which contains a native new object, and then SetXxx/getXxx assignment to copy the data


public class UserTransferImpl implements UserTransfer {
    public UserTransferImpl() {
    }

    public List<UserVo> entityToVo(List<User> user) {
        if (user == null) {
            return null;
        } else {
            List<UserVo> list = new ArrayList(user.size());
            Iterator var3 = user.iterator();

            while(var3.hasNext()) {
                User user1 = (User)var3.next();
                list.add(this.userToUserVo(user1));
            }

            return list;
        }
    }

    protected UserVo userToUserVo(User user) {
        if (user == null) {
            return null;
        } else {
            UserVo userVo = new UserVo();
            userVo.setId(user.getId());
            userVo.setName(user.getName());
            userVo.setAge(user.getAge());
            userVo.setAddress(user.getAddress());
            userVo.setSex(user.getSex());
            returnuserVo; }}}Copy the code

Spring’s BeanUtils and Apache’s BeanUtils use reflection. There are some differences between their internal implementations


Summary: Through this simple test, I have learned the implementation methods and performance differences of the three copy methods, but I still suggest friends to use MapStruct according to the actual scene. MapStruct performance is really good, when it is necessary to introduce third-party dependencies, if the amount of data is not large, Spring’s own BeanUtils will be enough.



Ps: write write imperceptibly zero, can not hurry to sleep, dog life matters, small friends three even oh!!



I am Yan Qing, not Yan Zhenqing, a struggle in the Internet line of small development