Java Bean Copy 问题发现

前因简介

    在工作过程中使用 Java Bean 作为容器盛放数据日常工作。我在工作过程中发现了这么一个问题:
    业务主体是,对一个订单进行拆分。订单分为主体部分和明细部分,明细可以有多个,拆分之后对订单重新整理计算总金额。具体业务比这个要复杂,此处简单说明问题产生的部分。

业务模拟

    此处定义 class Order,class OrderHead,OrderDetail 作为 Java 对象,并定义简单的属性,如下所示:

@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;
}

    因为在具体业务中涉及到多种拆分方式,此处订单的拆分和重新计算总金额分开写在两个部分。此处模拟金额大于20的放在一个订单中,其他放在另外一个订单中,具体方法代码:

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);
}

此处咋一看没什么问题,就是对订单明细进行拆分,订单主体部分分别放到两个订单里,但是具体测试的时候就有问题了,测试代码如下:

private static JavaBeanRelation relation = new JavaBeanRelation();

// 需求把 订单 a 中的 明细 拆分,大于20的单独拆分成一个订单,并重新计算head的总金额,得到最后的两个订单
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);
    //输出最后结果JsonUtils为序列化工具类
    System.out.println(JsonUtils.getInstance().toJsonString(result));
}

控制台输出结果:

[
  {
    "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"
    }
  }
]

    这个时候就有问题了,订单主体的最后一个是对的,15+16 =31 ,但是第一个怎么也变了?也就是说两个订单引用的是同一个对象,当其中一个发生改变的时候另外一个也发生了改变,此处可以在排序的时候对OrderHead进行复制重新计算,也可以在拆分的时候直接使用两个对象,我在此处使用的是 Spring 的 BeanUtils.copyProperties(Object source, Object target) throws BeansException 方法,具体split方法修改如下:

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);
}

     修改完成之后得到的结果就是正确的了,具体细节可以使用debug查看,拆分之后的order可以看到引用的对象是同一个,此篇就不赘述,关于为什么不适用 clone 方法下篇文章讲述。

上一篇:搭建Android工程的步骤及其第一个安卓程序


下一篇:Linux命令学习——用户及文件权限管理