Custom VO type conversion of EntityUtils tool class using JPA

  • 2021-12-13 08:08:58
  • OfStack

Directory JPA Custom VO Type Conversion (EntityUtils tool class) dto, vo, po, bo and other entity conversion tool classes announce the protagonist of this time: dozer

JPA Custom VO Type Conversion (EntityUtils Tool Class)

In the JPA query, if you need to return a custom class, you can use the EntityUtils tool class, which source code:


import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
/**
 * @author 954L
 * @create 2019/10/30 17:27
 */
public class EntityUtils {
 
    private static final Logger log = LoggerFactory.getLogger(EntityUtils.class);
 
    /**
     *  Converting Array Data to Entity Classes 
     *  The order of the array elements here must be the same as the order of the attributes in the entity class constructor 1 To 
     *
     * @param list   Array object collection 
     * @param clazz  Entity class 
     * @param <T>    Entity class 
     * @param model  Instantiated entity class 
     * @return  Entity class collection 
     */
    public static <T> List<T> castEntity(List<Object[]> list, Class<T> clazz, Object model) {
        List<T> returnList = new ArrayList<T>();
        if (list.isEmpty()) return returnList;
        Object[] co = list.get(0);
        List<Map> attributeInfoList = getFiledsInfo(model);
        Class[] c2 = new Class[attributeInfoList.size()];
        if (attributeInfoList.size() != co.length) {
            return returnList;
        }
        for (int i = 0; i < attributeInfoList.size(); i++) {
            c2[i] = (Class) attributeInfoList.get(i).get("type");
        }
        try {
            for (Object[] o : list) {
                Constructor<T> constructor = clazz.getConstructor(c2);
                returnList.add(constructor.newInstance(o));
            }
        } catch (Exception ex) {
            log.error(" Exception occurred when entity data was converted into entity class: exception information: {}", ex.getMessage());
            return returnList;
        }
        return returnList;
    }
 
    private static Object getFieldValueByName(String fieldName, Object modle) {
        try {
            String firstLetter = fieldName.substring(0, 1).toUpperCase();
            String getter = "get" + firstLetter + fieldName.substring(1);
            Method method = modle.getClass().getMethod(getter, new Class[]{});
            Object value = method.invoke(modle, new Object[]{});
            return value;
        } catch (Exception e) {
            return null;
        }
    }
 
    private static List<Map> getFiledsInfo(Object model) {
        Field[] fields = model.getClass().getDeclaredFields();
        List<Map> list = new ArrayList(fields.length);
        Map infoMap = null;
        for (int i = 0; i < fields.length; i++) {
            infoMap = new HashMap(3);
            infoMap.put("type", fields[i].getType());
            infoMap.put("name", fields[i].getName());
            infoMap.put("value", getFieldValueByName(fields[i].getName(), model));
            list.add(infoMap);
        }
        return list;
    } 
}

Use native sql query:


    /**
     *  Inquire the position according to the company name 
     * @param name  Company name 
     * @return  List<EmploymentPosition>
     */
    @Query(value = "select id, position, salary, people, experience, address, update_time from employment_position where company_name = ?1 and is_delete is false ORDER BY sort asc", nativeQuery = true)
    List<Object[]> findByCompanyName(String name);

Using type conversion:


@Override
    public List<EmploymentPositionVO> findByCompanyName(String name) {
        List<Object[]> objects = employmentPositionRepository.findByCompanyName(name);
        return EntityUtils.castEntity(objects, EmploymentPositionVO.class, new EmploymentPositionVO());
    }

The VO classes are as follows:


import lombok.Data; 
import java.math.BigInteger;
import java.sql.Timestamp;
 
/**
 * @website https://el-admin.vip
 * @description /
 * @author budezhenjia
 * @date 2021-02-08
 **/
@Data
public class EmploymentPositionVO {
 
    /** ID */
    private BigInteger id;
 
    /**  Job title  */
    private String position;
 
    /**  Monthly salary  */
    private String salary;
 
    /**  Number of people  */
    private Integer people;
 
    /**  Work experience  */
    private String experience;
 
    /**  Place of work  */
    private String address;
 
    /**  Update time  */
    private Timestamp updateTime; 
    public EmploymentPositionVO(BigInteger id, String position, String salary, Integer people, String experience, String address, Timestamp updateTime) {
        this.id = id;
        this.position = position;
        this.salary = salary;
        this.people = people;
        this.experience = experience;
        this.address = address;
        this.updateTime = updateTime;
    } 
    public EmploymentPositionVO() {
    }
}

Attention!

Query sql statement query out of the field should be with VO class attribute order 1, type also need 1 to! !

For example, in the field MySQL, the type is bigint, and in the class VO, the type is bigInteger

dto, vo, po, bo and other entity conversion tool classes

3-tier development and not very novel development ideas, However, the Cheng Xuyuan people who suffer from development are often confused by the conversion between various examples, especially when the number of conversions is too high, they are blinded once, and they don't know where to go. Bloggers also have this difficulty, so they look everywhere on the Internet, find some methods, and combine their own development and use to fill some pits, hoping to help everyone!

The following is the announcement of the protagonist: dozer

