Почему @OneToMany не работает с наследованием в Hibernate
-
09-06-2019 - |
Вопрос
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Problem {
@ManyToOne
private Person person;
}
@Entity
@DiscriminatorValue("UP")
public class UglyProblem extends Problem {}
@Entity
public class Person {
@OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
Я думаю, довольно ясно, что я пытаюсь сделать. Я ожидаю, что @ManyToOne человек будет наследоваться классом UglyProblem. Но будет исключение, говорящее что-то вроде: «В классе UglyProblem такого свойства не найдено (mappedBy = " person ")".
Я нашел только этот . Я не смог найти пост Эммануила Бернарда, объясняющий причины этого. Р> <Ч>
К сожалению, в соответствии с документацией Hibernate " Свойства из суперклассов, которые не отображаются как @MappedSuperclass, игнорируются. "
Ну, я думаю, это означает, что если у меня есть эти два класса:
public class A {
private int foo;
}
@Entity
public class B extens A {
}
тогда поле foo
не будет отображено для класса B. Что имеет смысл. Но если у меня есть что-то вроде этого:
@Entity
public class Problem {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
@Entity
public class UglyProblem extends Problem {
private int levelOfUgliness;
public int getLevelOfUgliness() {
return levelOfUgliness;
}
public void setLevelOfUgliness(int levelOfUgliness) {
this.levelOfUgliness = levelOfUgliness;
}
}
Я ожидаю, что у класса UglyProblem будут поля id
и name
, и оба класса будут отображаться с использованием одной и той же таблицы. (На самом деле, это именно то, что происходит, я только что проверил еще раз). У меня есть эта таблица:
CREATE TABLE "problem" (
"DTYPE" varchar(31) NOT NULL,
"id" bigint(20) NOT NULL auto_increment,
"name" varchar(255) default NULL,
"levelOfUgliness" int(11) default NULL,
PRIMARY KEY ("id")
) AUTO_INCREMENT=2;
Возвращаясь к моему вопросу:
Я ожидаю, что @ManyToOne человек будет наследоваться классом UglyProblem.
Я ожидаю, что, поскольку все остальные сопоставленные поля наследуются, и я не вижу причин делать это исключение для отношений ManyToOne.
<Ч>Да, я видел это. На самом деле, я использовал решение только для чтения для моего случая. Но мой вопрос был "Почему ..." :). Я знаю, что есть объяснение, данное членом команды hibernate. Я не смог найти его, и поэтому я спросил.
Я хочу выяснить мотивы этого дизайнерского решения.
(если вам интересно, как я столкнулся с этой проблемой: я унаследовал проект, созданный с использованием hibernate 3. Это был Jboss 4.0. Что-то + hibernate уже было там (вы бы загрузили все это вместе). Я переносил этот проект в Jboss 4.2.2, и я обнаружил, что есть унаследованные отображения «@OneToMany mappedBy», и он отлично работал на старой установке ...)
Решение
Я думаю, что это мудрое решение, принятое командой Hibernate. Они могут быть менее надменными и прояснить, почему это было реализовано таким образом, но именно так работают Эммануэль, Крис и Гэвин. :) Р>
Давайте попробуем понять проблему. Я думаю, что ваши концепции "лживые". Сначала вы говорите, что многие проблемы связаны с людьми . Но затем вы говорите, что у одного человека есть много проблем с UglyProblem (и это не относится к другим проблемам ). Что-то не так с этим дизайном.
Представьте, как это будет отображаться в базе данных. У вас есть одно наследование таблицы, поэтому:
_____________
|__PROBLEMS__| |__PEOPLE__|
|id <PK> | | |
|person <FK> | -------->| |
|problemType | |_________ |
--------------
Как hibernate заставит базу данных сделать так, чтобы Проблема относилась к людям , только если ее problemType равен UP? Это очень сложная проблема для решения. Итак, если вы хотите такого рода отношения, каждый подкласс должен быть в своей таблице. Это то, что делает @MappedSuperclass
.
PS .: простите за уродливый рисунок: D
Другие советы
В моем случае я хотел использовать тип наследования SINGLE_TABLE, поэтому использование @MappedSuperclass было невозможным.
Что работает, хотя и не очень чисто, это добавить проприетарную версию Hibernate @ Where в ассоциации @OneToMany для принудительного ввода типа в запросах:
@OneToMany(mappedBy="person")
@Where(clause="DTYPE='UP'")
private List< UglyProblem > problems;
К сожалению, согласно документации по Hibernate " Свойства из суперклассов, которые не отображаются как @MappedSuperclass, игнорируются. & Quot; Я тоже столкнулся с этим. Мое решение состояло в том, чтобы представить желаемое наследование через интерфейсы, а не сами бины сущностей.
В вашем случае вы можете определить следующее:
public interface Problem {
public Person getPerson();
}
public interface UglyProblem extends Problem {
}
Затем реализуйте эти интерфейсы, используя абстрактный суперкласс и два подкласса сущности:
@MappedSuperclass
public abstract class AbstractProblemImpl implements Problem {
@ManyToOne
private Person person;
public Person getPerson() {
return person;
}
}
@Entity
public class ProblemImpl extends AbstractProblemImpl implements Problem {
}
@Entity
public class UglyProblemImpl extends AbstractProblemImpl implements UglyProblem {
}
Как дополнительное преимущество, если вы кодируете, используя интерфейсы, а не фактические сущностные компоненты, которые реализуют эти интерфейсы, это упрощает последующее изменение базовых отображений (меньше риск нарушения совместимости).
Я думаю, что вам нужно аннотировать свой суперкласс Проблема с помощью @ MappedSuperclass вместо @Entity .
Я понял, как решить проблему с OneToMany mappedBy. Р>
В производном классе UglyProblem из исходного поста. Метод обратного вызова должен находиться в производном классе, а не в родительском классе.
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@ForceDiscriminator
public class Problem {
}
@Entity
@DiscriminatorValue("UP")
public class UglyProblem extends Problem {
@ManyToOne
private Person person;
}
@Entity
public class Person {
@OneToMany(mappedBy="person")
private List< UglyProblem > problems;
}
По крайней мере, нашел секретный соус для использования Hibernate. http://docs.jboss.org/ hibernate / stable / annotations / api / org / hibernate / annotations / ForceDiscriminator.html @ForceDiscriminator заставляет @OneToMany уважать дискриминатор
Требуются аннотации в спящем режиме.