Полиморфизм гибернации

Это вопрос полиморфизма Гибнера и дизайн модели данных вопрос; они переплетены. Я использовал Hibernate в прошлом, и наслаждались им, но иногда мне трудно думать ничего, кроме тривиальных конструкций. Не стук в спящий режим; просто что ОРМ вообще может быть сложным.

Я думаю, что это вопрос Hibernate 101, но я не уверен. То, что я пытаюсь достичь, возможно даже не возможно.

У меня есть абстрактный класс Fruit, который будет подклассифицирован в Apple и оранжевый. У меня есть класс Note, который представляет заметки или комментарии о яблоках и апельсинах. Apple или Orange могут иметь много Notes связанных с ним, но только один Apple или Orange когда-либо будут связанные с данным примечанием.

Вот эскизы классов, где я пока что не указываю, где объект id go и свойства яблок, которые различают их из Апельсинов. В настоящее время я не чувствую решимости о том, какую стратегию наследования Hibernate я использую.

abstract public class Fruit {
}

// Apples have notes written about them:
public class Apple extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}


// Oranges have notes written about them:
public class Orange extends Fruit {
   private Set<Note> note;
   ...

   @OneToMany(cascade = CascadeType.ALL)
   public Set<Note> getNote() {
       return note;
   }
}

Ниже приведен класс Note, в котором мы видим, что он имеет поля для Apple и Orange. Недостаток или неэффективность в этом проекте заключается в том, что экземпляр одной заметки будет указывать только на один из Apple или Orange, и оба они оба. Поэтому, если примечание привязано к Apple, оранжевое поле является излишним и неприглядным, и семафоре.

// A note about an Apple or Orange
public class Note {
   private String theNote;
   private Apple apple; 
   private Orange orange;
   ...

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "apple_id")
   public Apple getApple() { 
       return apple;
   }

   // with the usual many to one mapping
   @ManyToOne
   @JoinColumn(name = "orange_id")
   public Orange getOrange() {
       return orange;
   }

   ...
}

Тем не менее, это класс Note, который, как мне кажется, я хочу основать дизайн, но я не уверен, как думать об этом с уважением для аннотации Hibernate и отображения таблицы:

// A note about a fruit:
public class Note {
   private String theNote;
   private Fruit fruit;
   ...
}

после чего фрукты будут либо экземпляром Apple, либо апельсином.

Может ли этот последний класс Note со ссылкой на Fruit, который фактически будет содержать Apple или Orange, даже примирится с Hibernate ORM mapping? Если да, может кто-нибудь, пожалуйста, поговорите о том, как.

Ответ 1

Это абсолютно возможно. Вы можете связать заметки с абстрактным классом Fruit вместо того, чтобы повторять их в каждой из реализаций:

@Entity
@Inheritance
public abstract class Fruit {
   private Set<Note> notes;
   ...

   @OneToMany(cascade = CascadeType.ALL, mappedBy = "fruit")
   public Set<Note> getNotes() {
       return notes;
   }
}

@Entity
public class Apple extends Fruit {
   ...
}


@Entity
public class Orange extends Fruit {
   ...
}

@Entity
public class Note {
   private String theNote;

   @ManyToOne
   private Fruit fruit;
   ...
}

Ét voilà!

- добавление на основе комментария: JPA предоставляет множество стратегий для работы с наследованием. соответствующий раздел в учебнике по Java EE должен помочь вам начать работу.

В основном, ваши варианты:

  • Сохранение всего в одной таблице и использование столбца дискриминатора, чтобы узнать, какая строка является типом
  • Сохранение каждого конкретного класса (Apple и Orange) в отдельной таблице
  • Наличие общей таблицы фруктов со столбцом дискриминатора, а таблицы Apple и Orange с внешним ключом в таблице Fruit.

Другое редактирование: Заметил, что это спящий режим, а не вопрос JPA. Однако не имеет особого значения, поскольку параметры одинаковы. Здесь соответствующий раздел в документах Hibernate.

Ответ 2

Этот шаблон очень распространен в базе данных на базе Hibernate. Как это работает внутри, в значительной степени зависит от того, какая стратегия наследования используется.

При использовании таблицы для каждого класса или таблицы для наследования подкласса будет создана таблица для каждого подкласса/класса. Например, в вашем случае у вас будет таблица Fruit и две таблицы Apple и Orange, с ссылками на внешние ключи между Fruit и Apple/Orange. При запросе одного фрукта (будь то яблоко или апельсин) по ID, Hibernate будет нарушать соединение с таблицей Fruit с таблицей Apple и Orange. Каждая строка будет преобразована в Apple или Orange, в зависимости от того, из какой таблицы были получены поля.

Другая возможность - использовать дискриминаторы. Будет использоваться одна таблица Fruit, которая будет содержать поле дискриминатора (например, fruit_type, принимающее значения Apple и Orange). В зависимости от значения этого поля Hibernate определит, является ли соответствующий объект Apple или Orange.

В вашем случае, в случае активной загрузки, когда Hibernate загружает объект Note, он с нетерпением будет получать соответствующий Fruit и заполнять поле фруктов экземпляром Apple или Orange соответственно.

В случае ленивой выборки поле фруктов будет прокси-сервером, реализующим интерфейс Fruit. Пока не загрузится фактическое поле плодов, его тип не определен.

Надеюсь, что это ответит на некоторые ваши запросы.