Again we have two entities: Primus and Secundus. Both entities have primary key using Long Java type. They are related 1-1, and Secundus should use the same primary key as Primus.
3 Years after my initial post they will look slightly different ;)
@Entity
@Table(name = "PRIMUS")
public class Primus {
public static Primus newInstance() {
Primus primus = new Primus();
primus.secundus = new Secundus(primus);
return primus;
}
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "primus")
private Secundus secundus;
Primus() {
super();
}
...
}
Not much changed here, ;). Really important changes were made on the Secundus:@Entity
@Table(name = "SECUNDUS")
public class Secundus {
@Id
private Long id;
@JoinColumn(name = "ID")
@OneToOne
@MapsId
private Primus primus;
Secundus() {
super();
}
public Secundus(Primus primus) {
this();
this.primus = primus;
}
As you see, the @PrimaryKeyJoinColumn annotation is replaced with two annotations: @MapsId, which defines that Secundus identifier will be determined by Primus identifier, and @JoinColumn, specifying which column in SECUNDUS table will be used for joining. Nothing more is needed :) - JPA Provider should automatically ask Primus for its identifier, when persisting Secundus, as long as you take care of correct entities correlation, like in newInstance method of Primus.
Long live JPA 2.0+ ;) :)
Thanks for you post, it's extremely helpful. You mention towards the end in the second last paragraph that '@JoinColumn, specifying which column in SECUNDUS table will be used for joining.'. Should this not be '@JoinColumn, specifying which column in PRIMUS table will be used for joining.'?
ReplyDeleteI thought the JoinColumn annotation takes the column name from the entity that you can joining to, in this case the Primus entity.
I was banging my head on the keyboard, trying to solve this exact problem, until I found this post.
ReplyDeleteThank You very, very much.
Thank you...fixed my issue after 4 hours of trial and error.
ReplyDeleteThank you...fixed my issue after 4 hours of trial and error.
ReplyDeleteThanks for helping me with the fantastic solution. I am getting a org.springframework.orm.jpa.JpaSystemException: attempted to assign id from null one-to-one property [com.example.model.ApplicantAddressDetails.index]; nested exception is org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property [com.example.model.ApplicantAddressDetails.index]
ReplyDeleteHow do i resolve this ?? I have exactly as you have explained in the blog.
Thank you very much for this simple and great solution!
ReplyDeletePlease show example code to create new instances just to avoid common errors that I seem to be making :)
ReplyDeleteShould the secundus table be defined with a foreign-key constraint? When I define that constraint I get exceptions like this:
Cannot add or update a child row: a foreign key constraint fails
Helped a lot
ReplyDeleteI am using hibernate-jpa-2.1. I tried with same One to One scenario with Primary entity having Auto generated Id. Now I tried using the same scenario in secondary entity( Disabled auto id generation and used MapsId). But When I save or persist data I get the following error.
ReplyDeleteMy code:
@Entity
@Table(name="employee")
public class Employee {
@Id
@GeneratedValue
@Column(name="empid")
private Integer empId;
****etc code
@Entity
@Table(name="projects")
public class Projects {
@Id
private Integer id;
@JoinColumn(name = "ID")
@OneToOne
@MapsId
private Employee emp;
****etc code
========================================================================
Exception in thread "main" org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save(): com.test.hibernate.Projects
at org.hibernate.id.Assigned.generate(Assigned.java:33)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:98)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:98)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSaveOrUpdate(SessionImpl.java:507)
at org.hibernate.impl.SessionImpl.saveOrUpdate(SessionImpl.java:499)
at org.hibernate.engine.CascadingAction$5.cascade(CascadingAction.java:218)
at org.hibernate.engine.Cascade.cascadeToOne(Cascade.java:268)
at org.hibernate.engine.Cascade.cascadeAssociation(Cascade.java:216)
at org.hibernate.engine.Cascade.cascadeProperty(Cascade.java:169)
at org.hibernate.engine.Cascade.cascade(Cascade.java:130)
at org.hibernate.event.def.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:437)
at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:326)
at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:180)
at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:121)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:535)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:523)
at org.hibernate.impl.SessionImpl.save(SessionImpl.java:519)
at com.test.hibernate.StoreQuery.main(StoreQuery.java:110)
I also see this behaviour.
ReplyDeleteDoes anyone have a solution?
https://hibernate.atlassian.net/browse/HHH-12436
ReplyDeleteThanks a lot for this article...I have been struggling to resolve this issue.
ReplyDeleteThanks a lot!
ReplyDelete