Business Development must see - entity mapping tools recommended

Foreword

statement:

1, DO (business entity objects), DTO (data transfer object).

In a mature project, especially now distributed system, between the application and after application, as well as a separate application segmentation module, DO generally will not let external dependencies, this time the need to provide external interface module in put DTO object for transmission, that is, DO internal objects, objects outside DTO, DTO may be changed according to business needs and do not need to map all the attributes of DO.

  • In our external exposure Dubbo interface, usually so defined interface class
 /**
     * 获取营销信息
     *
     * @param hallId 场馆编号
     * @param modelCode 车型代码
     * @return
     */
    BrandMarketInfoDTO getBrandMarketInfo(String hallId, String modelCode);
复制代码
  • In the interface mapping and data persistence layer implementation in general, so we will write
/**
 * 获取水牌营销信息
 *
 * @param hallId
 * @param modelCode
 * @return
 */
HallCarManageDO getBoardMarketInfo(@Param("hallId") String hallId, @Param("modelCode") String modelCode);
复制代码

But we HallCarManageDO and BrandMarketInfoDTO attributes and attribute type is not equal. This mutual conversion between the object and the subject, we need to have a tool specifically designed to solve the conversion problem, after all, each field get / set will be very troublesome.

Common tools and implementation

Here are the common property of the majority of development engineers Bean Replication Tool

  • Spring.BeanUtils
  • Cglib.BeanCopier
  • MapStruct

The following selection attribute evaluation function for each tool to compare different classes

BeanUtils

import org.springframework.beans.BeanUtils;

/**
 * @author james mu
 * @date 2019/10/22
 */
public class PojoUtils {

    /**
     *
     * @param source 源对象
     * @param clazz 目标对象
     *
     * @return 复制属性后的对象
     */
    public static <T> T copyProperties(Object source, Class<T> clazz) {
        if (source == null) {
            return null;
        }
        T target;
        try {
            target = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException("通过反射创建对象失败");
        }
        BeanUtils.copyProperties(source, target);
        return target;
    }

}

复制代码

springframeworkThe BeanUtils also by java 内省机制get getter / setter, and then copy the properties in order to achieve invoked by reflection.

BeanCopier

Dependent reference

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>
复制代码

Tools to achieve

import net.sf.cglib.beans.BeanCopier;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author james mu
 * @date 2019/10/22
 */
public class BeanCopierUtils {

    /**
     * BeanCopier拷贝速度快,性能瓶颈出现在创建BeanCopier实例的过程中。
     * 所以,把创建过的BeanCopier实例放到缓存中,下次可以直接获取,提升性能
     */
    public static Map<String, BeanCopier> beanCopierMap = new ConcurrentHashMap<String, BeanCopier>();

    /**
     * cp 对象赋值
     *
     * @param source 源对象
     * @param target 目标对象
     */
    public static void copyProperties(Object source, Object target) {
        String beanKey = generateKey(source.getClass(), target.getClass());
        BeanCopier copier = null;
        if (!beanCopierMap.containsKey(beanKey)) {
            copier = BeanCopier.create(source.getClass(), target.getClass(), false);
            beanCopierMap.put(beanKey, copier);
        } else {
            copier = beanCopierMap.get(beanKey);
        }
        copier.copy(source, target, null);
    }

    private static String generateKey(Class<?> class1, Class<?> class2) {
        return class1.toString() + class2.toString();
    }
}

复制代码
  1. Dynamic proxies, to generate the class bytecode, and then through the Java reflection to Class, call its copy method.

  2. We can see here uses ConcurrentHashMap access copier, because BeanCopier.create use the cache, the process also consumes resources, the proposed global initialization only once.

    Custom converter

  3. Support for custom converter.

MapStruct

MapSturct Generating a type-safe, high-performance and no code dependent JavaBean mapping annotation processor (annotation processor).

Grasp the main points:

  1. Annotation Processor
  2. May be generated JavaBeanthat the mapping between the code
  3. Type safety, high performance, non-dependent

From the literal understanding, we can know, this tool can help us achieve JavaBeanconversion between, by way of comment.

