Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

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:

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 

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:

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

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

like image 823
Mike Lively Avatar asked Oct 26 '10 20:10

Mike Lively


People also ask

How do you make a one to many relationship as bidirectional?

Bidirectional @OneToMany Relationship – Employer/EmployeeWhen you traverse from the “Many” side to the “One” side, you only need to make reference to one object, which is why the Employee class holds a single reference to an Employer class via the private Employer employer instance variable.

What is bidirectional relationship in hibernate?

Bidirectional association allows us to fetch details of dependent object from both side. In such case, we have the reference of two classes in each other. Let's take an example of Employee and Address, if Employee class has-a reference of Address and Address has a reference of Employee.

What is bidirectional relationship in JPA?

JPA Relationships can be either unidirectional or bidirectional. This simply means we can model them as an attribute on exactly one of the associated entities or both. Defining the direction of the relationship between entities has no impact on the database mapping.

What is cascade type all?

The meaning of CascadeType. ALL is that the persistence will propagate (cascade) all EntityManager operations ( PERSIST, REMOVE, REFRESH, MERGE, DETACH ) to the relating entities. It seems in your case to be a bad idea, as removing an Address would lead to removing the related User .


1 Answers

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.

In your case LeadAffiliate is derived, so it should be the owner, when Lead should be marked as non-owning side by mappedBy. The following works in both 3.5.0 and 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;       ... } 
like image 159
axtavt Avatar answered Sep 21 '22 23:09

axtavt