One-to-One Unidirectional Relationship

Since you've already learned about the ins and outs of how unidirectional one-to-many and bidirectional one-to-many relationships work, it's time to learn about the One-to-One relationships.

We will start things off with the unidirectional One-to-One relationship and how it's set up in Hibernate.

First thing is first, you need to understand how a One-to-One relationship is actually set up in a database. Once you understand that the child table declares the parent's primary key as the child's primary key, then we can get moving with the Hibernate stuff!

For this example, we are going to use the One-to-One relationship between an Employee and their Address. The Address table will be set up as follows:

[table caption=”Address Table” class=”table table-bordered” style=”width: 21em”] Column[attr style=”background-color: #DDD”], Data Type[attr style=”background-color: #DDD”] employee_id, int(11)
street_address, varchar(255)
city, varchar(255)
region, varchar(255)
zip_code, varchar(10)
country, varchar(255)[/table]

So knowing this, let's have a look at what our Java Entity Object should look like for the Address:

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;

import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;

@Entity
public class Address
{
	private Long employeeId;
	private Employee employee;
	private String streetAddress;
	private String city;
	private String region;
	private String zipCode;
	private String country;

	@Id
	@GeneratedValue(generator="myGenerator")
	@GenericGenerator(name="myGenerator", strategy="foreign", parameters=@Parameter(value="employee", name = "property"))
	public Long getEmployeeId()
	{
		return employeeId;
	}
	public void setEmployeeId(Long employeeId)
	{
		this.employeeId = employeeId;
	}

	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="employee_id")
	public Employee getEmployee()
	{
		return employee;
	}
	public void setEmployee(Employee employee)
	{
		this.employee = employee;
	}
	@Column(name="street_address")
	public String getStreetAddress()
	{
		return streetAddress;
	}
	public void setStreetAddress(String streetAddress)
	{
		this.streetAddress = streetAddress;
	}
	public String getCity()
	{
		return city;
	}
	public void setCity(String city)
	{
		this.city = city;
	}
	public String getRegion()
	{
		return region;
	}
	public void setRegion(String region)
	{
		this.region = region;
	}
	@Column(name="zip_code")
	public String getZipCode()
	{
		return zipCode;
	}
	public void setZipCode(String zipCode)
	{
		this.zipCode = zipCode;
	}
	public String getCountry()
	{
		return country;
	}
	public void setCountry(String country)
	{
		this.country = country;
	}
}

Here are the topics of interest for our code above:

  1. We made sure to create an employeeId field as well as an employee object
  2. We use the @OneToOne annotation with a cascade on the getter method for the employee object (not the employeeId)
  3. Since the employeeId will not be a regular old auto generated value, we cannot use the regular @GeneratedValue logic that we used for the One-to-Many scenario
  4. We need to create our own @GenericGenerator and tell it how it should work

The One-to-One relationship requires some “extra” code because it has a special property of inheriting the foreign key from the parent table (Employee) and using it as the primary key of the child table (Address). To facilitate this property, we need to be sure that the primary key on the child table actually references the primary key of the parent table.

This is done in Java by creating an employeeId on our Address object (instead of creating an address_id).

We also declare an Employee object: we always need to maintain an actual real object reference, right? That's the whole point of a relationship!

The curve-ball here is that we'll need to specify the @JoinColumn for our employee‘s getter method. We do this because if we didn't, Hibernate would create a second column named employeeId (without the underscore) in our Address table, which would be confusing and a waste of space! So we need to help Hibernate by letting it know which column is our join column in our one-to-one relationship.

Okay, so now we're almost there, we just need to put the logic in that will tell Hibernate how to hook up our child's primary key with the parent's key. To do this, we need to create our own ID generator via the @GenericGenerator annotation.

With the @GenericGenerator annotation, we need to give it some information for it to work properly. First off, we name it whatever we like, for my example I just named it "myGenerator" but this is completely up to you. We also tell it what ID generation strategy to use, usually we use the “AUTO” strategy, but in this case we know that we want it to be a foreign key, so we use the “foreign” strategy.

Lastly, we need to tell the @GenericGenerator where the actual relationship exists. In our case, our @OneToOne relationship exists via the employee object, so we point it to that object via the use of the @Parameter annotation.

Oh, and let's not forget to point our @GeneratedValue annotation to our newly minted customer @GenericGenerator via the generator="myGenerator" property.

And presto! We have a functional unidirectional One-to-One relationship. Bravo.

You should use the same basic procedures to persist the data as we did in the unidirectional one-to-many with our controller and DAO. For this example we'd create an AddressDao which just invokes a session.saveOrUpdate() on the Address object.

We would then instantiate our Address object and populate everything that we can (except the actual employeeId field, as we'll need to leave that blank so it knows to perform an insert and not an update). It would look something like this:

Address address = new Address();
address.setStreetAddress("10 Capreol Ct");
address.setCity("Toronto");
address.setRegion("Ontario");
address.setCountry("Canada");
address.setEmployee(anEmployeeObject);
address.setZipCode("M1C 3L8");

addressDao.save(address);

One-to-One Bidirectional Relationship

The final piece in our One-to-One relationship puzzle is to outline how to create the bidirectional relationship.

This part is actually pretty straight-forward as we'll just implement the same steps we did in the bidirectional one-to-many relationship.

We will add an Address object as an instance variable in our class definition for the Employee object. We will then put an @OneToOne annotation on the getter method and be sure to assign the mappedBy property to complete the join. Here's what it would look like:

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;

@Entity
public class Employee
{
	private Long id;
	private String employeeName;
	private Employer employer;
	private Address address;

	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	public Long getId()
	{
		return id;
	}
	public void setId(Long id)
	{
		this.id = id;
	}
	public String getEmployeeName()
	{
		return employeeName;
	}
	public void setEmployeeName(String employeeName)
	{
		this.employeeName = employeeName;
	}
	@ManyToOne(cascade=CascadeType.ALL)
	public Employer getEmployer()
	{
		return employer;
	}
	public void setEmployer(Employer employer)
	{
		this.employer = employer;
	}
	@OneToOne(cascade=CascadeType.ALL, mappedBy="employee")
	public Address getAddress()
	{
		return address;
	}
	public void setAddress(Address address)
	{
		this.address = address;
	}
}

The only thing that will need to change after this is how we actually save/persist the data. In the unidirectional situation, we invoked the save on the child (Address) object. When we're dealing with a bidirectional relationship, we need to invoke the save on the parent (Employee) object.

Let's take a look at how that plays itself out:

Address address = new Address();
address.setStreetAddress("10 Capreol Ct");
address.setCity("Toronto");
address.setRegion("Ontario");
address.setCountry("Canada");
address.setEmployee(anEmployeeObject);
address.setZipCode("M1C 3L8");

anEmployeeObject.setAddress(address);

employeeDao.save(anEmployeeObject);

After instantiating the Address object and setting the appropriate fields, we need to make sure to set the Address object on the Employee object to complete the bidirectional associations.

Remember, in a bidirectional relationship the parent points to the child and the child points to the parent:

  • address.setEmployee(anEmployeeObject);
  • anEmployeeObject.setAddress(address)

Then we invoke the save on the parent (Employee) object and we are good to go!

As always, if you want to be on the cutting edge of these Java tutorials and receive updates on when I'm putting on free webinars and doing give-aways please join my mailing list by putting in your email address in the popup below. When you do you'll instantly receive one free gift from me (and plenty more in the future). I look forward to seeing you again soon!

Free Java Beginners Course

Start learning how to code today with our free beginners course. Learn more.