Meanwhile, as a tool class, compared to handwriting, which should have a convenient, error-prone characteristics.

getting Started

Dependent reference

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.3.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.3.0.Final</version>
            <scope>provided</scope>
        </dependency>
复制代码

Case presentation

UserDO

import lombok.Data;
import lombok.ToString;

import java.util.Date;

/**
 * @author james mu
 * @date 2019/10/22 
 */
@Data
@ToString
public class UserDO {
    private String name;
    private String password;
    private Integer age;
    private Date birthday;
    private String sex;
}


复制代码

UserDTO

import lombok.Data;
import lombok.ToString;


/**
 * @author james mu
 * @date 2019/10/22
 */
@Data
@ToString
public class UserDTO {
    private String name;
    private String age;
    private String birthday;
    private String gender;
}
复制代码

UserConvertUtils

import com.sanshengshui.javabeanconvert.DO.UserDO;
import com.sanshengshui.javabeanconvert.DTO.UserDTO;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;

/**
 * @author james mu
 * @date 2019/10/22 
 */
@Mapper
public interface UserConvertUtils {

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

    /**
     * 类型转换
     *
     * @param userDO UserDO数据持久层类
     * @return 数据传输类
     */
    @Mappings({
               @Mapping(target = "gender", source = "sex")
            })
    UserDTO doToDTO(UserDO userDO);
}


复制代码
  1. DTO and DO in the same attribute name when the default mapping (such as name), different types of properties will be the same attribute name mapping (such as birthday, a Data, a String)
  2. DTO and attribute names DO different, need @Mappingto form a clear relationship mapping (e.g., corresponding to Gender sex)
  3. No mapping for the attribute is ignored (as UserEntity the password)

The results are as follows:

UserDO(name=snow, password=123, age=20, birthday=Tue Oct 22 17:10:19 CST 2019, sex=男)
+-+-+-+-+-+-+-+-+-+-+-
UserDTO(name=snow, age=20, birthday=19-10-22 下午5:10, gender=男)

复制代码

MapStruct analysis

Above, I wrote three steps to achieve from UserDTOthe UserDOconversion.

Then, as an annotation processor, through MapStructthe generated code has the advantages of how to do?

high performance

Java reflection principle and low reflectivity reasons: juejin.im/post/5da33b...

This is reflected relatively speaking, the need to read the contents of the reflection bytecode, spending will be relatively large. Through MapStructto the generated code, which is similar to the human hand. It can be guaranteed speed.

The previous example can be seen in the generated code after compilation. You can see the target / generated-sources / annotations years.

Corresponding code

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-10-22T17:10:17+0800",
    comments = "version: 1.3.0.Final, compiler: javac, environment: Java 1.8.0_222 (Azul Systems, Inc.)"
)
public class UserConvertUtilsImpl implements UserConvertUtils {

    @Override
    public UserDTO doToDTO(UserDO userDO) {
        if ( userDO == null ) {
            return null;
        }

        UserDTO userDTO = new UserDTO();

        userDTO.setGender( userDO.getSex() );
        userDTO.setName( userDO.getName() );
        if ( userDO.getAge() != null ) {
            userDTO.setAge( String.valueOf( userDO.getAge() ) );
        }
        if ( userDO.getBirthday() != null ) {
            userDTO.setBirthday( new SimpleDateFormat().format( userDO.getBirthday() ) );
        }

        return userDTO;
    }
}

复制代码

You can see that generates an implementation class, and the code is also similar to our hand-written, easy to understand.

Performance Comparison

Time-consuming, the number of executions test between two simple conversion Bean were 10,100,1k, 10k, 100k, time unit is ms.

to sum up

Although the low reflection efficiency, but this time is very small. The performance and function of the dimensions of different tools, personal recommendations converting operation or when the object is small when the high performance requirements of the application, try not to use the tool, but the handwriting getter / setter; without considering performance, Common Object Conversion You can use Cglib.BeanCopier, converting complex objects using MapStruct.

Guess you like

Origin juejin.im/post/5dafa699f265da5b7415185e