Who is he? I don't know his English name. In fact, he is a well-known character. In spring, he is a well-known protagonist. That's right, beanUtils (in fact, it is his body double!) The main function is to copy the class library of JavaBean attribute. What is replication? Yes, just one model and one sample, but there is a little difference in this way, that is, when using it, it is necessary to specify a "container" (nonsense, it is a mapping acceptance object, which can also be called a target) to store it. Otherwise, where to copy it, isn't it?

At this time, there is a broker's appearance, which needs to be copied by the agent "broker" in order to call this body double out (professional body double for 30 years, must have a broker)


<dependency>
    <groupId>net.sf.dozer</groupId>
    <artifactId>dozer</artifactId>
    <version>5.5.1</version>
</dependency>

Ok, the broker's business card has been issued. At this time, find the crew and let's see if this body double can be competent and applied to which crew and scenes!

The first scene is completely 1: (Hey, the degree is 1, it must be easy to get, and you can't see the difference)

In the case that each entity is 1, it is very easy. It is not enough to go directly, and it is not necessary to do anything to deal with it:

Directly use the original body double (API method)


 Mapper mapper = new DozerBeanMapper();
DestinationObject destObject =  
    mapper.map(sourceObject, DestinationObject.class);

Hey hey, change your clothes and go directly to OK (that is, use mapper to convert and copy)

The second scenario is completely different:

Begging is totally different. Why do you get it? You must change to body double, right? At least find a similar one (don't worry, we have a makeup artist, make up and walk!)


@Data
public class UserVo{
    @Mapping("userName")
    private String name;
    @Mapping("password")
    private String pwd;
}

Look at 1, is this thing, it looks familiar, yes, it is our vo


@Data
@TableName("user")
public class UserEntity implements Serializable {
    @ApiModelProperty(value = "id")
    @TableId(value = "id", type = IdType.INPUT)
    private String id;
    @ApiModelProperty(value = " User name ")
    @Mapping("name")
    private String userName;
    @ApiModelProperty(value = " Password ")
    @Mapping("pwd")
    private String password;
    @ApiModelProperty(value = " Login name ")
    private String loginName;
    @ApiModelProperty(value = " Creation time ")
    private Date createTime;
    @ApiModelProperty(value = " Modification time ")
    private Date updateTime;
    @ApiModelProperty(value = " Version number ")
    private Integer version;
    @ApiModelProperty(value = " Void mark ")
    private Integer deleted;
}

What about this? It's our entity object (that is, the database object)

Look at 1, is it completely different from 1!

Here, @ Mappin acts as a cosmetic, covering every different detail to make it look like the original example 1 (that is, it has been mapped)


 @Mapping("userName")
 private String name;

The above is a makeup treatment, mapping name to userName

After each place is processed, go directly to the crew to see if it can not be discovered


    /**
     *  Convert Entity to Another 1 Specified entities 
     *  Arbitrary 1 Parameters are NULL Hour   Will throw NPE
     *
     * @param source  Source entity   Cannot be for NULL
     * @param clazz  Target entity   Cannot be for NULL
     * @param <T>  Generic type 
     * @return  The result after conversion 
     */
    @NonNull
    public static <T> T convert(@NonNull Object source, @NonNull Class<T> clazz) {
        return  dozerMapper.map(source, clazz);
    }

Example:


    /**
     *  Inquire the position according to the company name 
     * @param name  Company name 
     * @return  List<EmploymentPosition>
     */
    @Query(value = "select id, position, salary, people, experience, address, update_time from employment_position where company_name = ?1 and is_delete is false ORDER BY sort asc", nativeQuery = true)
    List<Object[]> findByCompanyName(String name);
0

That's right, so complete the conversion of body double operation, the result can be mapped on, you can try oh!

In the third scenario, a pile of different body double (it's very difficult, a pile of different ones, can't you find a few similar ones?)

For this kind of body double, dozer also has a way to make up through stream stream of JAVA8 (makeup artist cow, can only say so)


    /**
     *  Inquire the position according to the company name 
     * @param name  Company name 
     * @return  List<EmploymentPosition>
     */
    @Query(value = "select id, position, salary, people, experience, address, update_time from employment_position where company_name = ?1 and is_delete is false ORDER BY sort asc", nativeQuery = true)
    List<Object[]> findByCompanyName(String name);
1

See, this way, On OK, hey, did you find out where the makeup artist dozerMapper came from (such a cow makeup artist, call a few makeup shops to go), the blogger tells you that this makeup artist is not as good as the more (everyone knows, 1 kind of object, too much waste of space), only need to establish a global only 1 on the line, the blogger takes you to see 1 make-up room to understand (tools come)


    /**
     *  Inquire the position according to the company name 
     * @param name  Company name 
     * @return  List<EmploymentPosition>
     */
    @Query(value = "select id, position, salary, people, experience, address, update_time from employment_position where company_name = ?1 and is_delete is false ORDER BY sort asc", nativeQuery = true)
    List<Object[]> findByCompanyName(String name);
2

Let's show you one of our body double


@Data
public class UserVo{
    @Mapping("userName")
    private String name;
    @Mapping("password")
    private String pwd;
    private String loginName;
    private Integer version;
    private Integer deleted;
    private String id;
}

The places like 1 are mapped directly, and the places like 1 are annotated with @ Mapping, just fill in the field name of the source specified object

These are just a few methods, and there are other bidirectional mappings, data copying, etc. You can see his official document dozer


Related articles: