2012-05-06 105 views
1

我正在開發一個客戶數據加載器,客戶可以有多個地址。如果找不到客戶,我創建它並添加地址。如果客戶存在,我只需添加新地址,如下所示:在MongoDB中添加多個子文檔

DBObject findCustomer = new BasicDBObject(); 
    findCustomer.put("email", custEmail); 

    //check for existing customer 
    DBObject newCustomer = customerCollection.findOne(findCustomer); 

    if (newCustomer == null) { 
     //INSERT  
     newCustomer = new BasicDBObject(); 
     newCustomer.put("firstname", firstname); 
     newCustomer.put("lastname", lastname); 
     newCustomer.put("email", custEmail); 
     newCustomer.put("password", custData.getPassword()); 
     newCustomer.put("softwaretime", new Date()); 
    } 

    DBObject newAddress = new BasicDBObject(); 
    City tempCity = new City(); 
    tempCity = addressData.getCity(); 

    newAddress.put("type", addressData.getType()); 
    newAddress.put("line1", addressData.getLine1()); 
    newAddress.put("line2", addressData.getLine2()); 
    newAddress.put("city", tempCity.getCity()); 
    newAddress.put("state", tempCity.getState()); 
    newAddress.put("postal", tempCity.getZip()); 
    newAddress.put("country", tempCity.getCountry()); 

    newCustomer.put("address", newAddress); 

    customerCollection.save(newCustomer); 

這適用於新客戶。問題是,當客戶已經存在時,新地址將覆蓋現有地址。

如何將新地址添加到客戶,以便它將保持多個地址?

從我發現的情況來看,我應該可以通過shell的「推送」來完成此操作。但是我沒有在BasicDBObject上看到「push」方法。

+0

這看起來很危險 - 當您有多個客戶使用相同的名字和姓氏時會發生什麼?這段代碼也不是線程安全的 - 如果兩個線程正在嘗試創建同一個客戶,那麼最終可能會有重複。您也可能會推送相同的地址兩次。 –

+0

好點@AsyaKamsky,我將使用電子郵件地址作爲搜索關鍵字。另外,這是一個單線程應用程序,專爲將數據加載到MongoDB中而設計。如果多個(競爭)線程是可能的,你會如何建議考慮線程安全? – Aaron

+0

有多種方法可以做到這一點 - 取決於您的應用程序邏輯預期/需要。你可以使電子郵件唯一,然後第二次插入嘗試將失敗,但你的應用程序將需要期待和恢復。你可以使用Java併發性,但是如果它只是地址的多個副本,那麼可以使用$ addToSet而不是$ push,然後最終不會添加已在列表中的地址。 –

回答

1

您的邏輯可以更簡單,事實證明。您不需要通過「電子郵件」獲取客戶(我假設這是您客戶的唯一識別密鑰)只需更新即可。

findCustomer.put("email", custEmail); // search query for the customer email 

// construct your newAddress object the same way you already are 

BasicDBObject custMod = new BasicDBObject(); 
custMod.put("$addToSet", newAddress); 
customerCollection.update(findCustomer, custMod, true /* upsert */, false /* multi */); 

現在你有你的邏輯的方式的一個大問題是它不會工作的多線程。您可以檢查客戶,但不會在那裏。當你構建插入它的對象時,另一個線程已經在做它。由於地址對象是一個數組而不是單個字段,所以使用$ addToSet會添加到數組中,如果它存在,但是如果它創建一個新的客戶,那麼它將創建地址作爲數組。

+0

這工作。謝謝! – Aaron

2

您希望地址是地址列表而不是單個地址文檔。因此,對於新客戶,你想有:

 
newCustomer.put("addresses", [newAddress]) 
customerCollection.save(newCustomer) 

併爲現有客戶想要

 
customerCollection.update(newCustomer, {$push: {"addresses": newAddress}}) 

對不起,我不知道的Java API,所以你必須去適應代碼以創建適當的對象

+0

謝謝@ChrisAtLee,我會給你一個鏡頭。 – Aaron

+1

$ push對於現有客戶是不正確的,除非您檢查此地址記錄不存在(並且即使您這樣做也不會在多線程設置中安全使用$ addToSet,而只會在添加它時纔會添加還沒有,$推無條件地增加)。 –

+0

@AsyaKamsky你會碰巧知道這樣做的Java語法? – Aaron