Hibernate one-to-many bidirectional mapping using annotation

Hi friends,

It’s the time to explicate hibernate one-to-many bidirectional mapping.

One-to-many means, one parent can have one or more children. A child of one particular parent can’t be the child of another parent. That’s the significant difference between one-to-many mapping and many-to-many mapping.

What bidirectional mapping over here is, we could easily find out parent entity’s properties by using child entities.

So, move on.

My DAO class, DAO.java.

package hba.dao;

import hba.util.HibernateUtil;
import org.hibernate.FlushMode;
import org.hibernate.Session;

public class DAO {

    private static final ThreadLocal THREAD = new ThreadLocal();

    protected DAO() {
    }

    public static Session getSession() {
        Session session = (Session) DAO.THREAD.get();
        if (session == null) {
            session = HibernateUtil.getSessionFactory().openSession();
            DAO.THREAD.set(session);
            getSession().setFlushMode(FlushMode.COMMIT);
        }
        return session;
    }

    protected static void begin() {
        getSession().beginTransaction();
    }

    protected static void commit() {
        getSession().getTransaction().commit();
    }

    protected static void rollback() {
        getSession().getTransaction().rollback();
        getSession().close();
        DAO.THREAD.set(null);
    }

    protected static void flush() {
        getSession().flush();
    }

    protected static void close() {
        getSession().close();
        DAO.THREAD.set(null);
    }
}

The hibernate configuration file, hibernate.cfg.xml.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/hba</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">password</property>
        <property name="hibernate.show_sql">true</property>
        <property name="hibernate.hbm2ddl.auto">update</property>
        <mapping class="hba.pojo.o2m_b_author"/>
        <mapping class="hba.pojo.o2m_b_book"/>
    </session-factory>
</hibernate-configuration>

The parent POJO, o2m_b_author.java.

package hba.pojo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "AUTHOR", catalog = "hba")
public class o2m_b_author implements Serializable {

    private String penName, name;
    private List<o2m_b_book> books = new ArrayList<o2m_b_book>();

    @OneToMany(mappedBy = "author", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    public List<o2m_b_book> getBooks() {
        return books;
    }

    @Column(name = "AUTH_NAME", length = 20, nullable = false)
    public String getName() {
        return name;
    }

    @Id
    @Column(name = "AUTH_PEN", length = 20)
    public String getPenName() {
        return penName;
    }

    public void setPenName(String penName) {
        this.penName = penName;
    }

    public void setBooks(List<o2m_b_book> books) {
        this.books = books;
    }

    public void setName(String name) {
        this.name = name;
    }
}

The child POJO, o2m_b_book.java.

package hba.pojo;

import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "BOOK", catalog = "hba")
public class o2m_b_book implements Serializable {

    private String isbn, name;
    private o2m_b_author author;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "BOOK_AUTHOR")
    public o2m_b_author getAuthor() {
        return author;
    }

    @Id
    @Column(name = "BOOK_ISBN", length = 20)
    public String getIsbn() {
        return isbn;
    }

    @Column(name = "BOOK_NAME", length = 20, nullable = false)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public void setAuthor(o2m_b_author author) {
        this.author = author;
    }

    public o2m_b_book(String isbn, String name, o2m_b_author author) {
        this.isbn = isbn;
        this.name = name;
        this.author = author;
    }

    public o2m_b_book() {
    }
}

The save() method of o2m_b_author_book.java.

package hba.biz;

import hba.dao.DAO;
import hba.pojo.o2m_b_author;
import hba.pojo.o2m_b_book;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.criterion.Expression;

public class o2m_b_author_book extends DAO {

    private List<o2m_b_book> books = new ArrayList<o2m_b_book>();
    private o2m_b_author author = new o2m_b_author();
    private o2m_b_book book1 = new o2m_b_book();
    private o2m_b_book book2 = new o2m_b_book();

    public void save() throws Exception {
        try {
            begin();
            book1.setAuthor(author);
            book1.setIsbn("8176212032");
            book1.setName("WAR AND PEACE");
            book2.setAuthor(author);
            book2.setIsbn("0981834191");
            book2.setName("THE EMPTY BOAT");
            books.add(book1);
            books.add(book2);
            author.setName("OSHO RAJNEESH");
            author.setPenName("OSHO");
            author.setBooks(books);
            getSession().save(author);
            commit();
        } catch (Exception e) {
            rollback();
            System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());
        } finally {
            flush();
            close();
        }
    }

    public static void main(String[] args) {
        try {
            o2m_b_author_book ab = new o2m_b_author_book();
            ab.save();
        } catch (Exception e) {
            System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());
        }
    }
}

What happens here is, we are creating two book objects namely book1 and book2 and creating an author. After setting basic properties of books and author, we are mapping those books to the author and setting author of each book. Finally, the author object is saved. This is not the way how we have done the one-to-many unidirectional mapping. There’s one more difference that we are not creating a join table here for mapping.

The output of the above save() method will be.

