background

As a CRUD Boy, I was tired of Ctrl+C & Ctrl+V development. Especially in the Web class project development, we often encounter PO(Persistent Object, Persistent Object), DTO(Data Transfer Object, Data Transfer Object), VO(View Object, View Object) conversion between objects, After all, the back end can’t throw the original database data directly to the front end for processing (it is estimated to be 🔪), and return a suitable DTO the front end, which is the consciousness of a good CRUD engineer.

The problem

Usually we query a PO object from the database, process it, copy some of its properties to a DTO object or VO object and return it to the front end for further processing or direct display. So the most common statement in the code is the assignment of various properties between these objects:

  • Attributes more, handwriting is more complicated
  • This increases the amount of redundant code and unnecessary workload for subsequent maintenance
  • Because assignments are scattered throughout the code, the correspondence between properties is difficult to maintain
  • The names of attributes in DTO, VO, and PO objects may be different for semantic understanding, and they must be copied correctly
  • When the property value type is different, it can realize the conversion between different types of objects
  • .

Existing solution

Although there are various libraries for object copy: BeanUtils,BeanCopier, etc., but it is not so easy to use, we need a very lightweight method of object copy, so there is the method of object copy with annotations and reflection.

Annotation scheme

There are several reasons for using annotations:

  • Annotations can be non-invasive to the source data and will not involve changes to the source data
  • Annotations do not target the original logic of the object
  • Easy to extend, you can extend the object copy capability by increasing the ability to parse classes

features

  • The name field in the annotation allows you to copy the different name attributes
  • You can use the methodClass, methodName, and Parameters fields in the annotations to cast different attribute values
  • Annotations and a single attribute, can be used as needed, the default same attribute name automatically copied

Note the code

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface AliasField {
    /** * The name of the property that can be copied, with the name registered in the annotation being preferred, and the getter function corresponding to the name ** the first getter function found is the source */
    String[] name() default {};

    /** * The class in which the method converts the source value to the target value, which defaults to the target object */
    Class methodClass(a) default Class.class;

    /** * The name of the method that converts the source value to the target value */
    String methodName(a) default "";

    /** * Parameters required by the method that converts the source value to the target value */
    Class[] parameters() default {};
}
Copy the code

DEMO

The source object
public class UserTable {
    public UserTable(long id, String des, String userId){
        this.id = id;
        this.des = des;
        this.userId = userId;
    }

    private long id;

    private String des;

    private String userId;

    public long getId(a){
        return id;
    }

    public String getDes(a){
        return des;
    }

    public String getUserId(a){
        returnuserId; }}Copy the code
The target object
public class UserProtocol {
    private long id;

    public void setId(long id){
        this.id = id;
    }

    @AliasField(name={"des"})
    private String content;
    public void setContent(String content){
        this.content = content;
    }

    @AliasField(name={"userId"}, methodName = "String2Int", parameters = {String.class})
    private long selfId;
    public void setSelfId(long userId){
        this.selfId = userId;
    }

    public static int String2Int(String userId){
        return Integer.valueOf(userId);
    }

    @Override
    public boolean equals(Object object){
        if(object == null) {return false;
        }
        else if(object == this) {return true;
        }
        else if(object instanceof UserTable){
            UserTable target = (UserTable)object;
            return id == target.getId() && content.equals(target.getDes()) && selfId == Integer.valueOf(target.getUserId());
        }
        else if(object instanceof UserProtocol){
            UserProtocol target = (UserProtocol)object;
            return id == target.id && content.equals(target.content) && selfId == target.selfId;
        }
        else {
            return false; }}@Override
    public String toString(a) {
        return id+"-"+content+"-"+selfId; }}Copy the code
Copy the object

The object can be copied with a simple function call

userTable = new UserTable(2019."I am bast!"."2020");
UserProtocol userProtocol = new UserProtocol();
Convert.convert(userProtocol, userTable);
Copy the code

See unit Test Cases in code for more details