JPA @OneToOne Unidirectional and Bidirectional

THERE IS A NEW VERSION OF THIS POST.
CLICK HERE: http://uaihebert.com/?p=1674&page=19

Hello, good morning.

Let us talk today about the @OneToOne relationship Unidirectional and Bidirectional. We often need to map several types of relationships and the JPA make it easier through annotations.

We will use today a simple example, a Customer has a User. In this example the Customer can have only one User and vice-versa.

I will be using the java code from the older posts. If you want to create a project and run the code from this post, you can check the older posts about JPA. In the older posts you will find the configurations, downloads link and tutorials teaching how to set up a JPA application: Mapping two Tables in one Class, Mapping Date and Enum, Composite Primary-Key, SequenceGenerator, TableGenerator – Simple Primay Key, Auto Create Schema Script with: Ant, Hibernate 3 and JPA 2, Tutorial Hibernate 3 with JPA 2.

Let us see how a Unidirectional relationship looks like:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "CUSTOMER")
@SequenceGenerator(name = "CUSTOMER_SEQUENCE", sequenceName = "CUSTOMER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CUSTOMER_SEQUENCE")
    private int id;

    @Column
    private String name;

    @OneToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER, orphanRemoval = true)
    // @JoinColumn(name="USER_ID", nullable=false)
    @PrimaryKeyJoinColumn
    private User user;

    //Getters and Setters
}
package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name="USER")
@SequenceGenerator(name="USER_SEQUENCE", sequenceName="USER_SEQUENCE", allocationSize=1, initialValue=0)
public class User{

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator="USER_SEQUENCE")
    private int id;

    @Column
    private String login;

    @Column
    private String password;
    // Getters and Setters
}

About the @OneToOne annotations:

  • “cascade” – set triggers actions to the relationship. e.g.: If you delete a Customer the JPA can delete the User as well. We will talk about the “cascade” feature in another post.
  • “optional” – allow you to persist a Customer without a User; if true than you will not need to create a User before persisint the Customer (you will be able to find the Customer in the database, but if you do a Customer.getUser() you will get null as result). If your parameter value is false, when you persist a Customer you must have a User inside the Customer; you can set a User not persisted yet to the Csutomer.
  • “fetch” – the default value is EAGER; this value means that every time you search in the database for a Customer, the Customer User is search automatically. We will talk about this “fetch” feature in the future.
  • “orphanRemoval” – defines that a dependent entity without relationship must be removed from the database (in our model, the User depends on the Customer). If a User exists without a Customer, it will be deleted.

There are other two annotations to talk about, and one of them is commented (with //):

  • // @JoinColumn(name=“USER_ID”, nullable=“false”) – defines the database column that will be mapped into the attribute; it uses the parameter “name” to do this mapping and the “nullable” attribute is used to define if the attribute will required.
  • @PrimaryKeyJoinColumn – this annotation indicates to the JPA that to find a Customer User it just need to match the IDs; Our User will have the same ID that our Person.

To do a test, run the below code and you will see that a Customer and a User will be created; notice that the “persist” command is executed only in the Customer object:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = new Customer();
            customer.setName("John Doe");

            User user = new User();
            user.setLogin("jDoe");
            user.setPassword("123changeME!@#");

            customer.setUser(user);

            em.persist(customer);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Console Image.

If you want to search for the record in the database, you can use the below code:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = em.find(Customer.class, 1);

            System.out.println(customer.getName());
            System.out.println(customer.getUser().getLogin());

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Console Image.

Notice that through the Customer we could get its User.

To transform our relationship in a bidirectional relationship, we need to edit the User class:

package com;

//Using the * to make the import list smaller
import javax.persistence.*;

@Entity
@Table(name = "USER")
@SequenceGenerator(name = "USER_SEQUENCE", sequenceName = "USER_SEQUENCE", allocationSize = 1, initialValue = 0)
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_SEQUENCE")
    private int id;

    @Column
    private String login;

    @Column
    private String password;

    @OneToOne(mappedBy = "user")
    private Customer customer;

    //Getters and Setters
}

Notice that in the User class there is a reference to the Customer class; a @OneToOne annotation, in this reference, can be found. The parameter “mappedBy” indicates which class is the owner of this relationship, the stronger side. Pay attention to the fact that is not necessary to repeat any other mapping configuration, the JPA will use the Customer @OneToOne configurations.

What is “the thing” that makes Unidirectional not equal to the Bidirectional? When we had a Unidirectional relationship only the Customer class has a reference to the User class; you can only get a User by Customer.getUser() you can not do the other way. When we edit our User class we enabled the User to get the customer User.getCustomer().

A bidirectional relationship happens when both classes of a mapping contains multiple references. User HAS-A Customer and a Customer HAS-A User.

But, to persist into the database you will have to do add another code line:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            Customer customer = new Customer();
            customer.setName("John Doe");

            User user = new User();
            user.setLogin("jDoe");
            user.setPassword("123changeME!@#");

            // You must define a bidirectional relationship
			// like this
			customer.setUser(user);
            user.setCustomer(customer);

            em.persist(customer);

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Pay attention that now we need to use the method User.setCustomer(Customer) and the Customer.setUser(User). Remember that Java works with references; we must “point” the classes to each other.

We can use any side of the relationship to do a search in the database. What if we use the User to do this search? Take a look in our code:

package com;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("Hello");
        EntityManager em = emf.createEntityManager();

        try {
            em.getTransaction().begin();

            User user = em.find(User.class, 2);

            System.out.println(user.getLogin());
            System.out.println(user.getCustomer().getName());

            em.getTransaction().commit();
        }
        catch (Exception e) {
            em.getTransaction().rollback();
            e.printStackTrace();
        }
        finally{
            emf.close();
        }

        System.out.println("It is over");
    }
}

Console Image.

I hope this post might help you.

If you have any doubts/suggestions just post it.

See you soon.

6 thoughts on “JPA @OneToOne Unidirectional and Bidirectional

  1. Seems hacky but it works. You are using same generators for both tables so you get what you want but I don’t think it is about shared primary key. If you think I’m wrong please add some customers without adding users by hand. And then try to add customers and their users by your code. You will see your customers’ id’s will not be the same as their users’.

    • Hello Gokcen,
      I do not think you are wrong. [=
      In this post I wanted to show how to do a @OneToOne.
      There are other posts here in this blog just to talk about Id Generation.
      I think that everyone is free to choose any kind of id generation.
      I just used the AUTO because the focus here is to create relationship. [=
      And yes, the keys will not match, maybe I will refactor here to upgrade this. [=
      Thanks for passing by.

  2. Appreciated great efforts to share info but unfortunately it is not working i am using hibernate 4.1.9 using jboss hibernate plugin and code table are generated by plugin . i see the my table has blob type.

    javax.persistence.PersistenceException: org.hibernate.exception.DataException: Data truncation: Data too long for column ‘userId’ at row 1
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1377)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1306)
    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:871)
    at com.bhrikuti.employee.jpa.LoginDAO.save(LoginDAO.java:13)
    at com.bhrikuti.employee.jpa.UserTest.method1(UserTest.java:40)
    at com.bhrikuti.employee.jpa.UserTest.main(UserTest.java:7)

    • Hello raky, how are you?

      I believe that you are passing a value too big as ID.

      Data too long for column ‘userId’ at row 1

      Take a look at your ID fied in the database and the value that you are sending from the application.

      I am sorry for the late reply, I am really busy in this days. =/

  3. What you can say about this example for the MySQL? MySQL doesn’t support sequences, just primary keys with auto increments are available.
    It will be interesting to see what approaches can be applyed to this circumstance.

Leave a Comment