JPA @OneToMany and @ManyToOne Unidirectional and Bidirectional

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

Hello, good morning.

Let us see today about @OneToMany and @ManyToMany Unidirectional and Bidirectional relationship.

We can use the @OneToMany relationship with two annotations: @JoinColumn or @JoinTable.

I will use today the code from the older posts. If you want to set up an environment to run the code from this post, you can check the older posts: OneToOne Unidirectional and Bidirectional, 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.

I will use a simple user case, a Customer can have many dogs but a Dog can have only a Customer.

The Dog class:

package com;

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

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

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

    @Column
    private String name;

    //Getters and Setters
}

Notice that our Dog code is Unidirectional, it does not refer to the Customer class.

First we will see the Customer class with the @JoinTable annotation:

package com;

import java.util.List;

//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 = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="USER_ID", nullable=true)
    private User user;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinTable(name="CUSTOMER_HAS_DOGS", joinColumns={@JoinColumn(name="CUSTOMER_ID", referencedColumnName="id")}, inverseJoinColumns={@JoinColumn(name="DOG_ID", referencedColumnName="id")})
    private List dogs;

    //Getters and Setters
}

The @JoinTable annotation is simple and easy to use:

  • “name” – indicates which table will do the “join” between the Dog and the Customer (It is a good practice to put in the table name: owner + dependent side).
  • “joinColumns” – “let the JPA know” through an array of keys that will identify a record. It could be, e.g., ID and the name columns.
  • “@JoinColumn” – indicates a column to be the primary key of the owning side in the relationship. “name” is the column name and the “referencedColumnName” is the primary key of the related class; in this relationship the owner is the Customer class. (In the end of this post we will see who the real owner of this relationship is).
  • “inverseJoinColumns” – defines the mapped columns of the “weak/dominated” side of the relationship.

Let us add some data to the database. I will post bellow the code that will create a relationship between the Customer and Dog (if you want to execute the code bellow, check the older posts about JPA listed in the beginning of this post):

package com;

import java.util.ArrayList;
import java.util.List;

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();

            List dogs = new ArrayList();
            dogs.add(new Dog("Terminator"));
            dogs.add(new Dog("Fluffy"));
            dogs.add(new Dog("Bacon"));

            Customer customer = new Customer();
            customer.setName("Arnold");
            customer.setDogs(dogs);

            em.persist(customer);

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

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

Take a look at the results:

Console Image.

Database’s Image.

The JPA found the next sequence value to each object, inserted the objects and then inserted the relationship into the “CUSTOMER_HAS_DOGS”.

To use the @OneToMany annotation without an extra table, we can use the @JoinColumn annotation instead using the @JoinTable. Check out how our Customer class will look like:

package com;

import java.util.List;

//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 = true, fetch = FetchType.EAGER, orphanRemoval = true)
    @JoinColumn(name="USER_ID", nullable=true)
    private User user;

    @OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
    @JoinColumn(name="CUSTOMER_ID")
    private List dogs;

    //Getters and Setters
}

The @JoinColumn indicates which column will be used as the ID.

Delete the database Schema and create it again. Run the Main class again and check out the final result:

Console Image.

Database’s Image.

Notice that there is no need of another table in the system to do the relationship. In the Dog table, a new column was created to store this value.

To transform this Unidirectional relationship into a Bidirectional relationship, let us change our Dog class:

package com;

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

@Entity
@Table(name="DOG")
@SequenceGenerator(name="DOG_SEQUENCE", sequenceName="DOG_SEQUENCE", allocationSize=1, initialValue=0)
public class Dog {

    public Dog(){

    }

    public Dog(String name){
        this.name = name;
    }

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

    @Column
    private String name;

    @ManyToOne(cascade=CascadeType.ALL)
    private Customer owner;    

    //Getters and Setters
}

If you still with doubts about the concepts of Unidirectional and Bidirectional relationships, you can find the explanation here: JPA @OneToOne Unidirectional and Bidirectional.

By default, the annotation @ManyToOne is the one that should be the owner in a relationship with @OneToMany. If you check the @ManyToOne annotation API you will not find the “mappedBy” as parameter.

If you want to practice more, you could pass all the parameters of the @OneToMany (Customer class) to the @ManyToOne (Dog class), and put in the @OneToMany the “mappedBy” parameter.

I hope that you enjoyed this post.

Doubts/Suggestions just post it.

See you soon.

9 thoughts on “JPA @OneToMany and @ManyToOne Unidirectional and Bidirectional

  1. Hi, I tried your first example (One-To-Many Uni-directional). I’m really happy with the result but I still have a few question though.
    First of all, in my database, there appears a table with the 2 id’s. You can expect that from your code but should a One-To-Many relationship not just have a foreign key on the dog-table?
    Second, when I try to update my Customer-entity and I removed a couple of dogs from its list, the dogs are ‘removed’ in the join-table but not in the dogs-table. I tried removing them when I remove them from the list but because I’m working with EntityManager, it doesn’t allow removing detached entities.

    With kind regards.

    Renaud L.

    • Hello Renaud,

      I did not understand your questions. Sorry. =/

      If you have 2 ids for the same entity you should use composite key.

      If you want to delete an entity you must always sync it, you could use the entityManager.find() or the entityManager.getReference() methods of the JPA.

      Thanks for passing by

  2. I have user classes and unidirectional mapping document did , because I just need to see the document the user . Then save the document before you pick up the user add it to the document and save the document, but always the error saying the user id is null , but user seto in the document always has value.

    Below the link of my stack in doubt
    http://stackoverflow.com/questions/26620042/javax-ejb-ejbexception-javax-persistence-persistenceexception-org-hibernate-ex

  3. Hello,
    I have exactly have the same case as in :

    @OneToMany(cascade=CascadeType.ALL)
    @JoinTable(name=”CUSTOMER_HAS_DOGS”, joinColumns={@JoinColumn(name=”CUSTOMER_ID”, referencedColumnName=”id”)}, inverseJoinColumns={@JoinColumn(name=”DOG_ID”, referencedColumnName=”id”)})
    private List dogs;

    But, it is giving me an exception when I try to do delete/{customer_id}
    Caused by: java.sql.SQLIntegrityConstraintViolationException: ORA-02292: integrity constraint (MySCHEMA.FK_CUSTOMERHASDOGS_DOGID) violated – child record found.

    Any thoughts ?

    • Hello Pavan,

      You need to remove all relationships before removing.

      Maybe your DOG has other relationships.

      Thanks for passing by

Leave a Comment