Подтвердить что ты не робот

Как смоделировать дерево с Hibernate?

У меня есть класс под названием "Домен". Каждый домен может иметь несколько поддоменов (одного типа).

Мне нужно определить поддомены и корневые домены. Поддомены могут иметь поддомены сами. Это может быть довольно много уровней.

Пример:

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

Как моделировать такой Java-класс с аннотациями Hibernate?

4b9b3361

Ответ 1

Моделирование было бы довольно простым:

@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;
}

Обратите внимание, что parent - это свойство, ответственное за запись базы данных, т.е. вам нужно установить parent для поддомена для сохраняемого отношения.

То, что не совсем тривиально, это запросы, поскольку SQL (и, следовательно, HQL и JPQL) не просто поддерживают запросы дерева. Спящий режим может сделать это, лениво загружая следующий уровень, но если вы хотите загрузить кучу уровней в одном запросе, то там, где это становится трудно.

Ответ 2

Если вы хотите использовать ленивую инициализацию Hibernate/JPA (это обычный случай), вы должны не использовать составной шаблон.

Связанная с Hibernate проблема с Composite Pattern заключается в следующем: в Composite у вас есть Composite, который имеет ссылку на дочерние компоненты. Но Компонент - это только абстрактный класс или Интерфейс, поэтому каждый Компонент - Лист или Композит. Если теперь вы используете ленивую инициализацию для набора композитов Composite, но затем некоторые из них должны сделать конкретный дочерний элемент Leaf или Composite, вы получите исключение Cast, потому что hibernate использует прокси-сервер для компонента, который нельзя отнести к Листу или Композитный.

Второй недостаток составного шаблона состоит в том, что каждый класс будет листом или композитом для его полного срока службы. Это нормально, если ваша структура никогда не меняется. Но это не сработает, если Лист должен стать композитным, потому что кто-то хочет добавить под node/leaf.


Итак, если у вас есть динамическая структура, я рекомендую один класс Node, который имеет двунаправленное реляционное взаимодействие между родительским и дочерним узлами. Отношения должны быть двунаправленными, если вам часто приходится переходить к родителям или детям в вашем коде. Поддержание этих отношений немного сложно, поэтому я решил опубликовать еще несколько кода.

@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();
    }
}

Ответ 3

Я предлагаю вам сначала получить модель OO только для Java, а затем беспокоиться о том, какие аннотации Hibernate вы используете. Если вы обнаружите, что вам нужно законно изменить свою модель для Hibernate, чтобы она вписывалась, вы всегда можете это сделать.

В конце концов, этот тип проблемы обычно решается с некоторым изменением Composite pattern (не фокусируйтесь слишком сильно на шаблоне вещь, только по структуре и идее.)

Использование реляционной базы данных lingo (довольно расслабленным образом):

а. Если ваши домены (корневые домены и поддомены) являются отношениями (коллекции n-кортежей в таблице без дубликатов и с заметным первичным ключом) и

В. ваши домены и субдомены имеют схожую структуру, , затем

С. Возможно, вы сможете сохранить их все в одной физической таблице, указав "родительский" внешний ключ, чтобы родительский FK одного кортежа отображался на первичный ключ другого.

Самое главное, что это рекурсивное отношение должно быть ацикличным. Как вы это делаете, структурно зависит от вашего проблемного домена (у вас есть один корневой домен или у вас есть несколько несвязанных корневых доменов?). Корневой домен можно обозначить либо с помощью NULL родительского внешнего ключа, либо с условием где корневой доменный корневой внешний ключ имеет собственный первичный ключ. Либо у кого есть плюсы и минусы (которые являются предметом типично глупых пламенных войн.)

Ответ 4

Существует несколько возможностей, в зависимости от того, какие операции необходимо выполнить.

Наиболее простой способ состоит в том, чтобы просто иметь ассоциацию "один-ко-многим" между родителями и дочерними элементами. В зависимости от того, что вам нужно сделать, выберите соответствующий тип: однонаправленный однонаправленный, однонаправленный, многонаправленный или двунаправленный.

Часто полезно, чтобы все узлы дерева имели отношение "много-к-одному" с корнем node. Это позволяет легко загружать целые деревья в одном запросе.