有關讀數據庫的無刷新的三級聯動以及層次數據(樹形結構)在關系數據庫的存儲
關鍵字: jsp 三級聯動 層次 樹形結構 數據庫我的標題真冗長:P
最近一直在考慮這個問題,如何在現在常用的關系型數據庫(如mysql,mssql)中存儲含有層次結構(例如xml或者樹型結構)的數據,我最終要實現的結果是一個比較好的頁面三級聯動菜單。比較了一下現有的三級聯動,無非有兩種解決方案,1、將所有數據寫成靜態的,這樣的缺點在于對更新封閉;2、每改變一次去數據庫中獲取數據,填充到下一級菜單,這個缺點是對數據庫的開銷比較大,查詢一次數據要對數據庫做三次操作,而且數據庫設計的比較丑陋。上述兩種方法都讓我比較不能接受。
我想的是一種比較優雅的解決方案:在數據庫中存儲的數據不要冗余,便于查詢,不要產生遞歸,對數據庫的開銷要盡可能的小,盡量做到一次將所有數據都讀出來,用戶也要有比較好的體驗。我最初的解決方案就是用xml來存儲聯動的數據,但是對于數據庫操作來說,最后還是要落實到對文件的I/O操作,從這方面來講,使用xml和數據庫并沒有什么本質的不同。另外一種解決方案就是在關系數據庫中存類xml的數據了。google了半天,最后發現了一片自己學院一位學長翻譯的文章www.nirvanastudio.org/category/database,講得就是如何在關系型數據庫存儲這種層次數據的。文中提出了兩種解決方案,一是“鄰接列表模型”或稱為“遞歸方法”,這種存在著數據冗余,而且遞歸出于眾所周知的原因,效率不高。第二種方法,也就是我現在采用的方法是"前序優先遍歷",表結構也很簡單,沒有冗余
id | name | left | right |
我覺得應該屬于一種深度優先。通過每個節點的左右兩個屬性獲得其子女的屬性,好處就在于獲得一個或幾個節點只需要一次查詢,而在更新的時候速度會比較慢,因為要更新多個后繼節點,不過話說回來,更新相對于獲取來說次數要少的多,可以忽略。
下面是我的解決方案:
java 代碼
- import java.io.IOException;
- import java.sql.Connection;
- import java.sql.ResultSet;
- import java.sql.SQLException;
- import java.sql.Statement;
- import java.util.ArrayList;
- import com.netkc.struts.datasource.DataSource;
- import com.netkc.struts.mapping.District;
- /**
- * @author SONG Yihan
- * @version 1.0
- * @date: Monday, April 09 2007
- */
- public class DistrictTree {
- private ArrayList
tree; - private Statement stmt1 = null;
- private Statement stmt2 = null;
- private Connection conn = null;
- private ResultSet rs = null;
- public DistrictTree() {
- try {
- conn = DataSource.getConnection();
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- public DistrictTree(Connection conn) {
- this.conn = conn;
- }
- public boolean addNode(String parentId, String districtId, String districtName) {
- try {
- String sql = "SELECT * FROM district WHERE districtId='" + parentId + "';";
- conn.setAutoCommit(false);
- stmt1 = conn.createStatement();
- rs = stmt1.executeQuery(sql);
- if(rs.next()) {
- int right = rs.getInt("rgt");
- sql = "UPDATE district SET lft=lft+2 WHERE lft>=" + right;
- stmt2.addBatch(sql);
- sql = "UPDATE district SET rgt=rgt+2 WHERE rgt>=" + right;
- stmt2.addBatch(sql);
- sql = "INSERT INTO district (districtId, districtName, lft, rgt) VALUES ('"+districtId + "', '" + districtName + "', " +
- (right) + ", " + (right+1) +");";
- stmt2.addBatch(sql);
- int[] flag = stmt2.executeBatch();
- if(flag[flag.length - 1] == 1) {
- conn.commit();
- return true;
- } else {
- conn.rollback();
- return false;
- }
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- }
- return false;
- }
- public boolean deleteNode(String districtId) {
- String sql = "SELECT * FROM district WHERE districtId='" + districtId + "';";
- try {
- conn.setAutoCommit(false);
- rs = stmt1.executeQuery(sql);
- if(rs.next())
- {
- int left = rs.getInt("lft");
- int right = rs.getInt("rgt");
- int count = (right - left - 1) / 2 + 1;
- int minus = count * 2;
- sql = "DELETE FROM district WHERE lft BETWEEN " + left + " AND " + right + ";";
- stmt2.addBatch(sql);
- sql = "UPDATE district SET lft=lft-" + minus + " WHERE lft>"+right;
- stmt2.addBatch(sql);
- sql = "UPDATE district SET rgt=rgt-" + minus + " WHERE rgt>"+right;
- stmt2.addBatch(sql);
- int flag = stmt2.executeBatch()[0];
- if(flag == 1) {
- conn.commit();
- return true;
- } else {
- conn.rollback();
- return false;
- }
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return false;
- }
- public void displayTree(String parentId) {
- }
- public ArrayList
getTree(String parentId) { - String sql = "SELECT * FROM district WHERE districtId='" + parentId + "';";
- try {
- stmt1 = conn.createStatement();
- rs = stmt1.executeQuery(sql);
- if(rs.next()) {
- int left = rs.getInt("lft");
- int right = rs.getInt("rgt");
- stmt2 = conn.createStatement();
- sql = "SELECT * FROM district WHERE lft BETWEEN " + left + " AND " + right + " ORDER BY lft ASC";
- rs = stmt2.executeQuery(sql);
- tree = new ArrayList
(); - District district = null;
- while(rs.next()) {
- district = new District();
- district.setDistrictId(rs.getString("districtId"));
- district.setDistrictName(rs.getString("districtName"));
- district.setLeft(rs.getInt("lft"));
- district.setRight(rs.getInt("rgt"));
- tree.add(district);
- }
- return tree;
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- try {
- if (rs != null) {
- rs.close();
- }
- stmt1.close();
- stmt2.close();
- conn.close();
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- return null;
- }
- public ArrayList
getChildren(String parentId) { - if(tree == null) {
- tree = getTree(parentId);
- }
- ArrayList
children = new ArrayList (); - int index = this.indexof(parentId);
- if(index != -1) {
- District d = tree.get(index);
- final int count = (d.getRight() - d.getLeft() - 1) / 2;
- for(int i = index + 1; i <= index + count; i++) {
- d = tree.get(i);
- children.add(d);
- i += (d.getRight() - d.getLeft() - 1) / 2;
- }
- }
- return children;
- }
- private int indexof(String districtId) {
- for(int index = 0; index < tree.size(); index++) {
- if(districtId.equals(tree.get(index).getDistrictId())) {
- return index;
- }
- }
- return -1;
- }
- public boolean updateNode(String districtId, String districtName) {
- String sql = "UPDATE district SET districtName='" + districtName + "' WHERE districtId='" + districtId + "';";
- try {
- conn.setAutoCommit(false);
- stmt1 = conn.createStatement();
- int flag = stmt1.executeUpdate(sql);
- if(flag == 1) {
- conn.commit();
- return true;
- }
- else {
- conn.rollback();
- return false;
- }
- } catch (SQLException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } finally {
- }
- return false;
- }
- }
這個最遺憾的地方就是在其中混雜了java代碼,這讓我及其不爽,但是我沒有找到向js傳值的好辦法:(
- function District(id, name, left, right) {
- this.id=id;
- this.name=name;
- this.left=left;
- this.right=right;
- };
- var tree = new Array();
- <%
- for(District d : tree) {
- %> tree.push(new District('<%=d.getDistrictId()%>', '<%=d.getDistrictName()%>', '<%=d.getLeft()%>', '<%=d.getRight()%>'));
- <%}%>
- Array.prototype.indexof = function(id) {
- for(var index = 0; index < tree.length; index++) {
- if(id == tree[index].id) {
- return index;
- }
- }
- return -1;
- };
- function citychanged(id){
- document.getElementById('district').length = 0;
- document.getElementById('area').length = 0;
- var districts = getChildren(id);
- for(var i = 0; i < districts.length; i++){
- document.getElementById('district').options[i] = new Option(districts[i].name, districts[i].id);
- }
- };
- function districtchanged(id) {
- document.getElementById('area').length = 0;
- var area = getChildren(id);
- for(var i = 0; i < area.length; i++){
- document.getElementById('area').options[i] = new Option(area[i].name, area[i].id);
- }
- };
- function getChildren(id) {
- if(tree.length == 0) {
- }
- var children = new Array();
- var index = tree.indexof(id);
- if(index != -1) {
- var d = tree[index];
- var count = (d.right - d.left - 1) / 2;
- for(var i = index + 1; i <= index + count; i++) {
- d = tree[i];
- children.push(d);
- i += (d.right - d.left - 1) / 2;
- }
- }
- return children;
- };
- "/fastfoodAct?method=query" styleId="queryFoodForm">
- searchType :
- city :
-
- district :
-
- area :
-
- restaurantName :
- searchType :
- foodName :
-
以上就是我解決三級聯動和層次結構的一點想法:)