前因简介
在工作过程中使用 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 方法下篇文章讲述。