Почему @OneToMany не работает с наследованием в Hibernate

StackOverflow https://stackoverflow.com/questions/38057

Вопрос

@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 уважать дискриминатор

Требуются аннотации в спящем режиме.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top