为什么多个DTO会改变会话映射?

时间:2013-07-16 05:54:33

标签: java struts2 reference struts2-json-plugin

在我最近的项目中,我正在使用jQuery Grid,当用户点击按钮时生成动态网格。

在服务器端,使用Session操纵数据。

首先,我得到Session个对象并将该对象强制转换为gatepassDTO

然后我创建了一个本地对象,我正在将属性从旧对象复制到新对象并放入Map

之后我创建了futureDTO个对象,并将属性从gatepassDTO复制到futureDTO。如果我在futureDTO中更改任何内容,则会对包含对象的地图产生影响。

为什么会这样?

public String addOnemoreGatepass() {

        log.info("----------------Entering method addOnemoreGatepass------------");
        try {
            Object map = session.get("gatepassMap");

            GatepassDTO gatepassDTO = new GatepassDTO();
            gatepassDTO=(GatepassDTO)session.get("gatepassDTO");
            if(map!=null){

                //gatepassMap=Collections.synchronizedMap((HashMap)map);
            }
            if(truckNo!=null){
                gatepassDTO.setTruckNo(truckNo);
            }

                     if(gpDirect!=null){
                GatepassDTO tempDTO=new GatepassDTO();
                copyProperty(tempDTO,gatepassDTO);
            /*  HashMap<String,GatepassDTO> maps=null;
                if(gatepassNo.equals("1")){
                    maps=new HashMap<String, GatepassDTO>();
                local.saveMap(getRequest().getSession().getId(),maps);
                }
                maps=local.loadMap(getRequest().getSession().getId());
                maps.put(gatepassNo, tempDTO);*/
                putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
                gatepassMap.put(gatepassNo, tempDTO);

                //local.saveMap(getRequest().getSession().getId(),maps);
                session.put("documentType", documentType);
                session.put("gatepassMap", gatepassMap);
                return SUCCESS;
            }
            else{
                GatepassDTO tempDTO=new GatepassDTO();
                copyProperty(tempDTO,gatepassDTO);
                /*HashMap<String,GatepassDTO> maps=null;
                if(gatepassNo.equals("1")){
                    maps=new HashMap<String, GatepassDTO>();
                local.saveMap(getRequest().getSession().getId(),maps);
                }
                maps=local.loadMap(getRequest().getSession().getId());
                maps.put(gatepassNo, tempDTO);*/
                putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
                gatepassMap.put(gatepassNo, tempDTO);

                //local.saveMap(getRequest().getSession().getId(),maps);
                session.put("documentType", documentType);
                session.put("gatepassMap", gatepassMap);

            }
            GatepassDTO futureDTO=new GatepassDTO();
            copyProperty(futureDTO,gatepassDTO);

            DocumentDTO documentDTO =futureDTO.getDocumentDTO();
            List<GatepassGoodsDTO> goodsList=documentDTO.getGatepassGoodsList();
            int i=0;
            for(GatepassGoodsDTO goodsDTO:goodsList){
                if(goodsDTO.getRemovalType()!=null&&(goodsDTO.getRemovalType().equals("Quantity")||goodsDTO.getRemovalType().equals("Weight"))){
                goodsDTO.setPrevRemovalType(goodsDTO.getRemovalType());
                goodsDTO.setPrevGPQuantity((goodsDTO.getRemovalQuantity()!=null?goodsDTO.getRemovalQuantity():0));
                goodsDTO.setPrevGPWeight((goodsDTO.getRemovalWeight()!=null?goodsDTO.getRemovalWeight():0));
                goodsDTO.setBalanceQuantity(goodsDTO.getBalanceQuantity()-goodsDTO.getRemovalQuantity());
                goodsDTO.setBalanceWeight(goodsDTO.getBalanceWeight()-goodsDTO.getRemovalWeight());
                goodsDTO.getVehicleDetailsList().clear();
                goodsDTO.setVehicleDetailsList(goodsDTO.getDeletedVehicleDetailsList());
                goodsDTO.setDeletedVehicleDetailsList(new ArrayList<GatepassVehicleDTO>());
                goodsDTO.setRemovalType("");
                goodsDTO.setRemovalQuantity(0);
                goodsList.set(i, goodsDTO);}
                else{
                    goodsList.set(i, goodsDTO);
                }
                i++;
            }
            documentDTO.setGatepassGoodsList(goodsList);
            documentDTO.setContainerList(deletedContainerModel);
            futureDTO.setDocumentDTO(documentDTO);
            futureDTO.setTruckModel(new ArrayList<GatepassTruckDTO>());
            session.put("gatepassDTO",futureDTO);
            // setDocumentModel(null);
            // setGridModel(null);
            deletedVehicleModel.clear();
            deletedContainerModel.clear();
            // manifestNo=null;



        } catch (Exception e) {
            log.info(e.getMessage());
        } 
        log.info("---------------Ending method addOnemoreGatepass----------------");
        return SUCCESS;
    }


