2016-03-14 43 views
0

我有一個表在我的數據庫是這樣的:Contruct樹從表

enter image description here

等等...

正如你可以看到有幾根父母(其中有那些沒有parent_id),每個類別都有n個孩子。

我想用這個類將其轉換爲一個樹形結構中的Java:

private int id; 
private String name; 
private int parent; 
private List<Category> children; 

我用這個查詢得到的數據,我認爲這可能是改善:

SELECT c.*, ca.name, NVL(ca.parent_id, -1) AS parent_id FROM 
( 
    SELECT id, name, parent_id FROM categories 
) ca, 
( 
    SELECT LISTAGG(id || ':' || name || ':' || DECODE(parent_id, NULL, 
    DECODE(id, NULL, NULL, -1), parent_id), ';') 
    WITHIN GROUP (ORDER BY id) AS children, parent_id AS id 
    FROM categories 
    GROUP BY parent_id HAVING parent_id IS NOT NULL 
) c 
WHERE c.id = ca.id 

每次都遇到類別(id,name和parent_id)以及一個包含子元素的字符串。

然後i循環扔每結果集

List<Category> categories = new ArrayList<Category>(); 

while (rs.next()) { 
    Category c = new Category(); 
    c = JdbcToModel.convertToCategory(rs); // 
    if (c.getParent() == -1) { // parent_id is null in database 
     categories.add(c); 
    else { 
     categories = JdbcToModel.addCategoryToTree(categories, c); 

    } 
} 

方法convertToCategory

公共靜態類別convertToCategory(結果集RS){

Category toRet = new Category(); 
List<Category> children = new ArrayList<Category>(); 
try {  
    children = parseCategoriesFromReview(rs.getString("children")); 
    toRet.setId(rs.getInt("id")); 
    toRet.setName(rs.getString("name")); 
    toRet.setParent(rs.getInt("parent_id")); 
    toRet.setChildren(children); 

} catch (Exception e) { 
    e.printStackTrace(); 
} 
return toRet; 

}

的方法parseCategoriesFromReview當我孩子的解析的字符串:

public static List<Category> parseCategoriesFromReview(String categoriesString) { 

     List<Category> toRet = new ArrayList<Category>(); 
     try {  
      if (!categoriesString.equals("::")) { 

       String [] categs = categoriesString.split(";"); 
       for (String categ : categs) { 
        String [] category = categ.split(":"); 
        Category c = new Category(Integer.parseInt(category[0]), category[1], Integer.parseInt(category[2]), new ArrayList<Category>()); 
        toRet.add(c); 
       } 
      } 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     return toRet; 
    } 

和遞歸方法addCategoryToTree

public static List<Category> addCategoryToTree(List<Category> categories, Category c) { 
    try {  
     for (Category ct : categories) { 
      if (ct.getId() == c.getParent()) { 
       ct.getChildren().add(c); 
       break; 
      } else { 
       return addCategoryToTree(ct.getChildren(), c); 
      } 
     } 

    } catch (Exception e) { 
     e.printStackTrace(); 
    } 
    return categories; 

} 

我認爲最大的問題是這種方法。我從未寫過一遞歸方法,裏面有一個循環,我不知道它是否正確。關鍵是我得到了一個樹結構,但只有幾個類別。最後一棵樹沒有那麼多。

也許我做複雜的事情,但我不知道如何做到這一點的另一種方式..

任何人的幫助?

問候!

回答

1

甲骨文設置

CREATE TABLE categories (id, name, parent_id) AS 
SELECT 1, 'Restauracion', NULL FROM DUAL UNION ALL 
SELECT 2, 'Desayuno',  1 FROM DUAL UNION ALL 
SELECT 3, 'Calidad',   2 FROM DUAL UNION ALL 
SELECT 4, 'Organizacion', 2 FROM DUAL UNION ALL 
SELECT 5, 'Variedad',  2 FROM DUAL UNION ALL 
SELECT 6, 'Personal',  NULL FROM DUAL UNION ALL 
SELECT 7, 'Pisos',   6 FROM DUAL UNION ALL 
SELECT 8, 'Falta de Personal', 7 FROM DUAL UNION ALL 
SELECT 9, 'Trato',   7 FROM DUAL UNION ALL 
SELECT 10, 'Informacion',  7 FROM DUAL UNION ALL 
SELECT 11, 'Idiomas',   7 FROM DUAL UNION ALL 
SELECT 12, 'Otros',   7 FROM DUAL; 

的Java

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.util.ArrayList; 
import java.util.HashMap; 

public class Category { 
    private final String name; 
    private final int id; 
    private final Category parent; 
    private final ArrayList<Category> children = new ArrayList<>(); 

    private Category(final String name, final int id, final Category parent) { 
     this.name = name; 
     this.id  = id; 
     this.parent = parent; 
     if (parent != null) 
      parent.children.add(this); 
    } 

    @Override 
    public String toString(){ 
     final StringBuffer buffer = new StringBuffer(); 
     buffer.append('<'); 
     buffer.append(name); 
     buffer.append(':'); 
     buffer.append(id); 
     buffer.append(':'); 
     buffer.append(parent == null ? "" : parent.name); 
     buffer.append('>'); 
     return buffer.toString(); 
    } 

    public String toHierarchyString(){ 
     return toHierarchyString(0); 
    } 

    private String toHierarchyString(int level){ 
     final StringBuffer buffer = new StringBuffer(); 
     for (int i = 0; i < level; i++) 
      buffer.append('\t'); 
     buffer.append(toString()); 
     buffer.append('\n'); 
     for (final Category child : children) 
      buffer.append(child.toHierarchyString(level+1)); 
     return buffer.toString(); 
    } 
    public static ArrayList<Category> loadCategoriesFromDatabase(){ 
     try{ 

      Class.forName("oracle.jdbc.OracleDriver"); 

      final Connection con = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE","TEST","TEST"); 
      final PreparedStatement st = con.prepareStatement(
        "SELECT id, name, parent_id " + 
        "FROM categories " + 
        "START WITH parent_id IS NULL " + 
        "CONNECT BY PRIOR id = PARENT_ID " + 
        "ORDER SIBLINGS BY name" 
      ); 

      final ResultSet cursor = st.executeQuery(); 

      final HashMap<Integer,Category> categoryMap = new HashMap<>(); 
      final ArrayList<Category> categories = new ArrayList<>(); 

      while (cursor.next()) 
      { 
       final String name  = cursor.getString("NAME"); 
       final int id   = cursor.getInt("ID"); 
       final Integer parent_id = cursor.getInt("PARENT_ID"); 
       final Category parent = categoryMap.get(parent_id); 
       final Category category = new Category(name, id, parent); 
       categoryMap.put(id, category); 
       if (parent == null) 
        categories.add(category); 
      } 
      return categories; 
     } catch(ClassNotFoundException | SQLException e) { 
      System.out.println(e); 
     } 
     return null; 
    } 

    public static void main(final String[] args){ 
     ArrayList<Category> categories = loadCategoriesFromDatabase(); 
     for (final Category cat : categories) 
      System.out.println(cat.toHierarchyString()); 
    } 
} 

輸出

<Personal:6:> 
    <Pisos:7:Personal> 
     <Falta de Personal:8:Pisos> 
     <Idiomas:11:Pisos> 
     <Informacion:10:Pisos> 
     <Otros:12:Pisos> 
     <Trato:9:Pisos> 

<Restauracion:1:> 
    <Desayuno:2:Restauracion> 
     <Calidad:3:Desayuno> 
     <Organizacion:4:Desayuno> 
     <Variedad:5:Desayuno> 
+0

它就像一個魅力!那是我想要的。非常感謝MT0!「 – Light1988

0

我相信你的問題在於結果的排序。你的代碼假設,所有的孩子ID都有一個比所有父母ID更高的ID,根本不需要。例如。如果你有一個類別(id = 5,parent = 10),那麼它會遞歸當前樹(包含類別id的0到4)。它不會找到正確的父類別,並且在這種情況下(取決於類別類中的children字段是否初始化爲null或空列表)將打印異常的堆棧跟蹤,或者只是遍歷空的for循環所有的葉子類別,什麼都不做。

要從這樣的數據結構構建樹,您可能需要兩階段方法。一個我喜歡(因爲它是相當簡單的,雖然不是真正的高性能)將是:

  • 通過你的DB結果項初始化HashMap<Integer, List<Category>>
  • 環路,做mapFromAbove.get(category.getId()).add(category)(你還需要初始化這些名單)
  • 完成後,執行mapFromAbove.get(0),並對結果進行迭代,對於每個結果,從相同哈希映射中獲取子項並將其添加到它們。然後遞歸執行此操作。

這實際上很容易實現。

+0

確定。我理解前兩點,但最後一點我迷路了。 – Light1988

+0

Map > categories = new HashMap >(); categories.put(category.getId(),new ArrayList ()); categories.get(category.getId())。add(category))但我迷失在最後一個。當你說mapFromAbove.get(0)你是什麼意思???它應該是一個變量我嗎? – Light1988

+0

mapFromAbove.get(0)會返回所有父類爲Id的類別(它們都是根類別的所有類別) –