M.Rather
M.Rather

Reputation: 101

Hibernate @OneToOne mapping with reverse reference and CascadeType.ALL on owner does not persist the child

We have the following entity relation ship. User is the parent entity.DistrictUserDetail is the child.

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

This is the parent entity. The child entities primary key is a foreign key to User entity, and we have defined its key properties as:

@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

So, when we create a new User, and create a new DistrictUser, and set the DistrictUser to the User and save User, we expect that the User is saved and due to cascadeAll, DistrictUser is also saved.

This is our test:

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

But we encounter the following hibernate exception:

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

If User entity can invoke the save of DistrictUserDetail, then why can't DistrictUserDetail see the parent User (and its new id) and use it to save it.

We are using the hibernate version:3.2.7.ga Underlying database is : MySQL

Any help will be appreciated. Thanks

Upvotes: 1

Views: 3575

Answers (2)

Muammer Yucel
Muammer Yucel

Reputation: 11

You should set optional attribute of OneToOne to false like this:

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

And also, you should set DistrictUserDetail.user to a not-null entity of course.

Upvotes: 1

OrangeDog
OrangeDog

Reputation: 38816

userDeatil.user is null, which is what the exception is complaining about.

I would suggest removing the property and not having a bi-directional relationship. Otherwise, user.setDistrictUserDetail() also needs to set the user property.

Upvotes: 1

Related Questions