Почему @OneToMany не работает с наследованием в Hibernate - PullRequest
5 голосов
/ 01 сентября 2008
@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", и он работал нормально на старой установке ...)

Ответы [ 5 ]

6 голосов
/ 29 сентября 2014

В моем случае я хотел использовать тип наследования SINGLE_TABLE, поэтому использование @MappedSuperclass было невозможным.

Что работает, хотя и не очень чисто, так это добавить проприетарное предложение Hibernate @ Where к ассоциации @OneToMany для принудительного ввода типа в запросах:

@OneToMany(mappedBy="person")
@Where(clause="DTYPE='UP'")
private List< UglyProblem > problems;
5 голосов
/ 02 сентября 2008

К сожалению, в соответствии с документацией Hibernate "Свойства из суперклассов, не отображенных как @MappedSuperclass, игнорируются". Я тоже столкнулся с этим. Мое решение состояло в том, чтобы представить желаемое наследование через интерфейсы, а не сами бины сущностей.

В вашем случае вы можете определить следующее:

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 {
}

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

4 голосов
/ 10 сентября 2008

Я думаю, что это мудрое решение, принятое командой Hibernate. Они могут быть менее надменными и прояснить, почему это было реализовано таким образом, но именно так работают Эммануэль, Крис и Гэвин. :)

Давайте попробуем разобраться в проблеме. Я думаю, что ваши понятия "лгут". Сначала вы говорите, что многие проблемы s связаны с людьми . Но затем вы говорите, что у одного Человека есть много Уродливых проблем с (и это не относится к другим Проблемам с). Что-то не так с этим дизайном.

Представьте, как это будет отображаться в базе данных. У вас есть одно наследование таблицы, поэтому:

          _____________
          |__PROBLEMS__|          |__PEOPLE__|
          |id <PK>     |          |          |
          |person <FK> | -------->|          |
          |problemType |          |_________ |
          -------------- 

Как hibernate собирается заставить базу данных сделать Проблема относится только к Людям , если ее problemType равен UP? Это очень сложная проблема для решения. Итак, если вы хотите такого рода отношения, каждый подкласс должен быть в своей таблице. Вот что делает @MappedSuperclass.

PS: простите за уродливый рисунок: D

1 голос
/ 01 сентября 2008

Я думаю, что вам нужно аннотировать проблему суперкласса с @ MappedSuperclass вместо @Entity.

0 голосов
/ 17 декабря 2009

Я понял, как решить проблему с 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 соблюдать дискриминатор

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

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