Java 如何在 Hibernate 3.6 中正确级联保存主键上的一对一双向关系

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4027623/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 08:40:40  来源:igfitidea点击:

How do I properly cascade save a one-to-one, bidirectional relationship on primary key in Hibernate 3.6

javahibernateormjpajpa-2.0

提问by Mike Lively

I have an one-to-one, bidirectional entity relationship with shared keys. When I attempt to save the owner of the association I get a "null id generated" exception against the owned side of the relationship. I am utilizing hibernate-entitymanager and using spring for transaction management.

我与共享密钥有一对一的双向实体关系。当我尝试保存关联的所有者时,我收到针对关系拥有方的“生成空 ID”异常。我正在使用 hibernate-entitymanager 并使用 spring 进行事务管理。

Owning Entity

拥有实体

@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;
    }
}

Owned Entity

拥有的实体

@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;
    }
}

and the code below is being used to save the entity:

下面的代码用于保存实体:

LeadAffiliate aff = new LeadAffiliate();

aff.setLead(lead);
lead.setLeadAffiliate(aff);

em.persist(lead);

This all works perfectly fine in hibernate 3.5.0-Final. When attempting to upgrade to 3.5.6-Final or 3.6.0.Final is when I start getting the "null id generated for LeadAffiliate" error:

这一切在 hibernate 3.5.0-Final 中都可以正常工作。当我尝试升级到 3.5.6-Final 或 3.6.0.Final 时,我开始收到“为 LeadAffiliate 生成的 null id”错误:

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.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

As an aside, I am not sure that the annotations on Lead Affiliate were quite right to begin with. They worked, but seemed kind of kludgey. So I have since changed them to:

顺便说一句,我不确定关于 Lead Affiliate 的注释是否完全正确。他们工作了,但似乎有点笨拙。所以我已经将它们更改为:

@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;
    }
}

However, with these changes I get the same result. (Works in 3.5.0 but not 3.5.6 or 3.6.0)

但是,通过这些更改,我得到了相同的结果。(适用于 3.5.0 但不适用于 3.5.6 或 3.6.0)

Is there a new way I need to be doing this or is this a bug? My concern is that my code is currently working because of a bug :/.

有没有我需要这样做的新方法还是这是一个错误?我担心的是我的代码目前正在工作,因为一个错误:/。

采纳答案by axtavt

Specification says that derived entity should be the owning side of the relationship:

规范说派生实体应该是关系的拥有方:

2.4.1 Primary Keys Corresponding to Derived Identities

The identity of an entity may be derived from the identity of another entity (the "parent" entity) when the former entity (the "dependent" entity) is the owner of a many-to-one or one-to-one relationship to the parent entity and a foreign key maps the relationship from dependent to parent.

2.4.1 派生身份对应的主键

当前一个实体(“从属”实体)是多对一或一对一关系的所有者时,实体的身份可能源自另一个实体(“父”实体)的身份父实体和外键将关系从依赖映射到父实体。

In your case LeadAffiliateis derived, so it should be the owner, when Leadshould be marked as non-owning side by mappedBy. The following works in both 3.5.0 and 3.5.6:

在你的情况下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; 

    ...
}

回答by Pascal Thivent

My answer won't explain why things are working with Hibernate 3.5.0-Final but don't with 3.5.6-Final or 3.6.0.Final (and you should report this, I call this a regression).

我的回答不会解释为什么事情在 Hibernate 3.5.0-Final 中起作用,但在 3.5.6-Final 或 3.6.0.Final 中不起作用(你应该报告这个,我称之为回归)。

Anyway, derived identifiers are much better supported in JPA 2.0, in a standard way, and in your case, I think you could simply annotate your OneToOnerelation with an Idannotation.

无论如何,JPA 2.0 以标准方式更好地支持派生标识符,在您的情况下,我认为您可以简单地用注释来注释您的OneToOne关系Id

Update:As underlined by axtavt, when using a derived identifier, the "dependent" entity must be the owner of the relationship. So the full mapping for the dependent entity would be:

更新:正如 axtavt 所强调的,当使用派生标识符时,“依赖”实体必须是关系的所有者。因此,依赖实体的完整映射将是:

@Entity
@Table(name = "lead_affiliate")
public class LeadAffiliate {
    private Lead lead;

    @Id
    @OneToOne
    @JoinColumn(name="FK")
    public Lead getLead() {
        return lead;
    }
}

And the "parent" entity:

和“父”实体:

@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;
    }
}

This is a valid JPA 2.0 mapping, and works with EclipseLink. However, Hibernate doesn't like it and won't instantiate the EntityManagerFactory(dammit!).

这是一个有效的 JPA 2.0 映射,可与 EclipseLink 一起使用。但是,Hibernate 不喜欢它并且不会实例化EntityManagerFactory(该死的!)。

As workaround, you'll have to use the solution suggested by axtavti.e. to declare a primary key attribute as well asthe relationship attribute and to use MapsIdon the relationship attribute.

作为解决方法,您必须使用axtavt 建议解决方案,即声明主键属性以及关系属性并在关系属性上使用MapsId

But the above should work, there is IMO a bug in Hibernate (reported as HHH-5695).

但是以上应该可以工作,IMO 在 Hibernate 中有一个错误(报告为HHH-5695)。

References

参考