2011-08-15 27 views
13

Tôi có một lớp được gọi là "Miền". Mỗi Miền có thể có nhiều tên miền phụ (cùng loại).Làm thế nào để mô hình hóa một cây với Hibernate?

Tôi cần có khả năng xác định tên miền phụ và tên miền gốc. Các tên miền phụ có thể có các tên miền phụ. Đây có thể là một vài cấp độ sâu.

Ví dụ:

Rootdomain 
|- Subdomain 1 
| |- Subdomain 2 
| | 
| |- Subdomain 3 
| 
|- Subdomain 4 
| |- Subdomain 5 

Làm thế nào để mô hình như một Java-class với các chú thích Hibernate?

Trả lời

14

Các người mẫu sẽ là khá đơn giản:

@Entity 
class Domain { 
    @ManyToOne //add column definitions as needed 
    private Domain parent;  //each Domain with parent==null is a root domain, all others are subdomains 

    @OneToMany //add column definitions as needed 
    private List<Domain> subdomains; 
} 

Lưu ý rằng parent là tài sản chịu trách nhiệm về sự xâm nhập cơ sở dữ liệu, ví dụ: bạn cần phải thiết lập parent cho một tên miền phụ cho mối quan hệ được lưu trữ.

Điều không phải là khá nhỏ là các truy vấn, vì SQL (và do đó HQL và JPQL) không dễ dàng hỗ trợ truy vấn cây. Hibernate có thể làm điều này bằng cách lười biếng tải cấp độ tiếp theo nhưng nếu bạn muốn tải một loạt các cấp trong một truy vấn, đó là nơi nó trở nên khó khăn.

+0

Tốt, ngắn và chính xác. –

+0

Tôi sẽ không khuyên bạn nên sử dụng Danh sách. Hibernate không thể đồng thời lấy nhiều túi (danh sách). Nếu bạn cố gắng tải 3 cấp độ của cây này trong cùng một truy vấn, bạn sẽ cần phải sử dụng Đặt thay thế. – Dherik

+1

@Dherik bạn nói đúng. Sử dụng một danh sách nên có thể với một chỉ mục mặc dù. – Thomas

1

Có một số khả năng, tùy thuộc vào loại hoạt động bạn cần thực hiện.

Đơn giản nhất là chỉ cần có liên kết một-nhiều-cha-con. Tùy thuộc vào những gì bạn cần làm, hãy chọn loại thích hợp: một chiều, một chiều, nhiều chiều một chiều hoặc hai chiều.

Thường hữu ích khi có tất cả các nút của cây có mối quan hệ nhiều-với-một với nút gốc. Điều này cho phép tải toàn bộ cây một cách dễ dàng, trong một truy vấn duy nhất.

2

Tôi khuyên bạn trước hết nên lấy mô hình OO chỉ có Java và sau đó lo lắng về những chú thích Hibernate bạn sử dụng. Nếu bạn thấy mình cần hợp pháp thay đổi mô hình của mình cho Hibernate để phù hợp, bạn luôn có thể làm như vậy.

Cuối cùng, vấn đề kiểu này thường được giải quyết với một số biến thể của Composite pattern (không tập trung quá nhiều vào điều mẫu, chỉ cần vào cấu trúc và ý tưởng đằng sau nó.)

Sử dụng cơ sở dữ liệu quan hệ biệt ngữ (một cách khá thoải mái):

A. Nếu tên miền của bạn (tên miền gốc và tên miền phụ) là quan hệ (bộ sưu tập của n-bản ghi trong một bảng không có bản sao và với một khóa chính thấy rõ)

B. tên miền của bạn và tên miền phụ có cấu trúc tương tự, sau đó

C. Bạn có thể có thể lưu trữ tất cả trong số họ trong bảng vật lý như nhau bằng cách định nghĩa một "cha mẹ" chính nước ngoài như vậy FK cha của một bản đồ tuple để khóa chính của một số khác.

Quan trọng nhất, mối quan hệ đệ quy này phải theo chu kỳ. Làm thế nào bạn đi về nó cấu trúc là đến miền vấn đề của bạn (bạn có một tên miền gốc, hoặc bạn có thể có nhiều, tên miền gốc không liên quan?) Một tên miền gốc có thể được ký hiệu bằng cách có một khóa ngoại lệ cha mẹ NULL, hoặc với một điều kiện trong đó khóa ngoại của cha mẹ của tên miền gốc bằng khóa chính của nó. Một trong hai có ưu và khuyết điểm (là chủ đề của cuộc chiến tranh ngọn lửa ngu ngốc.)

+1

Tôi rất không đồng ý với việc sử dụng mẫu Composite (xem câu trả lời của tôi để biết thêm chi tiết). Nhưng nếu bạn giải quyết vấn đề đó với "một số biến thể" thì bạn nên giải thích sự thay đổi đó trong một số chi tiết. – Ralph

+0

