OneToOne annotation is one of the annotations that are used for defining a single-valued association to another entity that has one-to-one multiplicity. The multiplicity concept seems ambiguous but it simply tells you the maximum and minimum allowed members of the set. It is a UML design concept for defining the association relationship, in one-to-one case it indicates that at least one of the two related classes makes reference to another. Let’s define a class A and B, in that A has a relationship one-to-one with B, that’s mean at least the instance of A has a reference for instance of B.
In case, A has a reference for B but B hasn’t a reference of A, such that association is called One-To-One Unidirectional relationship. But when A has a reference of B and B has a reference of A, then the association is called One-To-One Bidirectional relationship. This tutorial will cover the One-To-One relationship either Unidirectional or Bidirectional types using an EclipseLink – JPA implementation.
Prerequiste for Using One-To-One Annotation
To be able to use a One-To-One annotation you should have a look into those steps that required for installing the JPA implementation. (See EclipseLink – Installation or EclipseLink – Maven – Installation).
The using of OneToOne annotation requires understanding of anatomy of @OneToOne and @JoinColumn annotations.
Anatomy of @OneToOne Annotation
- Target: fields (Including property get methods)
- Uses: @OneToOne
- Arguments: The @OneToOne annotation takes multiple arguments when it used and these are:
- targetEntity (Optional): It is used to determine the entity class that is the target of the association.
- cascade (Optional): It is used to determine the operations that must be cascaded to the target of the association.
- fetch (Optional): It is used to determine whether the association should be Lazily loaded or must be eagerly fetched. The EAGER strategy is a requirement on the persistence provider runtime that the associated entity must be eagerly fetched. The LAZILY strategy is just a hint to the persistence provider runtime.
- optional (Optional): It is used to determine whether the association is optional. If set to false then a non-null must be always exist.
- mappedBy (Optional): The field that owns the relationship. The mappedBy element is only specified on the inverse (non-owning) side of the association. This argument is very important one and it is will be clarified when we are coming to discuss the Bidirectional and Unidirectional.
Anatomy of @JoinColumn Annotation
- Target: fields (Including property get methods)
- Uses: @JoinColumn
- Arguments:The @OneToOne annotation takes multiple arguments when it used and these are:
- name (Optional): The name of the foreign key column. The table in which it is found depends upon the context.
- referencedColumnName (Optional): The name of the column referenced by this foreign key column.
- unique (Optional): Whether the property is a unique key.
- nullable (Optional): Whether the foreign key is nullable.
- insertable (Optional): Whether the column is included in the SQL INSERT statements generated by the persistence provider.
- updatable (Optional): Whether the column is included in the SQL UPDATE statements generated by the persistence provider.
- columnDefinition (Optional): The SQL fragment that is used when generated the DDL for the column.
- table (Optional): The name of the table that contains the column.
@OneToOne Unidirectional Example
This Example should clarify the using of OneToOne annotation to achieve a Unidirectional association. If you haven’t been looking for the previous examples that mentioned before that is talking about Employee entity, it is important to refer the links above cause a given examples here depends on it.
- The Employee class has two attributes, employeeId and employeeName, but even though the employee in any organization should has a home address that it is living in.Let’s say this is an assumption that we have required to establish a OneToOne relationship.
- In the unidirectional example that you would see, the employee has a reference to an instance of home address, but the address has no instance of employee entity.
- But let’s take a look at the Employee and Address database tables, in that where is the location of the foreign key that should be referring the other table?. We assume that the Employee table should contain the foreign key that referring a specific address record.
The below Java classes should clarify the usage, that we have about Employee and Address Tables.
package net.javabeat.eclipselink.data; import javax.persistence.Basic; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.Id; import javax.persistence.JoinColumn; import javax.persistence.OneToOne; @Entity public class Employee { @Id private int employeeId; @Basic(optional=false) private String employeeName; @OneToOne(cascade=CascadeType.ALL) @JoinColumn(name="AddressId") private Address address; public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public int getEmployeeId() { return employeeId; } public void setEmployeeId(int employeeId) { this.employeeId = employeeId; } public String getEmployeeName() { return employeeName; } public void setEmployeeName(String employeeName) { this.employeeName = employeeName; } }
package net.javabeat.eclipselink.data; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity(name="address") public class Address { @Id private int addressId; private String addressCountry; private String addressCity; public int getAddressId() { return addressId; } public void setAddressId(int addressId) { this.addressId = addressId; } public String getAddressCountry() { return addressCountry; } public void setAddressCountry(String addressCountry) { this.addressCountry = addressCountry; } public String getAddressCity() { return addressCity; } public void setAddressCity(String addressCity) { this.addressCity = addressCity; } }
<?xml version="1.0" encoding="UTF-8"?> <persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"> <persistence-unit name="EclipseLink-JPA-Installation" transaction-type="RESOURCE_LOCAL"> <strong><class>net.javabeat.eclipselink.data.Employee</class></strong> <strong> <class>net.javabeat.eclipselink.data.Address</class></strong> <properties> <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/JavaBeat"/> <property name="javax.persistence.jdbc.user" value="root"/> <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/> <property name="javax.persistence.jdbc.password" value="root"/> <property name="eclipselink.logging.level" value="OFF"/> </properties> </persistence-unit> </persistence>
- As you have noticed, the Employee class contains the details of the relationship, in that the Employee’s address property annotated using @OneToOne to indicate that it’s representing association with the Address entity.
- But if you haven’t use the @JoinColumn, you almost getting an exception; SQL Exception. Also, you should be able to see that the Address entity doesn’t has a reference of Employee entity. Such that association called “Unidirectional” association, and that’s mean the relationship is established from one side.
- Whatever you’re trying to develop, if you want the address entity to get reference of the employee entity (i.e. a bidirectional association), you have to declare another attribute at the address entity and use an OneToOne annotation. But this time, the using of OneToOne annotation is vary different from the old use, that you’ve looked before. This time we’ll introduce the concept of mappedBy.
- @JoinColumn annotation used by the owner side of the association. Because of Employee table has the a foreign key that referring the Address table, so that the @JoinColumn must be used in the Employee entity.
- MappedBy attribute is used by the non-owning side of the relation to achieve a bidirectional association.It is also used for referring the property that having the details of the relation at the other side of association.
- To achieve a bidirectional OneToOne association, you must add a new property within an Address entity called employee and annotate it using @OneToOne with a difference here, that’s not using the @JoinColumn in conjunction with @OneToOne. But by using the mappedBy to refer the name of the property at the Employee entity that having the details of the association (i.e. address property at the Employee entity).
- The persistence.xml shows you the full entries that you must have to establish the association required. As you’ve noticed the persistence domain (Persistence Context) has two classes; Employee and Address.
The next example, should clarify using of @OneToOne with the mapped by to establish a bidirectional one to one multiplicity between Employee & Address entities. The Employee entity need no change but the address entity is a subject of change.
@OneToOne Bidirectional Example
package net.javabeat.eclipselink.data; import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToOne; @Entity(name="address") public class Address { @Id private int addressId; private String addressCountry; private String addressCity; @OneToOne(cascade=CascadeType.ALL,mappedBy="address") private Employee employee; public Employee getEmployee() { return employee; } public void setEmployee(Employee employee) { this.employee = employee; } public int getAddressId() { return addressId; } public void setAddressId(int addressId) { this.addressId = addressId; } public String getAddressCountry() { return addressCountry; } public void setAddressCountry(String addressCountry) { this.addressCountry = addressCountry; } public String getAddressCity() { return addressCity; } public void setAddressCity(String addressCity) { this.addressCity = addressCity; } }
Anatomy of Database Tables
Main Application
This snippet of code will shows you the main code that responsible to persist an Employee associated with the Address. Look through and be careful. You have to use the transaction manager for the entity manager. The JPA example will not work if you haven’t such that transaction boundaries. That’s related to the JPA Transaction that should be discussed later.
public static void main(String [] args){ em.getTransaction().begin(); createEmployee(); em.getTransaction().commit(); } public static void createEmployee(){ Address address = new Address(); address.setAddressId(1); address.setAddressCountry("United Kingdom"); address.setAddressCity("London"); Employee employee = new Employee(); employee.setEmployeeId(1); employee.setEmployeeName("John Smith"); employee.setAddress(address); em.persist(employee); }
Summary
Different types of association can be achieved by eclipselink mapping annotations, one of them is @OneToOne. @OneToOne is an annotation that been used for establishing a one to one multiplicity. The established multiplicity by using @OneToOne divided into two main types; unidirectional and bidirectional. The unidirectional represents a street of one way of connectivity, while the bidirectional represents a street of two ways of connectivity. @OneToOne annotation used with the conjunction of @JoinColumn in case the owner side of the relation while the non-owner side we have used the mappedBy if we are going to establish a bidirectional association. By missing of @JoinColumn you’ve probably caused a headache (exception) for your program.