It took me more than few minutes to find how to do one to one mapping using Hibernate Annotations, so I though I will share it.
I think Hibernate Annotations is best explained by example. Actually, I am not sure I have ever read anything about Hibernate Annotation mappings beside looking at examples…
This example is built from 2 classes: Person and PersonDetails, corresponding to 2 database tables: person and person_details. The 2 classes will demonstrate the usage of one to one mapping from both directions.
I am not sure this example is so close to the real world, but I believe it is enough to get the idea.
The person table contains the following columns:
- person_id – used as primary key with auto increment
- first_name
- last_name
- birth_date
The person_details table contains the following columns:
- person_id – used as primary key and foreign key to person table (person_id column)
- weight
- height
- hair_color
- eyes_color
Note that there may be persons with no person details. That means that having a row in person table, but no row in person_details table is a trivial case. Of course that there may be at the most one person details row for each person.
This is how the Person class looks like using Hibernate Annotations:
package com.bashan.hibernate;import org.hibernate.validator.Length;import org.hibernate.validator.NotNull;import javax.persistence.*;import java.io.Serializable;import java.util.Date;@Entity@Table(name="person")public class Person implements Serializable {@Id@GeneratedValue(strategy= GenerationType.AUTO)@Column(name="person_id")private Integer personId;@Column(name="first_name")@NotNull@Length(max = 50)private String firstName;@Column(name="last_name")@NotNull@Length(max = 50)private String lastName;@Column(name="birth_date")private Date birthDate;@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "person")@JoinColumn(name="person_id")private PersonDetails personDetails;public Integer getPersonId() {return personId;}public void setPersonId(Integer personId) {this.personId = personId;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public Date getBirthDate() {return birthDate;}public void setBirthDate(Date birthDate) {this.birthDate = birthDate;}public PersonDetails getPersonDetails() {return personDetails;}public void setPersonDetails(PersonDetails personDetails) {this.personDetails = personDetails;}}
And the PersonDetails class:
package com.bashan.hibernate;import org.hibernate.annotations.Cascade;import org.hibernate.annotations.GenericGenerator;import org.hibernate.annotations.Parameter;import javax.persistence.*;@Entity@Table(name="person_details")public class PersonDetails {@Id@GeneratedValue(generator="foreign")@GenericGenerator(name="foreign", strategy = "foreign", parameters={@Parameter(name="property", value="person")})@Column(name="person_id")private Integer personId;@OneToOne(fetch = FetchType.LAZY, optional=true)@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})@PrimaryKeyJoinColumnprivate Person person;@Column(name="weight")private Integer weight;@Column(name="height")private Integer height;@Column(name="hair_color")private Integer hardColor;@Column(name="eyes_color")private Integer eyesColor;public Integer getPersonId() {return personId;}public void setPersonId(Integer personId) {this.personId = personId;}public Person getPerson() {return person;}public void setPerson(Person person) {this.person = person;}public Integer getWeight() {return weight;}public void setWeight(Integer weight) {this.weight = weight;}public Integer getHeight() {return height;}public void setHeight(Integer height) {this.height = height;}public Integer getHardColor() {return hardColor;}public void setHardColor(Integer hardColor) {this.hardColor = hardColor;}public Integer getEyesColor() {return eyesColor;}public void setEyesColor(Integer eyesColor) {this.eyesColor = eyesColor;}}
That’s pretty much it… I hope it will save you time…
Hello.
ReplyDeleteI wondering how you made the save, because I have the following problem.
I have 2 entitys fund and fund_details, each one mapped as you, but when hibernate try to save it, it first save the fund_details and then fund, but for that moment the fund id doesn't exist.
To save I made :
fund.setFundDetails(details);
details.setFund(fund);
The error I get is:
util.JDBCExceptionReporter SQL Error: 0, SQLState: 23503
[ERROR] util.JDBCExceptionReporter ERROR: insert or update on table "fund_details" violates foreign key constraint "fkd8ef6e256fba07b1"
Thx.
Hi Juan,
ReplyDeleteI believe the "mappedBy" property on the parent class should do the work. Try to annotate your classes as close as you can to my example (it is example taken from a working code).
Thanks! It helped to save time :)
ReplyDeletehi,
ReplyDeleteI wondering how you made the save, because I have the following problem.
org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: mainParent
Hi,
ReplyDeleteIt should work OK.
Just make sure you put all the annotations properly.
I have the following problem.
Deleteorg.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: mainParent
Hi,
DeleteI think you might have this problem since you didn't set your primary key in the parent table to be "Auto Increment" (My Sql) or identity column (SQL Server). If you use another database you have to set this column to automatically generate the value of the key or otherwise, change in Hibernate the declaration: @GeneratedValue(strategy= GenerationType.AUTO) to be something else that will tell Hibernate to generate the value of the primary key.
Hi,
ReplyDeleteI want to know why in Person class you are using @JoinColumn(name="person_id").Even without JoinColumn it works.
It has been too long to remember why I did it this way. If it works without the "JoinColumn" then thanks for the update. It might help to others.
ReplyDeleteHI Guy Bashan,
ReplyDeleteThanks for your kindness to answer each of all questions.
I am new to java, and I have a dummy question.
I want to use "hibernate mapping using annotation" like above in your code. The question is : which IDE should I use? Netbeans or Eclipse? and what did you use?
thanks so much if you could help!
Hi Shengmao Li,
ReplyDeleteWhich IDE to use is a hard question. It's a matter of taste and habit. I personally use IntelliJ. It gives nice solutions for Hibernate. It is kind of "Hibernate aware". You can see problems in mappings and even the HQL is highlighted and validated. If you want to choose between Eclipse or Netbeans I would recommend Eclipse. Not from my experience, but from what I have seen around it is more widely adopted.
When I try to insert I got this: null id generated for:class com.itym.pojo.elementos.radioenlaces.RadioenlaceDatosSupervisionPojo
ReplyDeleteIt seems it's trying to insert the child before the parent. Does anyone know why or how to resolve that?
Hi Krusher,
ReplyDeleteIt is hard to tell why you get it without looking at the code.
I have problem in updation of record
ReplyDeletefollowing error generated
-> Record Updated org.hibernte.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: studentobj
-> my update method code
public boolean getRecordUpdated(StudentBean sb){
boolean flag=false;
try {
System.out.println("user " +sb.getLoginobj().getUsername());
sf=MysqlConnection.getConnection();
s=sf.openSession();
s.getTransaction().begin();
s.update(sb);
s.getTransaction().commit();
flag=true;
s.close();
} catch (Exception e) {
System.out.println("Record Updated " +e);
e.printStackTrace();
}
return flag;
}
i have problem in updation of record
ReplyDeletefollowing error arises
->error
Record Updated org.hibernate.id.IdentifierGenerationException: attempted to assign id from null one-to-one property: studentobj
->my studentBean
@Entity
@Table(name="student")
public class StudentBean implements Serializable {
private int studentId;
private String sname,saddress,scontact;
private LoginBean loginobj;
@OneToOne(cascade= CascadeType.ALL,fetch= FetchType.LAZY,mappedBy="studentobj")
@JoinColumn(name="studentId")
public LoginBean getLoginobj() {
return loginobj;
}
public void setLoginobj(LoginBean loginobj) {
this.loginobj = loginobj;
}
@Id
@GeneratedValue(strategy= GenerationType.AUTO)
@Column(name="studentId")
public int getStudentId() {
return studentId;
}
public void setStudentId(int studentId) {
this.studentId = studentId;
}
-my loginbean code
@Entity
@Table(name="Login")
public class LoginBean implements Serializable{
private int loginId;
private String username,password;
private StudentBean studentobj;
@OneToOne(cascade= CascadeType.ALL,fetch= FetchType.LAZY,optional=true)
@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
@PrimaryKeyJoinColumn
public StudentBean getStudentobj() {
return studentobj;
}
public void setStudentobj(StudentBean studentobj) {
this.studentobj = studentobj;
}
@Id
@Column(name="loginId")
@GeneratedValue(generator="myGenValue")
@GenericGenerator(name="myGenValue",strategy="foreign",parameters=@Parameter(name="property",value="studentobj"))
public int getLoginId() {
return loginId;
}
public void setLoginId(int loginId) {
this.loginId = loginId;
}
->my update method
public boolean getRecordUpdated(StudentBean sb){
boolean flag=false;
try {
System.out.println("user " +sb.getLoginobj().getUsername());
sf=MysqlConnection.getConnection();
s=sf.openSession();
s.getTransaction().begin();
s.update(sb);
s.getTransaction().commit();
flag=true;
s.close();
} catch (Exception e) {
System.out.println("Record Updated " +e);
e.printStackTrace();
}
return flag;
}
Hi TedyBear,
ReplyDeleteI believe Hibernate has gone quite a few improvements since I last wrote this blog, therefore it will be hard for me to help you... Anyway, back then it used to work quite well for me...