One of the less commonly used mappings in JPA and Hiberate is the @OneToOne mapping with a shared primary key. Conceptually this type of mapping is simple to understand. However, recently I tried to implement this relationship between two objects and ran into some petty issues. No clear example was listed on the net, so I decided to post a solution on a blog for possible reference for others.
OneToOne Shared Primary Key
A one to one shared primary key is not the most common mapping pattern, but is still used by many domain object designers. More common is the one to one mapping with a separate foreign key. A one to one shared primary key is simply where two objects have a one to one relationship where they share a primary key.
For example, let's use the prominant Person to Address example.
A Person can have one Address only, and an Address can only have one Person living in it. Let's consider Person the owner of the relationship, meaning its primary key gets generated first and essentially objectwise an Address is "set" in the Person object. A one to one shared primary key relationship would mean that a Person table does not have a FK column to Address, rather the Address table's primary key value is the same as Person, and acts as a foreign key to the Person table.
In contrast a one to one relationship with a foreign key would result in the Person table having a field like ADDRESS_ID, which acts as a foreign key to the Address table. The Person primary key generation is independent of the Address primary key. This means the primary key to Person and Address are not the same generally, but could be by coincidence.
Essentially, one to one with a shared primary key saves a column in the database and usually forces a bidirectional relationship through an ORM such as hibernate.
References and Example
When attempting to implement one to one shared mapping with JPA, I had a little trouble. The official JPA documentation does not give an example with a generated id, but only gives one where the ids are manually set. The Hibernate annotations site does the same. In addition there are a few blog postings which address the issue, but they are muddled and don't specify clearly the implementation details. Thus, I had to experiment and put together knowledge from all over to reach a solution.
In Hiberate Core (without JPA or Annotation), the concept and mapping implementation is explained nicely. 5.1.13 One-to-One.
The most informational resource was the book: Java Persistence with Hibernate - 7.1 Single Valued entity associations, pg278-282. This gave the implemenation with annotations.
In order to use a one to one with a shared primary key, ids on both sides of the object need a @GeneratedValue. The owner can use a generic generator where it gets a fresh number every time, but the other side of the one to one needs a custom hibernate extension, which is from @GenericGenerator. This grabs the primary key from the owner object and places it in the dependent object. In our example, Address will have the hibernate extension @GenericGenerator.
Person class
package com.assarconsulting.addressbook.model;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
@Entity
@Table(name = "PERSON")
public class Person {
@Id
@GeneratedValue
@Column(name = "PERSON_ID")
private Long id;
@Column(name = "NAME", nullable=false)
private String name;
@Column(name = "AGE", nullable=false)
private int age;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
private Address address;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getStreetNumber() {
return address.getStreetNumber();
}
public String getStreetName() {
return address.getStreetName();
}
public String getCity() {
return address.getCity();
}
public String getState() {
return address.getState();
}
public String getZipCode() {
return address.getZipCode();
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
address.setPerson(this);
}
}
Address Class
package com.assarconsulting.addressbook.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
@Entity
@Table(name = "ADDRESS")
@org.hibernate.annotations.GenericGenerator(name="person-primarykey", strategy="foreign",
parameters={@org.hibernate.annotations.Parameter(name="property", value="person")
})
public class Address {
@Id
@GeneratedValue(generator = "person-primarykey")
@Column(name = "PERSON_ID")
private Long id;
@Column(name = "STREET_NUMBER", nullable=false)
private String streetNumber;
@Column(name = "STREET_NAME", nullable=false)
private String streetName;
@Column(name = "CITY", nullable=false)
private String city;
@Column(name = "STATE", nullable=false)
private String state;
@Column(name = "ZIP_CODE", nullable=false)
private String zipCode;
@OneToOne
@PrimaryKeyJoinColumn
private Person person;
public Address() {
}
public Address(String streetNumber, String streetName, String city,
String state, String zipCode) {
super();
this.streetNumber = streetNumber;
this.streetName = streetName;
this.city = city;
this.state = state;
this.zipCode = zipCode;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStreetNumber() {
return streetNumber;
}
public void setStreetNumber(String streetNumber) {
this.streetNumber = streetNumber;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String streetName) {
this.streetName = streetName;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
}
Not a great example, as a person could own more than one home, or if they didn't they could move home. Having the person id in the address table would then be a nuisance.
ReplyDeleteP.S Why don't the cursor keys work in this edit box!!!!
The statement "a person could own more than one home, or if they didn't they could move home." is quite true in the real world, but is not much value in this context. The important point illustrated in the example is implementation of one to one shared primary key in JPA.
ReplyDeleteIn order to make the example work we can just make a simple assumption that one Person can have only on Address. If this doesn't make much sense then substitute the Address object with a Heart object and say "A Person can have only one Heart." The important illustration is the implementation of JPA one to one.
In regards to the edit box, I am not sure.
Fair enough. I guess you might use this pattern in situations where you want to split data (based on the same identifier), across multiple tables for performance reasons.
ReplyDeleteI would like to suggest that you might cut your example code above down to show just the relevant bits. It would much clearer for the reader.
Thanks Nirav. contrary to that idiot the post actually helped. Cheers mate.
ReplyDeleteMany thanks mate! It really helped me a lot.
ReplyDeleteNo problem
ReplyDeleteNice explaination..I am using one-to-one mapping which have shared primary key.Issue is first time hibernate always issues an insert for child even when its present, which obviously fails.
ReplyDeleteAfter going through lot of blogs, I realise mapping works fine when
property name="hbm2ddl.auto=create
If you change above to "update" it stops working as insert fails for UserDetails saying there is already a duplicate key in DB
Can you tell me how to resolve the above issue?
used the same example as in your post and that also failed for Address when i made property name="hbm2ddl.auto=update
ReplyDeleteThat was exactly what I was looking for without succes in the official documentation and on the net.
ReplyDeleteMany thanks !
Thanks ,the example helped a lot and provides good understanding.
ReplyDelete@Abhilasha.
ReplyDeleteThanks for the positive feedback.
Has anybody found a way of doing that without annotations? @Nirav Assar Thx for the article.
ReplyDeleteThanks a ton for this dude ! there is way to many annotations and many tutorials online that are not simple ; I just used ur code as a template and got my stuff sorted !
ReplyDeleteCheers
J
No problem great it worked out for you. JPA is good, but check out GORM, with groovy and grails. I am hooked on it now and mapping is way easier.
ReplyDeleteI am trying to do the same thing with some changes. In my case I have another value in Person that is a primary key and is a foreign key in the Address table.
ReplyDeleteso like in Person
primary key(person_id,person_age)
and in the Address table it would be like
foreign key (person_id, person_age) references person(person_id, person_age).
The example you showed only works if there is one primary key on the table, can you post one where its using composite key. I am still trying to use the generator for the person_id and would set the person_age on the person.
Hello Nirav, great example, but somebody could do it via JPA 2 standard annotations? We want to stay attached to JEE6 standards. Thanks!
ReplyDeleteGonzalo, I am not sure of the variance between JEE 5 and 6 concerning the annotation, but I am sure there is not a huge difference. You could probably map this example to the JEE6 spec for JPA. Good luck, and thanks!
ReplyDeleteThanks for the example, it helped a lot. I wanted to map a view to the master table and use the same primary key for both. I would not have thought about that solution.
ReplyDeleteMany thanks again!
@Nirav:
ReplyDeleteGreat article! I just did some reading and found a very CONCISE and intuitive method for achieving this. It is the @PrimaryKeyJoinColumn method.
http://download.oracle.com/javaee/5/api/javax/persistence/OneToOne.html
http://docs.jboss.org/hibernate/annotations/3.5/reference/en/html_single/#entity-mapping-association
Excellent post, thank you.
ReplyDeleteThank you very much for this. Recently upgrading Hibernate broke these for me and your explanation was just what I needed.
ReplyDeleteGreat article, indeed an uncovered subject. You refferd a bidirectional relationship. Could you please refer the case where a person holds an addredd but the address doesn't point back to the person (a unidirectional relationship)? whould you change it and how?
ReplyDeleteThanks in advance
Good article.
ReplyDeleteI'm just trying out if it works with javax.persistence.SequenceGenerator as well, so that the solution may be provider-independant.
But I'm right at the beginning. Maybe I will be able to give some more information at the end of the week.
Thanks a lot - your blog really helped us in solving a crucial problem. We went through all the hibernate documentation and tried everything but we kept getting various errors. The examples in the hibernate documentation and the hibernate book seem to be wrong as well.Thanks again.
ReplyDelete-Vandana
Great, I am glad I could help.
ReplyDeleteAs I said earlier I wanted to try out javax.persistence.SequenceGenerator, but I due to a constant lack of time I keep using your solution.
ReplyDeleteI only leave the generated value of the Address-class and therefore use an Address-constructor with a Person-object.
I also tried some other ways, but this one is by now the only one that keeps working.
I'm running into a few issues with this example. I use SequenceGenerator that use an oracle database sequence to generate the PERSON primary key. Will this example work with sequence generator ?
ReplyDeletePerson person = new Person();
Address address = new Address();
person.setAddress(addres);
session.SaveOrUpdate(person);
When I do the above I get the error IdentifierGenerationException - attempted to assign id from null one-ton-one property.
However when I changed the code as below to explicitly set the bi-directional association, I get a integrity constraint violated - parent key not found error.
Person person = new Person();
Address address = new Address();
person.setAddress(addres);
address.setPerson(person);
session.SaveOrUpdate(person);
I'm trying to create a brand new person and address record. Is there something I'm doing wrong while saving or is this the issue with Sequence generator.
I was wrong with the above post. This example works as it is for SequenceGenerator. I must have missed something. Thanks for the example. Saved me a lot of time & headache.
ReplyDeleteHibernate reference annotation 3.5.6 explains shared primary keys by using @PrimaryKeyJoinColumn which is quite different from what you explained here.
ReplyDeleteYour thoughts on this?
This post saved my day.
ReplyDeleteThanks for sharing.
Thank you for good pointers, however...
ReplyDeleteAccording to the official documentation the "mappedBy" should be specified on the non-owning side of the relationship.
In your case it seems incorrect to use mappedBy = "person" in the
Person class (which owns an Address)
@OneToOne(cascade = CascadeType.ALL, mappedBy = "person")
hi, did u get the answer for this ?
DeleteUsing this example I am able to post the data but unable to retrieve. Getting hibernate PropertyAccessException at
ReplyDeletepublic void setAddress(Address address) {
this.address = address;
address.setPerson(this);
}
We have followed the same foreign key generator and thanks for that. But in my case, i have a specific problem. Consider the Address is optional in your same example. Now, I fetch a record Person who has address and now i am making the address object in the person entity a null(to remove the address) and perform a entityManeger.MERGE(person) operation. In this case the record in the Address table is not getting deleted automatically. Let me know if you could help me in this.
ReplyDelete-AraZu
I just wanted to thank Jakey in the comments. I really appreciated his post!
ReplyDeleteWe can achieve the same result in JPA without using Hibernate API. Below is the example.
ReplyDelete//This is the User table having userId as primary key
@Entity
@Table(name = "users")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "userid")
private Long userId;
private boolean active;
@Column(name = "email")
private String emailId;
@Column(name = "firstname")
private String firstName;
@OneToOne
@PrimaryKeyJoinColumn(name = "userid",referencedColumnName = "userProfileId")
private UsersProfileEntity userProfile;
}
//This is the userProfile table sharing same primary key as User //table
@Entity
@Table(name="users_profile")
public class UsersProfileEntity implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Long userProfileId;
private Integer age;
private String description;
@OneToOne(mappedBy="userProfile", fetch=FetchType.LAZY)
private UserEntity user;
}
//Now this is my DAO saving both the entities
@Repository
public class UserDaoImpl implements UserDao {
@PersistenceContext
EntityManager entityManager;
public Long saveUser(UserEntity user) throws DAOException {
entityManager.persist(user);
user.setImageName(user.getUserId().toString());
user.getUserProfile().setUserProfileId(user.getUserId());
entityManager.persist(user.getUserProfile());
entityManager.flush();
return user.getUserId();
}
}
I have truncated the code in order to keep the example simple.
using this approach we can use the generated userId key inside UserEntity within UserProfileEntity. Hope this will be helpfull
Thank you for the propsed solution! It helped me for hibernate 4.0.1.Final
ReplyDeletecan you please pass the complete source code for the example posted
ReplyDeleteThank you! This helped to solve my probelm.
ReplyDeleteInteresting Article
ReplyDeleteSpring Hibernate Online Training | Hibernate Training in Chennai
Hibernate Online Training | Java Online Training | Java EE Online Training
Some us know all relating to the compelling medium you present powerful steps on this blog and therefore strongly encourage contribution from other ones on this subject while our own child is truly discovering a great deal. Have fun with the remaining portion of the year.
ReplyDeleteSelenium training in Chennai
Selenium training in Bangalore
Nice blog.
ReplyDeleteFor the best python training institute in Bangalore, Visit:
Python training in bangalore
visit here. . . IOT TRAINING IN BANGALORE
ReplyDeleteFor more info reach us Big Data and hadoop training in bangalore
ReplyDeleteThis comment has been removed by the author.
ReplyDeletekeep up the good work. this is an Assam post. this to helpful, i have reading here all post. i am impressed. thank you. this is our digital marketing training center. This is an online certificate course digital marketing training in bangalore
ReplyDeletethanks for sharing us
ReplyDeleteHindi Shayari
Hindi Shayari
hindi love shayari 2020
Rap Name Generator
Mod Apk
Picsart Mod Apk
Kinemaster pro Mod Apk
mp4moviez
Latest News
Best laptop under 50000
Get Into Pc
Thanks for sharing this informative content , Great work
ReplyDeleteLeanpitch provides online training inScrum Master during this lockdown period everyone can use it wisely.
CSM online certification
Interesting post. I wondered about this issue, so thanks for posting. A very good article. This is a really very nice and useful article. Thank you. Digital Marketing Course in Hyderabad
ReplyDeleteYou actually make it look so easy with your performance but I find this matter to be actually something which I think I would never comprehend. It seems too complicated and extremely broad for me. I'm looking forward for your next post, I’ll try to get the hang of it!
ReplyDeleteBest Institute for Data Science in Hyderabad
best cars
ReplyDeleteinteresting to read.thank you.
ReplyDeleteAngular training in Chennai
A good blog always comes-up with new and exciting information and while reading I have feel that this blog is really have all those quality that qualify a blog to be a one.
ReplyDeletedata scientist training in malaysia
I am happy to find this post very useful for me, as it contains lot of information. I always prefer to read the quality content and this thing I found in you post. Thanks for sharing.
ReplyDeleteWashing Machine Repair Service in Hyderabad
This is very useful post for me. This will absolutely going to help me in my project.
ReplyDeletefull stack web development course in malaysia
ReplyDeleteI really like reading a post that can make people think. Also, thank you for permitting me to comment.
Really impressed, Everything is clearly explained. I enjoyed reading this blog.
ReplyDeleteImmigration Lawyer Near Me Virginia
Personal Injury Lawyers Near Me Virginia
Bankruptcy Attorney Near Me Virginia
ReplyDeleteNice post. Thanks for sharing! I want people to know just how good this information is in your article. It’s interesting content and Great work.
Affordable Interior Designer In Gurgaon
visit - lookobeauty
https://lookobeauty.com/best-interior-designer-in-gurgaon/
Very interesting post and thanks for your knowledgeable sharing with us. Keep doing well!
ReplyDeleteVirginia Online Divorce
Divorce Lawyer Consultation Cost
Divorce in Virginia with Child
This is an informative posts and useful blogs
ReplyDeleteDC Child Pornography Lawyer
Culpeper Traffic Lawyer
Hi I have read a lot from this blog thank you for sharing this information. We provide all the essential topics in Web Development Course In Bangalore for more information just log in to our website Web Development Course In Bangalore
ReplyDeleteyurtdışı kargo
ReplyDeleteresimli magnet
instagram takipçi satın al
yurtdışı kargo
sms onay
dijital kartvizit
dijital kartvizit
https://nobetci-eczane.org/
OG6
resimli magnet
ReplyDeleteresimli magnet
çerkezköy çatı ustası
silivri çatı ustası
dijital kartvizit
R6İ
شركة مكافحة الحمام
ReplyDeleteمكافحة حمام
Powerful paris dedicated server offering blazing speed, robust security, and 24/7 support for your online ventures. Elevate your server experience!
ReplyDeleteEncountering challenges in implementing @OneToOne mapping with shared primary keys underscores the value of shared solutions and collaborative learning at our Software Training Institute in Bangalore.
ReplyDeleteDigital Marketing Course in T Nagar
For comprehensive insights into JPA and Hibernate mappings, consider exploring Cambridge Infotech.
ReplyDeleteSoftware Training Institute in Bangalore
Exhibition stall designer
ReplyDeleteشركة مكافحة حشرات بالاحساء ElJ7Fdb29Q
ReplyDeleteشركة كشف تسربات المياه بالقطيف 0STMV5rMwS
ReplyDeletevery nice post....
ReplyDeletebril ai
Very Nice Blog . Thanks for Posting
ReplyDeleteI found this post really interesting. This information is beneficial for those who are looking for
Best Data Science Course Training in Bangalore
Best Data Science Course Training in Hyderabad
Best Data Science Course Training in Pune
Data Science Course Training Certification in USA
Best Data Science Certification Course Online