Java Bean Copy problem discovery

Keywords: Java Spring

Introduction to antecedents

Java beans are used as containers to hold data for daily work. I found a problem in my work:
The business entity is to split an order. The order is divided into main part and detail part. There can be multiple details. After splitting, the order is reorganized to calculate the total amount. The specific business is more complex than this. Here is a brief description of the problem.

Business simulation

Class order, class orderhead and orderdetail are defined as Java objects here, and simple properties are defined as follows:

@Setter
@Getter
class Order {
    private OrderHead orderHead;
    private List<OrderDetail> orderDetails;
}

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
class OrderHead {
    private String orderId;
    private String productName;
    private String orderMoney;
}

@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
class OrderDetail {
    private String detailId;
    private String detailName;
    private String detailMoney;
}

Because there are many splitting methods involved in specific business, here the total amount of order splitting and recalculation is written in two parts separately. Here, the simulation amount greater than 20 is placed in one order, and the others are placed in another order. Specific method code:

private void resetMoney(List<Order> result) {
   for (Order order : result) {
       BigDecimal money = new BigDecimal(0);
       for (OrderDetail orderDetail : order.getOrderDetails()) {
           money = money.add(new BigDecimal(Double.valueOf(orderDetail.getDetailMoney())));
       }
       order.getOrderHead().setOrderMoney(String.valueOf(money));
   }
}

private void split(Order order, List<Order> result) {
  	List<OrderDetail> orderDetails = order.getOrderDetails();
    Order order1 = new Order();
    Order order2 = new Order();

    List<OrderDetail> orderDetails1 = new ArrayList<>();
    List<OrderDetail> orderDetails2 = new ArrayList<>();
    for (OrderDetail orderDetail : orderDetails) {
        if (Double.valueOf(orderDetail.getDetailMoney()) >= 20) {
            orderDetails1.add(orderDetail);
        } else {
            orderDetails2.add(orderDetail);
        }
    }
    order1.setOrderDetails(orderDetails1);
    order2.setOrderDetails(orderDetails2);

    order1.setOrderHead(order.getOrderHead());
    order2.setOrderHead(order.getOrderHead());
    result.add(order1);
    result.add(order2);
}

There is no problem in this view. It is to split the order details. The main part of the order is placed in two orders, but there is a problem in the specific test. The test code is as follows:

private static JavaBeanRelation relation = new JavaBeanRelation();

// It is required to split the details in order a into one order if it is greater than 20, and recalculate the total amount of head to get the last two orders
public static void main(String[] args) {
    Order order = new Order();
    OrderDetail detail1 = new OrderDetail("d1", "dname1", "24");
    OrderDetail detail2 = new OrderDetail("d2", "dname2", "25");
    OrderDetail detail3 = new OrderDetail("d3", "dname3", "15");
    OrderDetail detail4 = new OrderDetail("d4", "dname4", "16");
    OrderHead orderHead = new OrderHead("b1", "bname1", "80");

    order.setOrderHead(orderHead);

    List<OrderDetail> orderDetails = new ArrayList<>();
    orderDetails.add(detail1);
    orderDetails.add(detail2);
    orderDetails.add(detail3);
    orderDetails.add(detail4);
    order.setOrderDetails(orderDetails);

    List<Order> result = new ArrayList<>();
    relation.split(order, result);
    relation.resetMoney(result);
    //Output the final result JsonUtils as the serialization tool class
    System.out.println(JsonUtils.getInstance().toJsonString(result));
}

Console output:

[
  {
    "orderDetails": [
      {
        "detailId": "d1",
        "detailMoney": "24",
        "detailName": "dname1"
      },
      {
        "detailId": "d2",
        "detailMoney": "25",
        "detailName": "dname2"
      }
    ],
    "orderHead": {
      "orderId": "b1",
      "orderMoney": "31",
      "productName": "bname1"
    }
  },
  {
    "orderDetails": [
      {
        "detailId": "d3",
        "detailMoney": "15",
        "detailName": "dname3"
      },
      {
        "detailId": "d4",
        "detailMoney": "16",
        "detailName": "dname4"
      }
    ],
    "orderHead": {
      "orderId": "b1",
      "orderMoney": "31",
      "productName": "bname1"
    }
  }
]

There is a problem at this time. The last one of the order body is right, 15 + 16 = 31, but how does the first one change? In other words, two orders refer to the same object. When one of them changes, the other also changes. Here, you can copy and recalculate the OrderHead during sorting, or you can directly use two objects during splitting. I use Spring's BeanUtils.copyProperties(Object source, Object target) throws BeansException method. The specific split method is modified as follows:

private void split(Order order, List<Order> result) {
   List<OrderDetail> orderDetails = order.getOrderDetails();
   Order order1 = new Order();
   Order order2 = new Order();

   List<OrderDetail> orderDetails1 = new ArrayList<>();
   List<OrderDetail> orderDetails2 = new ArrayList<>();
   for (OrderDetail orderDetail : orderDetails) {
       if (Double.valueOf(orderDetail.getDetailMoney()) >= 20) {
           orderDetails1.add(orderDetail);
       } else {
           orderDetails2.add(orderDetail);
       }
   }
   order1.setOrderDetails(orderDetails1);
   order2.setOrderDetails(orderDetails2);

   order1.setOrderHead(order.getOrderHead());

   OrderHead orderHeadCopy = new OrderHead();
   BeanUtils.copyProperties(order.getOrderHead(),orderHeadCopy);
   order2.setOrderHead(orderHeadCopy);

   result.add(order1);
   result.add(order2);
}

After the modification is completed, the result is correct. You can use debug to view the specific details. After splitting, the order can see that the referenced objects are the same. This article will not elaborate. The next article will explain why the clone method is not applicable.

Published 7 original articles, won praise 0, visited 2357
Private letter follow

Posted by scuff on Sat, 07 Mar 2020 21:09:02 -0800