Странное поведение JPA «один ко многим» при попытке установить «много» для «одного» объекта - PullRequest
1 голос
/ 15 июня 2010

Я сопоставил две сущности, используя JPA (в частности, Hibernate). Эти сущности имеют отношение один ко многим (я упростил для представления):

@Entity
public class A {

    @ManyToOne
    public B getB() { return b; }
}

@Entity
public Class B {

    @OneToMany(mappedBy="b")
    public Set<A> getAs() { return as; }
}

Теперь я пытаюсь создать взаимосвязь между двумя экземплярами этих сущностей, используя установщик отношения «одна сторона / не является владельцем» (то есть таблица, на которую ссылаются):

em.getTransaction().begin();

A a = new A();
B b = new B();
Set<A> as = new HashSet<A>();
as.add(a);
b.setAs(as);

em.persist(a);
em.persist(b);
em.getTransaction().commit();

Но тогда связь с БД не сохраняется (строка, созданная для сущности А, не ссылается на строку, созданную для сущности Б). Почему это так? Я ожидал бы, что это сработает.

Также, если я удалю свойство "mappedBy" из аннотации @OneToMany, оно будет работать . Опять же - почему это так? и каковы возможные эффекты удаления свойства "mappedBy"?

1 Ответ

5 голосов
/ 15 июня 2010

В двунаправленных ассоциациях спецификация JPA определяется таким образом, что реализация просматривает ассоциативную сторону ассоциации, только когда она хочет увидеть текущее «состояние» ассоциации, чтобы определить, что необходимо сохранить, и каждая двунаправленная ассоциация имеетвладеющая и обратная сторона.Это может избежать двусмысленностей (то есть, что нужно сохранить, если обе стороны ассоциации не согласованы?), А также дать возможности для оптимизации и более простых реализаций в реализациях JPA.Обратите внимание, что это, как правило, не является проблемой, поскольку двунаправленные ассоциации должны поддерживаться вами, а не JPA.Когда вы всегда правильно поддерживаете двунаправленные ассоциации в своем приложении (обновляя обе стороны и сохраняя их согласованными), проблем не возникает.

OneToMany с mappedBy является обратной стороной, поэтому имплан JPA не смотрит на эту сторону, когдаопределение состояния ассоциации при очистке / фиксации транзакции.Он только смотрит на A.getB () и имеет значение null, поэтому для JPA ассоциация равна нулю.

OneToMany без mappedBy, то есть становится стороной-владельцем, поддерживается только с JPA 2.0, но я думаю, что Hibernateподдерживает это с возрастов.Вот почему ваш пример работает, если вы удалите mappedBy из OneToMany.В этом случае OneToMany становится стороной-владельцем, и, следовательно, реализация «смотрит» на эту сторону, чтобы определить, что следует сохранить.Это не меняет того факта, что ваша ассоциация в памяти все еще неполная.Вы должны установить обе стороны.

ОБНОВЛЕНИЕ: Я не знаю точно, что делает Hibernate, когда вы отключаете mappedBy с любой стороны, но я думаю, что это может привести к менее оптимальному SQL.См. Также: http://simoes.org/docs/hibernate-2.1/155.html, в частности, раздел об "обратном =" ложном "".«обратный» - это родной термин Hibernate для JPA «mappedBy».Таким образом, inverse = "true" в отображении Hibernate аналогично использованию mappedBy = "other" в отображении JPA, оба помечают эту сторону как обратную сторону, которая игнорируется при определении обновлений ассоциации.

...