Saturday 26 November 2011

JPA one-to-one relationship

Basic one-to-one unidirectional mapping

@Entity
@Table(name="T_CUSTOMER")
public class Customer {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="CUST_ID")
    private int id;
    
    @Column(name="CUST_NAME")
    private String name;
    
    @OneToOne
    private Address address;
    
}

@Entity
@Table(name="T_ADDRESS")
public class Address {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ADDR_ID")
    private int id;
    
    @Column(name="STREET")
    private String street;
    
    @Column(name="POSTCODE")
    private String postcode;
    
}

Domain model


T_CUSTOMER.ADDRESS_ADDR_ID is the foreign key referenced to T_ADDRESS.ADDR_ID

ADDRESS_ADDR_ID is the default foreign key column name. It is derived from class name + ‘_’ + primary key name (‘ADDRESS’ + ‘_’+’ADDR_ID’)

Since by default, no operations are cascaded, Address entity needs to be saved before Customer entity does.

@PersistenceContext(unitName="unit")
private EntityManager em;

public void save(){
    Address address = new Address("10 test st", "2912");
    Customer customer = new Customer("Sun", address);
    //Save address to database
    em.save(address);
    //Save customer to database
    em.save(customer);
    System.out.println("Customer saved");
}

The result:

T_CUSTOMER


T_ADDRESS


Add cascade attribute

@OneToOne(cascade={CascadeType.PERSIST, CascadeType.MERGE, 
        CascadeType.REMOVE})
private Address address;

Now Address entity does not need to be saved before Customer entity does.

public void save(){
    Address address = new Address("10 test st", "2912");
    Customer customer = new Customer("Sun", address);
    //Save customer to database
    em.save(customer);
    System.out.println("Customer saved");
} 

Same for update and delete.

Specify whether association is optional, fetch type

@OneToOne(cascade={CascadeType.PERSIST, 
        CascadeType.MERGE, CascadeType.REMOVE}, 
        optional=false, fetch=FetchType.EAGER)
private Address address;

Default value of optional attribute is true
Default value of fetch attribute is EAGER

Now if trying to save a customer without setting his address, an exception will be thrown.

@PersistenceContext(unitName="unit")
private EntityManager em;

public void save(){
    Address address = new Address("10 test st", "2912");
    Customer customer = new Customer("Sun", address);
    //Save customer to database
    em.save(customer);
    System.out.println("Customer saved");
}

Specify foreign key column name

@OneToOne(cascade={CascadeType.PERSIST, 
        CascadeType.MERGE, CascadeType.REMOVE}, 
        optional=false, fetch=FetchType.EAGER)
@JoinColumn(name="F_ADDR_ID", referencedColumnName="ADDR_ID")
private Address address;

Domain model


The name attribute of @JoinColumn (‘F_ADDR_ID’) specifies the foreign key column name.

The referenceColumnName attribute of @JoinColumn (‘ADDR_ID’) specifies the primary key of the table referenced to. It can be omitted.

One-to-one bidirectional mapping

@Entity
@Table(name="T_CUSTOMER")
public class Customer {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="CUST_ID")
    private int id;
    
    @Column(name="CUST_NAME")
    private String name;
    
    @OneToOne(cascade={CascadeType.ALL}, optional=false)
    @JoinColumn(name="F_ADDR_ID")
    private Address address;
    
}

@Entity
@Table(name="T_ADDRESS")
public class Address {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="ADDR_ID")
    private int id;
    
    @Column(name="STREET")
    private String street;
    
    @Column(name="POSTCODE")
    private String postcode;
    
    @OneToOne(mappedBy="address")
    private Customer customer;    
}

Domain model

Same as unidirectional mapping


@PersistenceContext(unitName="unit")
private EntityManager em;

public void save(){
    Address address = new Address("10 test st", "2912");
    Customer customer = new Customer("Sun", address);
    address.setCustomer(customer);
    //Save customer to database
    em.save(customer);
    System.out.println("Customer saved");
}