Hibernate: select o2m_b_book_.BOOK_ISBN, o2m_b_book_.BOOK_AUTHOR as BOOK3_11_, o2m_b_book_.BOOK_NAME as BOOK2_11_ from hba.BOOK o2m_b_book_ where o2m_b_book_.BOOK_ISBN=?
Hibernate: select o2m_b_book_.BOOK_ISBN, o2m_b_book_.BOOK_AUTHOR as BOOK3_11_, o2m_b_book_.BOOK_NAME as BOOK2_11_ from hba.BOOK o2m_b_book_ where o2m_b_book_.BOOK_ISBN=?
Hibernate: insert into hba.AUTHOR (AUTH_NAME, AUTH_PEN) values (?, ?)
Hibernate: insert into hba.BOOK (BOOK_AUTHOR, BOOK_NAME, BOOK_ISBN) values (?, ?, ?)
Hibernate: insert into hba.BOOK (BOOK_AUTHOR, BOOK_NAME, BOOK_ISBN) values (?, ?, ?)

Another way of designing above mentioned POJO classes looks like this. This will create a join column, namely author_penName, in book table.

package hba.pojo;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "AUTHOR", catalog = "hba")
public class o2m_b_author implements Serializable {

    private String penName, name;
    private List<o2m_b_book> books = new ArrayList<o2m_b_book>();

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "author_penName")
    public List<o2m_b_book> getBooks() {
        return books;
    }

    @Column(name = "AUTH_NAME", length = 20, nullable = false)
    public String getName() {
        return name;
    }

    @Id
    @Column(name = "AUTH_PEN", length = 20)
    public String getPenName() {
        return penName;
    }

    public void setPenName(String penName) {
        this.penName = penName;
    }

    public void setBooks(List<o2m_b_book> books) {
        this.books = books;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package hba.pojo;

import java.io.Serializable;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "BOOK", catalog = "hba")
public class o2m_b_book implements Serializable {

    private String isbn, name;
    private o2m_b_author author;

    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "author_penName")
    public o2m_b_author getAuthor() {
        return author;
    }

    @Id
    @Column(name = "BOOK_ISBN", length = 20)
    public String getIsbn() {
        return isbn;
    }

    @Column(name = "BOOK_NAME", length = 20, nullable = false)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public void setAuthor(o2m_b_author author) {
        this.author = author;
    }

    public o2m_b_book(String isbn, String name, o2m_b_author author) {
        this.isbn = isbn;
        this.name = name;
        this.author = author;
    }

    public o2m_b_book() {
    }
}

This time we are free to eliminate reverse mapping. The new save() method looks like this.

package hba.biz;

import hba.dao.DAO;
import hba.pojo.o2m_b_author;
import hba.pojo.o2m_b_book;
import java.util.ArrayList;
import java.util.List;

public class o2m_b_author_book extends DAO {

    private List<o2m_b_book> books = new ArrayList<o2m_b_book>();
    private o2m_b_author author = new o2m_b_author();
    private o2m_b_book book1 = new o2m_b_book();
    private o2m_b_book book2 = new o2m_b_book();

    public void save() throws Exception {
        try {
            begin();
            book1.setIsbn("8176212032");
            book1.setName("WAR AND PEACE");
            book2.setIsbn("0981834191");
            book2.setName("THE EMPTY BOAT");
            books.add(book1);
            books.add(book2);
            author.setName("OSHO RAJNEESH");
            author.setPenName("OSHO");
            author.setBooks(books);
            getSession().save(author);
            commit();
        } catch (Exception e) {
            rollback();
            throw new Exception("Exception in save." + e.getMessage());
        } finally {
            flush();
            close();
        }
    }

    public static void main(String[] args) {
        try {
            o2m_b_author_book ab = new o2m_b_author_book();
            ab.save();
        } catch (Exception e) {
            System.out.println("Exception in main." + e.getMessage());
        }
    }
}

And the output will be

Hibernate: select o2m_b_book_.BOOK_ISBN, o2m_b_book_.author_penName as author3_11_, o2m_b_book_.BOOK_NAME as BOOK2_11_ from hba.BOOK o2m_b_book_ where o2m_b_book_.BOOK_ISBN=?
Hibernate: select o2m_b_book_.BOOK_ISBN, o2m_b_book_.author_penName as author3_11_, o2m_b_book_.BOOK_NAME as BOOK2_11_ from hba.BOOK o2m_b_book_ where o2m_b_book_.BOOK_ISBN=?
Hibernate: insert into hba.AUTHOR (AUTH_NAME, AUTH_PEN) values (?, ?)
Hibernate: insert into hba.BOOK (author_penName, BOOK_NAME, BOOK_ISBN) values (?, ?, ?)
Hibernate: insert into hba.BOOK (author_penName, BOOK_NAME, BOOK_ISBN) values (?, ?, ?)
Hibernate: update hba.BOOK set author_penName=? where BOOK_ISBN=?
Hibernate: update hba.BOOK set author_penName=? where BOOK_ISBN=?

See and understand the difference in output, generated by our two different approaches.
In both cases, our table design would be the very same.

Now we are retrieving author’s details, the listAuthor() method.

package hba.biz;

import hba.dao.DAO;
import hba.pojo.o2m_b_author;
import hba.pojo.o2m_b_book;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.hibernate.criterion.Expression;

public class o2m_b_author_book extends DAO {

    private o2m_b_author author = new o2m_b_author();

    public void listAuthor() throws Exception {
        try {
            begin();
            List<o2m_b_author> list = getSession().createCriteria(o2m_b_author.class).add(Expression.eq("penName", "OSHO")).list();
            System.out.println("List : " + list);
            for (o2m_b_author author : list) {
                System.out.println("Name of the Author : " + author.getName());
                List<o2m_b_book> authorBooks = author.getBooks();
                for (Iterator it = authorBooks.iterator(); it.hasNext();) {
                    o2m_b_book book = (o2m_b_book) it.next();
                    System.out.println("Book : " + book.getName());
                }
            }
        } catch (Exception e) {
            rollback();
           System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());
        } finally {
            flush();
            close();
        }
    }

    public static void main(String[] args) {
        try {
            o2m_b_author_book ab = new o2m_b_author_book();
            ab.listAuthor();
        } catch (Exception e) {
            System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());

        }
    }
}

Output of the above method will be


Hibernate: select this_.AUTH_PEN as AUTH1_10_0_, this_.AUTH_NAME as AUTH2_10_0_ from hba.AUTHOR this_ where this_.AUTH_PEN=?
List : [hba.pojo.o2m_b_author@458f41]
Name of the Author : OSHO RAJNEESH
Hibernate: select books0_.BOOK_AUTHOR as BOOK3_1_, books0_.BOOK_ISBN as BOOK1_1_, books0_.BOOK_ISBN as BOOK1_11_0_, books0_.BOOK_AUTHOR as BOOK3_11_0_, books0_.BOOK_NAME as BOOK2_11_0_ from hba.BOOK books0_ where books0_.BOOK_AUTHOR=?
Book : THE EMPTY BOAT
Book : WAR AND PEACE

Here, the advantage of bidirectional mapping arrives. Here we are querying book details. The result of the query contain author details as well.

The listBook() method.

package hba.biz;

import hba.dao.DAO;
import hba.pojo.o2m_b_author;
import hba.pojo.o2m_b_book;
import java.util.Iterator;
import java.util.List;

public class o2m_b_author_book extends DAO {

    private o2m_b_author author = new o2m_b_author();

    public void listBook() throws Exception {
        try {
            begin();
            List<o2m_b_book> list = getSession().createQuery("from o2m_b_book where isbn='8176212032'").list();
            System.out.println("List : " + list);
            for (o2m_b_book book : list) {
                author = book.getAuthor();
                System.out.println("Name of the author : " + author.getName());
                List<o2m_b_book> bookList = author.getBooks();
                for (Iterator it = bookList.iterator(); it.hasNext();) {
                    book = (o2m_b_book) it.next();
                    System.out.println("Book : " + book.getName());
                }
            }
            commit();
        } catch (Exception e) {
            rollback();
            System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());
        } finally {
            flush();
            close();
        }
    }
    public static void main(String[] args) {
        try {
            o2m_b_author_book ab = new o2m_b_author_book();
            ab.listBook();
        } catch (Exception e) {
            System.out.println("Exception : " + e.getMessage() + "Cause : " + e.getCause());

        }
    }
}

Output of the above method will be

Hibernate: select o2m_b_book0_.BOOK_ISBN as BOOK1_11_, o2m_b_book0_.BOOK_AUTHOR as BOOK3_11_, o2m_b_book0_.BOOK_NAME as BOOK2_11_ from hba.BOOK o2m_b_book0_ where o2m_b_book0_.BOOK_ISBN='8176212032'
Hibernate: select o2m_b_auth0_.AUTH_PEN as AUTH1_10_0_, o2m_b_auth0_.AUTH_NAME as AUTH2_10_0_ from hba.AUTHOR o2m_b_auth0_ where o2m_b_auth0_.AUTH_PEN=?
List : [hba.pojo.o2m_b_book@6a3960]
Name of the author : OSHO RAJNEESH
Hibernate: select books0_.BOOK_AUTHOR as BOOK3_1_, books0_.BOOK_ISBN as BOOK1_1_, books0_.BOOK_ISBN as BOOK1_11_0_, books0_.BOOK_AUTHOR as BOOK3_11_0_, books0_.BOOK_NAME as BOOK2_11_0_ from hba.BOOK books0_ where books0_.BOOK_AUTHOR=?
Book : THE EMPTY BOAT
Book : WAR AND PEACE

Now you could see that, by querying a book using its isbn number, we could easily get its author details as well as the details of other books written by that author.

Thanks.