1:1 relation

Full source code is provided in package: de.laliluna.relation.one2one If a class has a relation to another class then you have the option to map this as a relation or as a component. A relation is more useful if both class are used for themselves. Having a relation between order and invoice, you will probably have business methods dealing only with the order or only with the invoice, so a relation is the better choice.

Uni-directional

Order1 has a relation to invoice. Invoice1 does not have any notion of the relation.

Classes

Tables

images/c_relation_one2one_classes.jpg

images/c_relation_one2one_tables.jpg

Annotation mapping. 

import javax.persistence.CascadeType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
....... snip ..........
   @OneToOne(cascade = CascadeType.ALL)
   @JoinColumn(name = "invoice_id")
   private Invoice1 invoice;

@One2One specifies the relation. Cascade is explained in chapter xref:cascading. @JoinColumn defines the column in the order table having a foreign key relation to the invoice table. This annotation is optional. By default the column name targetClassName_id would be chosen.

XML mapping. The used tag is many-to-one in combination with unique set to true. This might be disturbing but it is the only way to define a uni-directional one-to-one relation. The one-to-one tag would not allow to configure the foreign key in the order table.

<hibernate-mapping package="de.laliluna.relation.one2one">
  <class name="Order1" table="torder">
....... snip .......
    <many-to-one name="invoice" class="Invoice1" cascade="all" unique="true">
      <column name="invoice_id"></column>
    </many-to-one>
  </class>
</hibernate-mapping>

The Invoice mapping file does not contain any tags related to this relation. It is uni-directional. Resulting tables:

CREATE TABLE tinvoice
(
  id int4 NOT NULL,
  number varchar(255),
  CONSTRAINT tinvoice_pkey PRIMARY KEY (id)
) ;
CREATE TABLE torder
(
  id int4 NOT NULL,
  number varchar(255),
  invoice_id int4,
  PRIMARY KEY (id),
  FOREIGN KEY (invoice_fk)
      REFERENCES tinvoice (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
) ;

Below you can find some usage samples:

/* create entries and set relation */
Order1 order = new Order1(null, "123");
Invoice1 invoice = new Invoice1(null, "456");
order.setInvoice(invoice);
session.save(order); // cascade will save invoice as well

/* delete an invoice*/
// reattach order (update) to bind it to the current session
session.update(order1); // cascade also updates the invoice
session.delete(order1.getInvoice());
order1.setInvoice(null);

/* select all order and initialize the invoices with one join (very fast) */
List<Order1> list = session.createQuery("from Order1 o left join fetch o.invoice")
   .list();

/*select orders where invoice number starts with 2 */
List<Order1> list = session.createQuery(
   "from Order1 o where o.invoice.number like '2%' ").list();

/*select some invoices where order number starts with 1 */
List<Invoice1> invoices = session.createQuery(
   "select o.invoice from Order1 o where o.number like '1%' ").list();

Bi-directional

Classes

Tables

images/c_relation_one2one2_classes.jpg

images/c_relation_one2one_tables.jpg

Annotation mapping. 

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
....... snip ......

@Entity
public class Order2 implements Serializable {

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "invoice_id")
private Invoice2 invoice;

@One2One specifies the relation. Cascade is explained in chapter xref:cascading. @JoinColumn defines the column in the order table having a foreign key relation to the invoice table. This annotation is optional. By default the column name targetClassName_id would be chosen.

import javax.persistence.Entity;
import javax.persistence.OneToOne;
....... snip ......

public class Invoice2 implements Serializable {
   @OneToOne(mappedBy="invoice")
   private Order2 order;

@OneToOne(mappedBy="invoice") defines that the relation is managed by the Order2 class and not by the Invoice2 class. Only if you assign the invoice in the Order2 class, Hibernate will reflect the relation in the database. Keep in mind that you have to set bi-directional relations on both sides, if you do not want to render your Hibernate session in an inconsistent state.

XML mapping. 

<hibernate-mapping package="de.laliluna.relation.one2one">
  <class name="Order2" table="torder2">
.......snip ........
    <many-to-one name="invoice" class="Invoice2" cascade="all" unique="true">
      <column name="invoice_id"></column>
    </many-to-one>
  </class>
</hibernate-mapping>

<hibernate-mapping package="de.laliluna.example2" >
  <class name="Invoice2" table="tinvoice2">
......... snip ........
   <one-to-one name="order" property-ref="invoice"/>
  </class>
</hibernate-mapping>

The resulting tables are the same as mentioned above. I found that the following works as well. The Hibernate reference proposes the foreign key reference.

<one-to-one name="order" class="Order2"/>

Now, let’s have a look at some samples of use. It is important that you always set the relations on both sides. If not, you will render your Hibernate session into an inconsistent state.

order.setInvoice(invoice);
invoice.setOrder(order);
session.save(order);
/* create and set relation */
Order2 order = new Order2(null, "123");
      Invoice2 invoice = new Invoice2(null, "456");
      // bi-directional set on both sides !!!
      order.setInvoice(invoice);
      invoice.setOrder(order);
      session.save(order); // cascade will save order as well

/* delete an invoice
* order is detached because the old session is closed, so reattach it using
* update */
      session.update(order); // cascade also updates the invoice
      session.delete(order.getInvoice());
      order.setInvoice(null);

/* find orders where invoice number starts with 2 */
List<Order2> list = session.createQuery(
            "from Order2 o where o.invoice.number like '2%' ").list();

/* find invoices where order number starts with 1 ");
      List<Invoice2> invoices = session.createQuery(
            "select i from Invoice2 i where i.order.number like '1%' ").list();

Relation to a primary key

The samples above use a foreign key relation. Sometimes both tables should share the same primary key. In this case we need a relation to the primary key. The primary key of the second table is set to the same value as the table of the first table. This is managed by Hibernated.

Classes

Tables

images/c_relation_one2one3_classes.jpg

images/c_relation_one2one3_tables.jpg

Annotation mapping. 

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
....... snip ......

@Entity
public class Invoice3 implements Serializable {

   @Id
   @GeneratedValue(generator = "foreign_id")
   @GenericGenerator(name = "foreign_id", strategy = "foreign", parameters = {
      @Parameter(name = "property", value = "order") })
   private Integer id;

   @OneToOne(cascade = CascadeType.ALL,optional=false)
   @PrimaryKeyJoinColumn
   private Order3 order;

The Order3 class does not contain any relation specific annotation.

XML mapping. 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="de.laliluna.example2" >
  <class name="Invoice3" table="tinvoice3">
    <id name="id" >
      <generator class="foreign">
       <param name="property" >order</param>
      </generator>
    </id>

     <one-to-one name="order" class="Order3"  />
........ snip ........

  </class>
</hibernate-mapping>

You can add a constraint as well:

<one-to-one name="order" class="Order3" constrained="true" />

Table structure. 

CREATE TABLE tinvoice3
(
  id int4 NOT NULL,
  number varchar(255),
  PRIMARY KEY (id),
  FOREIGN KEY (id)
      REFERENCES torder3 (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
) ;
CREATE TABLE torder3
(
  id int4 NOT NULL,
  number varchar(255),
  PRIMARY KEY (id)
) ;

Samples of use:

/* create and set relation */
Order3 order = new Order3(null, "123");
Invoice3 invoice3 = new Invoice3(null, "456");
invoice3.setOrder(order);
session.save(order);
session.save(invoice3);

/* delete an invoice */
session.buildLockRequest(LockOptions.NONE).lock(invoice3); // reattach using lock
session.delete(invoice3);