具有反向引用的Hibernate @OneToOne映射和所有者的CascadeType.ALL不会使子进程持久化

时间:2011-03-08 00:55:47

标签: mysql hibernate mapping one-to-one

我们有以下实体关系。用户是父实体.DistrictUserDetail是子实体。

@Entity
@Table(name="USERS")
public class User implements Serializable {

@Id
@Column(name="ID", nullable=false)
@GeneratedValue(generator="system-uuid")
@GenericGenerator(name="system-uuid", strategy = "uuid")
private String id;

    @OneToOne(cascade= {CascadeType.ALL})
    @PrimaryKeyJoinColumn(name="ID", referencedColumnName="USER_ID")
    public DistrictUserDetail districtUserDetail;

    ... other properties..

这是父实体。 子实体主键是User实体的外键,我们将其关键属性定义为:

@Entity
@Table(name="DISTRICT_USER_DETAIL")
public class DistrictUserDetail implements Serializable{



@Id  
@Column(name="USER_ID")  
@GeneratedValue(generator="foreign")   
@GenericGenerator(name="foreign", strategy = "foreign",   
parameters={@Parameter(name="property",value="user")})  
protected String userId;  

@OneToOne(mappedBy="districtUserDetail")  
User user; 

    ... other properties

因此,当我们创建一个新用户,并创建一个新的DistrictUser,并将DistrictUser设置为User并保存用户时,我们希望保存用户,并且由于cascadeAll,还保存了DistrictUser。

这是我们的测试:

public void testSaveUserWithDistrictUserDetails() throws Exception {
    User user = new User();
    user.setFirstName("John");
    user.setLastName("Doe");
    user.setUserName("U11111");


    DistrictUserDetail userDetail = new DistrictUserDetail();

    userDetail.setIsLoginAllowed(Boolean.TRUE);
    userDetail.setIsSuperintendent(Boolean.TRUE);

    user.setDistrictUserDetail(userDetail);

    User newUser = dao.save(user);

    assertTrue(newUser!=null);
    assertTrue(newUser.getId()!=null);
}

但我们遇到以下hibernate异常:

Caused by: org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: user
at org.hibernate.id.ForeignGenerator.generate(ForeignGenerator.java:44)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:99)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:315)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:283)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:238)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:688)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:670)
at org.hibernate.engine.CascadingAction$6.cascade(CascadingAction.java:245)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:269)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:217)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:170)
at org.hibernate.engine.Cascade.cascade(Cascade.java:131)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:456)
at org.hibernate.event.def.DefaultMergeEventListener.mergeTransientEntity(DefaultMergeEventListener.java:352)
at org.hibernate.event.def.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:283)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:238)
at org.hibernate.event.def.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:85)
at org.hibernate.impl.SessionImpl.fireMerge(SessionImpl.java:678)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:662)
at org.hibernate.impl.SessionImpl.merge(SessionImpl.java:666)
at org.springframework.orm.hibernate3.HibernateTemplate$23.doInHibernate(HibernateTemplate.java:817)
at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:406)
... 49 more

如果User实体可以调用DistrictUserDetail的保存,那么为什么DistrictUserDetail不能看到父User(及其新id)并使用它来保存它。

我们使用的是hibernate版本:3.2.7.ga 底层数据库是:MySQL

任何帮助将不胜感激。 感谢

2 个答案:

答案 0 :(得分:1)

userDeatil.usernull,这是异常抱怨的内容。

我建议删除属性而不是双向关系。否则,user.setDistrictUserDetail()还需要设置user属性。

答案 1 :(得分:1)

您应该将OneToOne的可选属性设置为false,如下所示:

@OneToOne(mappedBy="districtUserDetail", optional = false)  
User user; 

此外,您当然应将DistrictUserDetail.user设置为非空实体。