Как мне правильно каскадно сохранить однозначное, двунаправленную связь на первичном ключ в Hibernate 3.6
Вопрос
У меня есть однозначное, двунаправленное отношение сущности с общими ключами. Когда я пытаюсь спасти владельца ассоциации, я получаю исключение «сгенерировал нулевый идентификатор» против собственной стороны отношений. Я использую Hibernate-Entitymanager и использую весну для управления транзакциями.
Владение сущностью
@Entity
@Table(name = "lead")
public class Lead
{
private Long leadId;
private LeadAffiliate leadAffiliate;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Long getLeadId()
{
return leadId;
}
@OneToOne(cascade = CascadeType.ALL)
@PrimaryKeyJoinColumn
public LeadAffiliate getLeadAffiliate()
{
return leadAffiliate;
}
}
Владеющее лицо
@Entity
@Table(name = "lead_affiliate")
public class LeadAffiliate
{
private Long leadId;
private Lead lead;
@Id
public Long getLeadId()
{
return leadId;
}
@MapsIdmappedBy = "leadAffiliate")
@OneToOne(cascade = CascadeType.All)
@PrimaryKeyJoinColumn
@JoinColumn(name = "lead_id")
public Lead getLead()
{
return lead;
}
}
И код ниже используется для сохранения объекта:
LeadAffiliate aff = new LeadAffiliate();
aff.setLead(lead);
lead.setLeadAffiliate(aff);
em.persist(lead);
Все это отлично работает в Hibernate 3.5.0-Final. При попытке обновить до 3.5.6-Final или 3.6.0.ffinal - это когда я начинаю получать «нулевый идентификатор, сгенерированный для Lendaffiliate» ошибки:
javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.sellingsource.bizdev.entities.LeadAffiliate
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1214)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1147)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1153)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:678)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:365)
at $Proxy152.persist(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:240)
at $Proxy120.persist(Unknown Source)
at com.sellingsource.common.dao.JpaGenericDao.create(JpaGenericDao.java:38)
... 64 more
Caused by: org.hibernate.id.IdentifierGenerationException: null id generated for:class com.sellingsource.bizdev.entities.LeadAffiliate
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:799)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:791)
at org.hibernate.engine.EJB3CascadingAction$1.cascade(EJB3CascadingAction.java:48)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:392)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:335)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:204)
at org.hibernate.engine.Cascade.cascade(Cascade.java:161)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:450)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:282)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:203)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:129)
at org.hibernate.ejb.event.EJB3PersistEventListener.saveWithGeneratedId(EJB3PersistEventListener.java:69)
at org.hibernate.event.def.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:179)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:135)
at org.hibernate.event.def.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:61)
at org.hibernate.impl.SessionImpl.firePersist(SessionImpl.java:808)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:782)
at org.hibernate.impl.SessionImpl.persist(SessionImpl.java:786)
at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:672)
... 77 more
Кроме того, я не уверен, что аннотации на филиале ведущи были совершенно правы с. Они работали, но казались вид ключей. Так что я с тех пор изменил их на:
@Entity
@Table(name = "lead_affiliate")
public class LeadAffiliate
{
private Long leadId;
private Lead lead;
@Id
@GenericGenerator(name = "foreign", strategy = "foreign", parameters = {
@org.hibernate.annotations.Parameter(name = "property", value="lead")
})
@GeneratedValue(generator = "foreign")
public Long getLeadId()
{
return leadId;
}
@OneToOne(mappedBy = "leadAffiliate")
@PrimaryKeyJoinColumn
public Lead getLead()
{
return lead;
}
}
Однако с этими изменениями я получаю тот же результат. (Работает в 3.5.0, но не 3.5.6 или 3.6.0)
Есть ли новый способ, которым я должен делать это или это ошибка? Моя проблема заключается в том, что мой код в настоящее время работает из-за ошибки: /.
Решение
Спецификация говорит, что производное сущность должна быть собственной стороной отношений:
2.4.1 Основные ключевые ключи, соответствующие производным тождествам
Личность субъекта может быть получена из идентичности другого объекта («родительский» субъект), когда прежний объект («зависимый» объект) является владельцем отношений многих к одному или однознаком к одному Родительская организация и внешний ключевые карты отображают отношения зависимыми родителями.
В твоем случае LeadAffiliate
получено, так что должно быть владельцем, когда Lead
следует пометить как некурящие стороны mappedBy
. Отказ Следующие работы в 3.5.0 и 3.5.6:
public class Lead {
@Id @GeneratedValue
private Long leadId;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "lead")
private LeadAffiliate leadAffiliate;
...
}
.
public class LeadAffiliate {
@Id
private Long leadId;
@OneToOne @MapsId
private Lead lead;
...
}
Другие советы
Мой ответ не объяснит, почему вещи работают с Hibernate 3.5.0-Final, но не с 3.5.6-Final или 3.6.0.ffinal (и вы должны сообщить об этом, я называю это регрессией).
В любом случае, производные идентификаторы намного лучше поддерживаются в JPA 2.0, и в вашем случае, я думаю, что вы могли бы просто аннотировать OneToOne
отношение с А.Н. Id
Аннотация.
Обновлять: Как подчеркивается Axtavt, при использовании производного идентификатора «зависимая» сущность должна быть владельцем отношений. Таким образом, полное сопоставление для зависимого лица будет:
@Entity
@Table(name = "lead_affiliate")
public class LeadAffiliate {
private Lead lead;
@Id
@OneToOne
@JoinColumn(name="FK")
public Lead getLead() {
return lead;
}
}
И «родительский» сущность:
@Entity
@Table(name = "lead")
public class Lead {
private Long leadId;
private LeadAffiliate leadAffiliate;
@Id @GeneratedValue(strategy = GenerationType.AUTO)
public Long getLeadId() {
return leadId;
}
@OneToOne(cascade = CascadeType.ALL, mappedBy="lead")
public LeadAffiliate getLeadAffiliate() {
return leadAffiliate;
}
}
Это действительное отображение JPA 2.0, и работает с EclipseLink. Отказ Тем не менее, гибернат не нравится, и не будет создавать EntityManagerFactory
(Даммит!).
Как обходной путь, вам придется использовать Решение, предложенное axtavt т.е. объявить атрибут первичного ключа также как и атрибут отношений и использовать MapsId
на атрибуте отношений.
Но приведенное выше должно работать, есть ошибка в гибернации (сообщается как HHH-5695.).
Рекомендации
- Спецификация JPA 2.0
- Раздел 2.4.1 «Первичные ключевые ключи, соответствующие производным тождествам» (длительный, охватывает много случаев)
- JPA Wikibook.