【專案問題總結】5:樹形結構節點的級聯刪除邏輯

連江偉發表於2016-06-06

問題描述:

        改Bug改到機構型別管理模組的時候,有一個非常常見的問題出現,如下圖所示:


        當使用者在進行刪除操作的時候,如果將樹形結構的中間節點刪除,整個頁面的資料就消失了,比如將上圖中的名稱為“學院”的這個節點刪除,那麼整個樹形結構的資料都沒了,這是一個很明顯的Bug。

 

問題分析:

        對於這樣一個這麼明顯的Bug,什麼原因造成的?一開始在頁面進行操作的時候並不知道是哪裡出了問題,後來開啟資料庫對應的表結構才發現,這種樹形結構對應的資料庫表是一張自關聯表。當我在頁面將中間節點(非葉子節點)刪除之後,對照著資料庫檢視,發下該節點下的子節點並沒有被刪除。

        找到了問題的關鍵,下面就要想辦法解決之。這是一個級聯刪除的情景,在刪除父節點的時候,其下所有的子節點也應該被刪除。之前使用過Hibernate,知道Hibernate的對映關係中有一個cascade屬性,其值有all、delete、save-update和none,它們各自的含義相信大家也都瞭解。其中delete表示在執行delete時,進行級聯操作,刪除和該物件關聯的物件。

        那麼下面的問題是,該專案使用的ORM框架是不是支援級聯操作的設定呢?本專案所使用的ORM框架是EclipseLink + JPA,我一想完了,沒用過啊,接著去網上找相關的資料,看EclipseLink的級聯操作如何設定,發現資料很少,大都是英文的,而且也沒有找到如何設定類似於cascade屬性設定的內容,於是乎,我就開始想自己寫程式碼實現級聯刪除。

        當我去分析程式碼的時候,發現底層的刪除方法並不是真正的delete,而是所謂的“軟刪除”,也就是在表結構中使用isDelete欄位來表示該資料記錄是否存在,此時我的內心是崩潰的,這玩意怎麼整啊?框架能解決級聯軟刪除的問題嗎?Maybe,but I don’t know。這更加堅定了我自己寫程式碼實現級聯刪除的決心。

 

問題解決:

        解決辦法已經敲定,那麼剩下的就是coding了。要想實現級聯刪除(更新),就要引入“遞迴”的思想了,必須設計一個遞迴方法,來從樹形結構的上層節點向下進行遍歷,直到找到該分支的葉子節點,執行刪除之後,再一層一層的返回依次進行刪除,最終該節點以及其所有的子孫節點都刪除。下面我們來看具體的程式碼實現。

/**
	 * 更新節點資訊的isDelete欄位值,設定為1(0:存在  1:刪除)
	 */
	@Override
	public boolean updateNodeLevel(String nodeid,String dataBaseName) {
		
		String variable = "h.isDelete=:isDelete";
		String condition = "where h.id ='" + nodeid + "'";
		Map<Serializable,Serializable> map = new HashMap<Serializable, Serializable>(); 
		map.put("isDelete", 1);
		
		return institutionTypeEao.updateByConditionGeneric(variable, condition, dataBaseName, map, "h");
	}
	/**
	 * 遞迴刪除每個子節點
	 * @param nodeid
	 * @param dataBaseName
	 */
	private void recursionDelNode(String nodeid,String dataBaseName){
		//查詢該節點的所有子節點
		List<NodeLevel> nodeLevelList=findChildLevelsById(nodeid,dataBaseName);
		//判斷該節點是否有子節點
		if(nodeLevelList.size()>0){
			//若有多個子節點,則遞迴遍歷所有的子節點
			for(int i=0;i<nodeLevelList.size();i++){
				//遞迴呼叫
				recursionDelNode(nodeLevelList.get(i).getId(),dataBaseName);
			}
		}
		//若沒有子節點則直接刪除
		updateNodeLevel(nodeid,dataBaseName);
	}
	
	/**
	 * 刪除節點以及節點的所有子節點資訊
	 */
	@Override
	public boolean deleteNodeLevel(String nodeId, String dataBaseName) {
		boolean flag=true;
		try{
			recursionDelNode(nodeId,dataBaseName);
		}catch(Exception e){
			e.printStackTrace();
			flag=false;
		}
		return flag;
	}

        其實,使用框架為我們封裝好的各種服務固然是方便快捷的,但是對於我們的能力提升是並沒有什麼益處的。最為一個程式設計師,不要求你能寫出多麼高大上的框架和演算法,但是基礎的東西還是需要我們去掌握的,知其然,知其所以然。

相關文章