業務場景:
採集服務持續採集數據並放入容器中,每當容器的數量大於1000時,會將容器內的數據放入緩存容器,並清空容器,執行一次保存緩存容器的操作,保存結束後清空緩存數據。
報錯場景:
在保存這1000條緩存容器的數據時候偶爾會報More than one row with the given identifier was found...的錯誤
一般情況這個錯誤:More than one row with the given identifier was found.....都是查詢的時候發生,這次發現在保存時任然也會出現這樣的錯誤。接下來寫了一個test代碼:
@Component
public class ThreadTest2 {
CollectDataDao collectDataDao;
@Autowired
public void setCollectDataDao(CollectDataDao collectDataDao) {
this.collectDataDao = collectDataDao;
}
public void testSaveAll() throws InterruptedException {
List<CollectData> collectDataList = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
CollectData collectData = new CollectData();
collectData.setApmac("123");
collectData.setMac("321");
collectData.setCreatedTime(new Date());
collectData.setDate(new Date());
collectData.setStatus(i);
collectDataList.add(collectData);
}
for (int j = 0; j < 10; j++) {
try {
CollectData save = collectDataDao.save(collectDataList.get(0));
CollectData save1 = collectDataDao.save(collectDataList.get(1));
CollectData save2 = collectDataDao.save(collectDataList.get(2));
CollectData save3 = collectDataDao.save(collectDataList.get(0));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
當程序save3時就會報這樣的錯誤,通過debugger發現通過save方法(這裏主鍵id是自增)原對象會被填充自生成的id,當再一次被保存時就會產生上面的異常。
我們知道使用jpa時,有id的會被執行更新操作,而這裏是報錯,同過查看源碼可以看到
jpa在保存的時候會判斷是否是一個新的對象,如果是一個新的對象,並且存在id纔會執行更新操作。
結論:
jpa在保存的時候如果對同一個對象保存多次就會報More than one row with the given identifier was found的異常。
回到項目的問題,在批量保存的時候報的這個錯誤的原因通過分析可以知道,如果容器的數量到達1000時容器裏的數據就會放到緩存容器,原容器被清空,在保存緩存容器數據還沒保存結束,容器又達到1000,此時容器又會將數據放入緩存容器中並再保存,導致緩存容器存在2000條數據並有一些已經保存,最後造成了異常。