private void copyProperty(GatepassDTO tempDTO,GatepassDTO gatepassDTO){`enter code here`
        tempDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());
        tempDTO.setTruckNo(gatepassDTO.getTruckNo());
    }

为什么会出现此问题? 这是核心Java问题还是Struts2 JSON问题?

2 个答案:

答案 0 :(得分:1)

您正在使用引用(将它们视为指针)。

从Session Map获取对象时,您将获得它的引用(即使Map只包含引用,而不是真实对象)

gatepassDTO = (GatepassDTO)session.get("gatepassDTO");

然后你实例化tempDTO,并将gatepassDTO.getDocumentDTO()的引用(这是一个引用本身)分配给tempDTO

GatepassDTO tempDTO = new GatepassDTO();
copyProperty(tempDTO,gatepassDTO);
// that inside inside copyProperty does:
tempDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());

然后你实例化futureDTO,并为AGAIN指定gatepassDTO.getDocumentDTO()的引用:

GatepassDTO futureDTO = new GatepassDTO();
copyProperty(futureDTO,gatepassDTO);
// that inside inside copyProperty does:
futureDTO.setDocumentDTO(gatepassDTO.getDocumentDTO());

此时,如果你从gatepassDTO或tempDTO,甚至来自futureDTO调用.getDocumentDTO().setSomething();,你总是要改变相同的物理对象,即在地图中仍然(引用)。

实际上,copyProperty并没有复制任何东西。

原始类型 ,行intchar以及 不可变对象< / em> ,例如IntegerString;

但是这将永远发生在所有其他对象上,例如您编码的对象,DocumentDTO。

为防止出现此类问题,您需要return defensive copies of your objects,这是首选方式,或至少手动复制每个属性,如下所示:

private void copyProperty(GatepassDTO tempDTO,GatepassDTO gatepassDTO){
    DocumentDTO src = gatepassDTO.getDocumentDTO();
    DocumentDTO dest = new DocumentDTO();
    dest.setSomething(src.getSomething());
    dest.setSomethingElse(src.getSomethingElse());
    dest.setEtc(src.getEtc());
    /* go on like that with primitives or immutables, 
    and instantiate new objects for each mutable object you find */

    tempDTO.setDocumentDTO(dest);
    tempDTO.setTruckNo(gatepassDTO.getTruckNo());
}

显然,如果你把这种逻辑放在getter中,你就不需要在你的应用程序中复制这段代码片段了,你就可以防止遗忘或写错字的风险。


注1

在这种情况下帮助你,Apache就在那里。

BeanUtils.copyProperties正是您要找的,它会复制两个对象之间名称匹配的每个字段。


Note2

您的代码可以重构 ...例如:

 if(gpDirect!=null){
    GatepassDTO tempDTO = new GatepassDTO();
    copyProperty(tempDTO,gatepassDTO);
    putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
    gatepassMap.put(gatepassNo, tempDTO);
    session.put("documentType", documentType);
    session.put("gatepassMap", gatepassMap);
    return SUCCESS;
}else{
    GatepassDTO tempDTO = new GatepassDTO();
    copyProperty(tempDTO,gatepassDTO);
    putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
    gatepassMap.put(gatepassNo, tempDTO);
    session.put("documentType", documentType);
    session.put("gatepassMap", gatepassMap);
}

可以写成

GatepassDTO tempDTO = new GatepassDTO();
copyProperty(tempDTO,gatepassDTO);
putCachedObject("SINGLEGATEPASSCACHE", gatepassNo, tempDTO);
gatepassMap.put(gatepassNo, tempDTO);
session.put("documentType", documentType);
session.put("gatepassMap", gatepassMap);

if(gpDirect!=null) return SUCCESS;

花点时间清理它,这将为您节省时间和头痛。

答案 1 :(得分:0)

当您从Map获取对象时,您没有获得该对象的副本,您将获得对该对象的引用。这意味着如果更改对象的属性,则hashmap中的对象“in”也会更改其属性(因为它是同一个对象)。

这是您的代码所发生的事情:

  1. 您获得对会话对象“getpassDTO”
  2. 的引用
  3. 您正在获取有关该对象属性的引用(copyProperty复制对象的引用,它不会克隆您的对象)
  4. 您正在修改这些引用的属性。但是您的“getPassDTO”会话对象仍然引用相同的属性。