Resolve the problem that BeanUtils. copyProperties does not support replication of collections

  • 2021-09-16 07:02:18
  • OfStack

In our work, we often use the tool class BeanUtils. copyProperties of Spring to copy the bean attribute, and the replication here belongs to shallow replication. And you cannot copy collections and arrays. This article will do some tests on this tool.

At the end of this article, we will propose a solution to copy collection attributes

Preparation: Prepare the classes needed for testing


@Data
public class Class {
    private People[] member;
    private People teacher;
    private List<People> student;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class People {
    private Integer id;
    private String name;
    private Integer age;
    private Integer sex;
}

Test code: Test whether BeanUtils. copyProperties supports copying arrays and collections, and solutions


public static void main(String[] args) {
 //  Test the replication of an array 
 People[] member = new People[3];
 member[0] = new People(1, " Teacher ", 30, 1);
 member[1] = new People(2, " Monitor ", 15, 1);
 member[2] = new People(3, " Students ", 15, 1);
 People[] member1 = new People[]{};
 BeanUtils.copyProperties(member, member1);
 System.out.println(" Whether you can copy arrays: " + (member1.length == 0 ? false : true));
 //  Test List Copy of ( Map It can't be copied either, and the test is omitted) 
 List<People> student = new ArrayList<>();
 student.add(member[1]);
 student.add(member[2]);
 List<People> student1 = new ArrayList<>();
 BeanUtils.copyProperties(student, student1);
 System.out.println("BeanUtils.copyProperties Can you copy List : " + (student1.isEmpty() ? false : true));
 //  Pass JSON Tool implementation List Replication of (not just List Array and Map And so on can also realize replication through similar methods, and it is necessary to have a parameter-free construction method, otherwise an error is reported) 
 student1 = JSON.parseArray(JSON.toJSONString(student), People.class);
 System.out.println(" Pass JSON Tool replication List : " + student1);
 System.out.println(" Pass JSON Whether the tool is deeply copied: " + (student.get(0) != student1.get(0) ? true : false));
 //  Test for deep replication 
 Class source = new Class();
 source.setMember(member);
 source.setTeacher(member[0]);
 source.setStudent(student);
 Class target = new Class();
 BeanUtils.copyProperties(source, target);
 System.out.println("BeanUtils.copyProperties Whether to copy deeply: " + (source.getMember() != target.getMember() ? true : false));
}

Test results

Can I copy the array: false
BeanUtils. Can copyProperties replicate List: false
Copy List through JSON tool: [People (id=2, name=Monitor, age=15, sex=1), People (id=3, name=Student, age=15, sex=1)]
Whether to copy deeply through JSON tool: true
BeanUtils. copyProperties deep replication: false

In addition to the JSON tool, the simplest replication for List is to loop replicate collection attributes. The following tests the efficiency of the two methods.


public static void main(String[] args) {
 int count = 1;
 System.out.println(" Test data length: " + count);
 List<People> source = new LinkedList<>();
 List<People> target = new LinkedList<>();
 long start;
 for (int i = 0; i < count; i++) {
     source.add(new People(1, "ly", 25, 1));
 }
 
 start = System.nanoTime();
 target = JSON.parseArray(JSON.toJSONString(source), People.class);
 System.out.println("JSON : " + (System.nanoTime() - start));
 
 start = System.nanoTime();
 for (int i = 0; i < count; i++) {
     People p = new People();
     BeanUtils.copyProperties(source.get(i), p);
     target.add(p);
 }
 System.out.println("BeanUtils.copyProperties" + (System.nanoTime() - start));
}

The results of count=1, 10, 100, 10000, 10000 and 100000 were tested respectively. In order to prevent the impact of one execution, only one case of one replication method was tested at a time, and a total of 12 executions were performed. By comparison, you can see that copying attributes through JSON is faster than BeanUtils.

Test data length: 1
JSON: 154767336
Bean: 275182853
Test data length: 10
JSON: 165678435
Bean: 275301421
Test data length: 100
JSON: 167937206
Bean: 328461161
Test data length: 1000
JSON: 187832969
Bean: 315815289
Test data length: 10000
JSON: 297461312
Bean: 362763360
Test data length: 100000
JSON: 562035707
Bean: 5815319343

Solve the replication of List and Map in the following ways


public static <T> List copyList(List<T> list) {
    if (CollectionUtils.isEmpty(list)) {
        return new ArrayList();
    }
    return JSON.parseArray(JSON.toJSONString(list), list.get(0).getClass());
}

public static Map<String, Object> copyMap(Map map) {
    return JSON.parseObject(JSON.toJSONString(map));
}

Usage, advantages and disadvantages of BeanUtils. copyProperties

1. Introduction

The BeanUtils provides reflective and introspective wrapping of the Java and the API. Its main purpose is to process the attributes of JavaBean by using reflection mechanism. As we know, an JavaBean usually contains a large number of attributes. In many cases, the processing of JavaBean leads to a large number of get/set codes piling up, which increases the code length and the difficulty of reading the code.

2. Usage

BeanUtils is a common tool class in this package, and only its copyProperties () method is introduced here. The method is defined as follows:

Java code


public static void copyProperties(java.lang.Object dest,java.lang.Object orig)   
throws java.lang.IllegalAccessException, java.lang.reflect.InvocationTargetException 

If you have two JavaBean with many of the same attributes, a very common situation is the PO object (persistent object) in Struts and the corresponding ActionForm, such as Teacher and TeacherForm.

We will construct an PO object from ActionForm in Action. The traditional way is to assign values to attributes one by one by using statements like the following:


// Get TeacherForm   
TeacherForm teacherForm=(TeacherForm)form;   
  
// Structure Teacher Object    
Teacher teacher=new Teacher();   
  
// Assignment    
teacher.setName(teacherForm.getName());   
teacher.setAge(teacherForm.getAge());   
teacher.setGender(teacherForm.getGender());   
teacher.setMajor(teacherForm.getMajor());   
teacher.setDepartment(teacherForm.getDepartment());   
  
// Persistence Teacher Object to database    
HibernateDAO.save(teacher);  

After using BeanUtils, the code is greatly improved, as follows:


// Get TeacherForm   
TeacherForm teacherForm=(TeacherForm)form;   
  
// Structure Teacher Object    
Teacher teacher=new Teacher();   
  
// Assignment    
BeanUtils.copyProperties(teacher,teacherForm);   
  
// Persistence Teacher Object to database    
HibernateDAO.save(teacher);  

If there are attributes with different names between Teacher and TeacherForm, BeanUtils does not process these attributes and requires the programmer to handle them manually.

For example, if Teacher contains modifyDate (which records the last modification date and does not require the user to enter it in the interface) and TeacherForm does not have this attribute, then add a sentence after copyProperties () in the above code:


teacher.setModifyDate(new Date());  

Well, it's very convenient! In addition to BeanUtils, there is also a tool class named PropertyUtils, which also provides copyProperties () method, which is similar to BeanUtils method with the same name. The main difference is that the latter provides type conversion function, that is, when two JavaBean attributes with the same name are found to be different types, they can be converted within the supported data type range, while the former does not support this function, but the speed will be faster.

The types of transformations supported by BeanUtils are as follows:


* java.lang.BigDecimal   
* java.lang.BigInteger   
* boolean and java.lang.Boolean   
* byte and java.lang.Byte     
* char and java.lang.Character    
* java.lang.Class   
* double and java.lang.Double    
* float and java.lang.Float
* int and java.lang.Integer   
* long and java.lang.Long   
* short and java.lang.Short   
* java.lang.String   
* java.sql.Date   
* java.sql.Time   
* java.sql.Timestamp  

One point to note here is that java. util. Date is not supported, while its subclass java. sql. Date is supported. So if an object contains properties of a time type and you want to be converted, 1 must use the java. sql. Date type. Otherwise, an argument mistype exception will be prompted at conversion time.

3. Advantages and disadvantages

The Apache Jakarta Commons project is very useful. I have used various popular commons components, directly or indirectly, on many different projects. One of the powerful components is BeanUtils. I'll show you how to use BeanUtils to convert an local entity bean into a corresponding value object:


BeanUtils.copyProperties(aValue, aLocal)  

The above code copies attributes from the aLocal object to the aValue object. It's quite simple! It copies no matter how many attributes the local (or corresponding value) object has. We assume that the local object has 100 attributes.

The above code eliminates the need to type at least 100 lengthy, error-prone, and repeated get and set method calls. This is great! It's so powerful! It's so useful!

Now, there is another bad news: the cost of using BeanUtils is surprisingly expensive! In a simple test, BeanUtils took more time than the sum of fetching the data, copying it to the corresponding value object (by manually calling the get and set methods), and serializing it back to the remote client. So use this power carefully!


Related articles: