Как я могу выразить полиморфную ассоциацию в JPA? - PullRequest
4 голосов
/ 26 июня 2009

A полиморфная ассоциация похожа на внешний ключ или отношение многие-к-одному, с той разницей, что целью может быть один из нескольких типов (классы в языке, таблицы в дб).

Я портирую дизайн базы данных, который использовал уже несколько лет, с PHP на Java. В старом коде я развернул свой собственный ORM, который не был оптимальным по ряду причин. Хотя я мог бы начать настраивать вещи позже и, возможно, в конечном итоге сам реализовать их заново, сейчас я хотел бы использовать готовые ORM и JPA для моих классов сущностей.

Теперь в макете базы данных есть одна вещь, которую я не знаю, как выразить в JPA:

У меня есть таблица Node и Edge, в которой хранится график (DAG, если это имеет значение). Каждый узел может опционально ссылаться на один другой объект из базы данных. Эти энтиты могут повторяться по всему графику несколько раз, а также могут быть «осиротевшие» энтиты, которые не будут доступны для пользователя, но которые могут иметь смысл сохранять хотя бы некоторое время.

Эти объекты вообще не связаны с точки зрения наследования и т. Д., Но имеют естественную иерархию, аналогичную Customer-> Site-> Floor-> Room. Фактически, много лет назад я начал с полей внешнего ключа, указывающих на «родительские» объекты. Однако эта иерархия недостаточно гибкая и начала разваливаться.

Например, я хочу разрешить пользователям группировать объекты в папках, некоторые объекты могут иметь несколько «родителей», а также отношения со временем меняются. Мне нужно следить за тем, как раньше были отношения, поэтому с ними у графа есть промежуток времени, который указывает, когда и когда этот край был действительным.

Ссылка от узла к объекту хранится в двух столбцах таблицы узлов, один содержит идентификатор в чужой таблице, другой - его имя. Например (некоторые столбцы опущены):

table Node:
+--------+-------+----------+
| ixNode | ixRef | sRefType |
+--------+-------+----------+
|    1   |  NULL |   NULL   |  <-- this is what a "folder" would look like
|    2   |   17  |  Source  |
|    3   |   58  |  Series  |  <-- there's seven types of related objects so far
+--------+-------+----------+

table Source (excerpt):
+----------+--------------------+
| ixSource |        sName       |
+----------+--------------------+
|    16    | 4th floor breaker  |
|    17    | 5th floor breaker  |
|    18    | 6th floor breaker  |
+----------+--------------------+

Возможно, решение отличается от использования JPA. Я мог бы что-то изменить в макете таблицы или представить новую таблицу и т. Д. Однако я уже много думал об этом, и структура таблицы мне кажется нормальной. Может быть, есть и третий способ, о котором я не думал.

Ответы [ 4 ]

5 голосов
/ 03 июля 2009

Ответ будет:

  • наследование (как уже предлагал Майк)
  • плюс @ DiscriminatorColumn для предоставления информации, в каком столбце хранится информация о том, какой подкласс следует использовать: sxRef. Единственное сомнение, которое я вижу, это то, что sxRef - это столбец , допускающий нулевое значение . Я думаю, что это запрещено.
5 голосов
/ 30 июня 2009

Я думаю, что вы уже нашли ответ. Создайте абстрактный класс (@Entity или @MappedSuperclass) и сделайте так, чтобы его расширяли разные типы.

Нечто подобное может работать

@MappedSuperclass
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Edge { 
    // . . .
    @OneToMany
    Collection<Node> nodes; 
}

@Entity 
public class Source extends Edge { 
}

@Entity public class Series extends Edge { 
}

@Entity
public class Node { 
    // . . .
    @ManyToOne
    Edge edge; 
}

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

InheritanceType.TABLE_PER_CLASS будет хранить Source и Series в отдельных таблицах (вы можете использовать SINGLE_TABLE, чтобы сделать что-то похожее на предыдущий ответ).

Если это не то, что вам нужно, многие провайдеры JPA предоставляют инструмент, который создает сопоставления на основе существующего набора таблиц. В OpenJPA это называется ReverseMappingTool [1]. Инструмент создаст исходные файлы Java, которые вы можете использовать в качестве отправной точки для ваших отображений. Я подозреваю, что Hibernate или EclipseLink имеют что-то похожее, но вы можете просто использовать OpenJPA и использовать определения сущностей с другим поставщиком (насколько мне известно, инструмент не генерирует какой-либо специфичный для OpenJPA код).

[1] http://openjpa.apache.org/builds/latest/docs/manual/manual.html#ref_guide_pc_reverse

2 голосов
/ 30 июня 2009

Вы смотрели на аннотацию @ Any ? Он не является частью JPA, но является расширением Hibernate Annotation.

0 голосов
/ 26 июня 2009

Сколько информации хранится в таблицах источников и серий? Это просто имя? Если это так, вы можете объединить их в одну таблицу и добавить столбец типа. Ваша таблица узлов потеряет свой sRefType, и у вас будет новая таблица, которая выглядит следующим образом:

ixSource        sName                  sType
  16            4th floor breaker      SOURCE
  17            5th floor breaker      SOURCE
  18            6th floor breaker      SOURCE
  19            1st floor widget       SERIES
  20            2nd floor widget       SERIES

Эта таблица заменит таблицы источников и серий. Источник и серия принадлежат к суперклассу? Это было бы естественное имя для этой таблицы.

...