Biến thể mà tôi nói đến là bằng cách có tất cả các lá và vật liệu tổng hợp có cùng loại bê tông (xem câu trả lời của Thomas để biết chi tiết.) Tất nhiên, việc diễn giải nghiêm ngặt (và ngây thơ) về "mô hình tổng hợp" sẽ dẫn đến nhiều vấn đề (độc lập với hibernate trong hình.) Và thực tế là tất cả "kosher", bởi việc thực hiện bởi các mẫu cũng sẽ chạy vào các vấn đề. Tên "tổng hợp" không được gợi ra các khải tượng về các loại lá và loại hỗn hợp hoàn toàn khác nhau nhưng thay vì * mọi thứ * cũng có thể bao gồm các thứ khác, có thể là loại * tương đương *. –

9

Nếu bạn muốn sử dụng khởi tạo lười biếng Hibernate/JPA (trường hợp bình thường) thì bạn nên không sử dụng mẫu tổng hợp.

Vấn đề liên quan đến Hibernate với Composite Pattern là: trong một Composite, bạn có một Composite có tham chiếu thành phần con của nó. Nhưng Component chỉ là một lớp trừu tượng hoặc Interface, vì vậy mỗi Component là một Leaf hoặc một Composite. Nếu bây giờ bạn sử dụng khởi tạo lười cho tập hợp Composite, nhưng sau đó một số cách cần đưa một con cụ thể vào Leaf hoặc Composite, bạn sẽ nhận được một ngoại lệ Cast vì hibernate sử dụng proxy cho thành phần không thể được đưa vào Leaf hoặc Composite.

Hạn chế thứ hai của mẫu hỗn hợp là mọi lớp sẽ là Lá hoặc Hỗn hợp cho toàn bộ thời gian tồn tại của nó. Điều đó là tốt nếu cấu trúc của bạn không bao giờ thay đổi. Nhưng nó sẽ không hoạt động nếu Leaf phải trở thành một Composite vì ai đó muốn thêm một nút con/lá.


Vì vậy, nếu bạn có cấu trúc động, tôi đề xuất một lớp Nút có mối quan hệ hai chiều giữa nút cha và nút con. Mối quan hệ phải là hai chiều nếu bạn thường cần điều hướng đến cha mẹ hoặc trẻ em trong mã của bạn. Duy trì mối quan hệ đó là một chút phức tạp, vì vậy tôi quyết định đăng thêm một số mã.

@Entity 
public class Domain { 

    @Id 
    private long id; 

    /** The parent domain, can be null if this is the root domain. */ 
    @ManyToOne 
    private Domain parent; 

    /** 
    * The children domain of this domain. 
    * 
    * This is the inverse side of the parent relation. 
    * 
    * <strong>It is the children responsibility to manage there parents children set!</strong> 
    */ 
    @NotNull 
    @OneToMany(mappedBy = "parent") 
    private Set<Domain> children = new HashSet<Domain>(); 
    /** 
    * Do not use this Constructor! 
    * Used only by Hibernate. 
    */ 
    Domain() { 
    } 

    /** 
    * Instantiates a new domain. 
    * The domain will be of the same state like the parent domain. 
    * 
    * @param parent the parent domain 
    * @see Domain#createRoot() 
    */ 
    public Domain(final Domain parent) { 
     if(parent==null) throw new IllegalArgumentException("parent required"); 

     this.parent = parent; 
     registerInParentsChilds(); 
    } 

    /** Register this domain in the child list of its parent. */ 
    private void registerInParentsChilds() { 
     this.parent.children.add(this); 
    } 

    /** 
    * Return the <strong>unmodifiable</strong> children of this domain. 
    * 
    * @return the child nodes. 
    */ 
    public Set<Domain> getChildren() { 
     return Collections.unmodifiableSet(this.children); 
    }   

    /** 
    * Move this domain to an new parent domain. 
    * 
    * @param newParent the new parent 
    */ 
    public void move(final Domain newParent) { 
     Check.notNullArgument(newParent, "newParent"); 

     if (!isProperMoveTarget(newParent) /* detect circles... */) { 
      throw new IllegalArgumentException("move", "not a proper new parent", this); 
     } 

     this.parent.children.remove(this); 
     this.parent = newParent; 
     registerInParentsChilds(); 
    } 

    /** 
    * Creates the root. 
    * 
    * @param bid the bid 
    * @return the domain 
    */ 
    public static Domain createRoot() { 
     return new Domain(); 
    } 
} 
+0

Khi tôi xóa một nút, điều đó có xóa tất cả các con không? Điều gì về thuộc tính 'nghịch đảo' và' xếp tầng' để cho phép lưu một cây trong một lần? –

+0

@Aaron Digulla: Câu trả lời của tôi không đề cập đến chủ đề này vào lúc này. Vì vậy, tại thời điểm này nó không xóa các childs nếu bạn xóa parant của nó. Nhưng bạn sẽ có thể đạt được điều đó bằng cách xóa tầng. Tôi không tin rằng bạn cần thuộc tính 'nghịch đảo' để xóa. – Ralph

+0

Tôi thấy ít nhất 2 Phiên bản này nhưng tôi tự hỏi cái nào tốt hơn: Bạn cũng có thể thêm Chú thích '@JoinColumn (name =" parent_id ")' nhưng như tôi đã nói ở trên, tôi không biết các lợi ích là gì. – Zarathustra

Các vấn đề liên quan