Entity Relationship On Hibernate JPA

This article is tested against Java EE 6, Hibernate 4.2.2.

One To Many Unidirectional Relationship

Unidirectional here means you can only obtain related entities from one side. For example supposed we have one to many Customer --> Account relationship, here’s what Customer entity look like

@Entity
public class Customer {

  @Id
  @GeneratedValue
  private long id;

  private String name;

  @OneToMany
  @JoinColumn(name = "customer_id")
  private Set<Account> accounts;

  // getters & setters..
}

Instead of Set, you can use List, however if you have more than one relationship in your entity class, you will get MultipleBagFetchException.

And This is what Account looks like. Notice there’s no reference to Customer object from Account.

@Entity
public class Account {

  @Id
  @GeneratedValue
  private long id;
  
  private double balance;

  // Getters & Setters ..
}

One To Many Bidirectional Relationship

Similar to unidirectional except you can obtain relation from both direction. This is what Customer and Account look like:

@Entity
public class Customer {

  @Id
  @GeneratedValue
  private long id;

  private String name;

  @OneToMany(mappedBy = "customer")
  private Set<Account> accounts;

  // getters & setters..
}

The “customer” on @OneToMany(mappedBy = "customer") refers to customer java property on Account entity.

@Entity
public class Account {

  @Id
  @GeneratedValue
  private long id;
  
  private double balance;

  @ManyToOne
  @JoinColumn(name = "customer_id")
  private Customer customer;

  // Getters & Setters ..
}

The @JoinColumn(name = "customer_id") annotation sets the database foreign key column name to be “customer_id”.

Lazy / Eager Loading

Lazy / Eager refers to when does entity relationship get populated. Eager means it will get populated as soon as the entity is obtained from database, where in Lazy it’s only populated when you request the relationship for the first time.

The default value for @OneToMany is LAZY, but for @ManyToOne it’s EAGER. It’s a good practice to explicitly configure it so your code is easier to understand

@Entity
public class Customer {
  ...
  @OneToMany(mappedBy = "customer", fetch = FetchType.LAZY)
  private Set<Account> accounts;
  ...
}
@Entity
public class Account {
  ...
  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "customer_id")
  private Customer customer;
  ...
}

LazyInitializationException: could not initialize proxy – no Session

Almost every Hibernate/JPA noobie struggle with this exception when they started learning (including myself). This is basically saying you’re tring to access unfetched data after transaction has been closed.

If you use Spring declarative transaction management, transaction start and finish mark are declared by simply annotating DAO method with @Transactional. Let’s see a common failure where LazyInitializationException can happen.

Here’s your CustomerDAO class:

@Repository
public class CustomerDAO {
  
  @PersistenceContext private EntityManager em;

  @Transactional
  public List<Customer> getAllCustomers() {
    List<Customer> customers = em.createQuery("select c from Customer", 
      Customer.class).getResultList();
    return customers;
  }

Then on your controller you populate your Model object with list of Customers, and on your view your JSP tried to access Accounts associated with each customer:

<h1>Customer Info</h1>
Id: ${customer.id}
Name: ${customer.name}
Accounts: <ul>
  <c:forEach var="acc" items="${customer.accounts}">
    <li>Acc Id: ${acc.id}, Balance: ${acc.balance}</li>
  </c:forEach>
</ul>

Since the transaction is already closed when CustomerDAO.getAllCustomers() method returns, this will throw LazyInitializationException.

My preferred strategy to overcome this problem is to prefetch the related entities on the DAO, so all the information we need is available on view level in detached state. Other strategy being opening a transaction / session while view is being generated (see: OpenSessionInViewFilter). To achieve this, change the getAllCustomers() DAO method to use JPQL left join fetch syntax like this:

@Repository
public class CustomerDAO {
  ...
  @Transactional
  public List<Customer> getAllCustomers() {
    List<Customer> customers = em.createQuery(
      "select distinct c from Customer " +
      "left join fetch c.accounts", 
      Customer.class).getResultList();
    return customers;
  }
  ...
}

Now you can iterate and access all related accounts entities without exceptions.

Hibernate One To Many Relationship

Continuing from my post Spring MVC + Hibernate + MySQL Quick Start From Scratch, here is how to setup one to many relationship on Hibernate.

Let’s assume you want to add a new table called topping, each pizza can have several toppings (one to many relationship).

Pizza table
pizzatable

Topping table
topping table

First code the Topping entity class:

@Entity
@Table(name = "topping")
public class Topping {
  @Id
  @GeneratedValue
  private long id;
  
  private String name;
  
  @ManyToOne
  @JoinColumn(name = "pizza_id", referencedColumnName = "id")
  private Pizza pizza;

  // getters & setters..
}

Notice the usage of @ManyToOne and @JoinColumn annotation. This allows the owning Pizza to be queried from the Topping. This also says “in order to find pizza that owns this topping, have a look at pizza_id column on topping table, match it with id column on pizza table”

And let’s have a look at our modified Pizza entity class:

@Entity
@Table(name = "pizza")
public class Pizza {
  @Id @GeneratedValue private long id;
  private String name;
  private double price;
  
  @OneToMany(mappedBy = "pizza", fetch = FetchType.LAZY)
  private List<Topping> toppings;

  // getters & setters
}

This setups a bi-directional relationship where you can obtain list of toppings from a Pizza object, and the owning Pizza from a Topping object.

The FetchType.LAZY setting tells Hibernate not to bother populating the toppings unless we ask it to (this is handy to conserve memory if you have a long chains of relationships with thousands of objects on it).

Next if I want to list my pizzas with its topping, I can write following method on my DAO class:

@Transactional
public List<Pizza> findAllWithToppings() {
  Session session = sessionFactory.getCurrentSession();
  List pizzas = session.createQuery("select distinct p from Pizza as p left join fetch p.toppings").list();
  return pizzas;
}

The above hibernate query will roughly translate into following SQL:

select * from pizza p left outer join topping t on p.id = t.pizza_id;

Download the source code

git clone https://gerrytan@bitbucket.org/gerrytan/pizzashop.git -b one_to_many

Spring MVC + Hibernate + MySQL Quick Start From Scratch

This is a tutorial to build a very simple pizzashop Spring MVC, Hibernate and MySQL application. The main page shows all pizza rows stored on a database table:

03

Technology in Java world moves very fast. Using straight Hibernate might no longer be preferable since Java EE 5 introduces JPA (Java Persistence API) but it’s still good to learn anyway. Spring MVC and Hibernate is one of the most popular Java libraries used out there.

Tools / Environment Required
If you’re just starting fresh and don’t have most of the tools below just install JDK, STS (Springsource Tools Suite) and MySQL server, everything else is bundled within them.

Project Setup and Boilerplate

  1. First, create new Maven project on STS. Skip archetype selection. Pick maven group id, artifact id and select packaging to war.01 02
  2. By default Maven will use JDK 1.5, re-configure it to use 1.6. Add following maven-compiler-plugin section to¬†pom.xml under <pom> element. Ensure the change takes effect by updating maven project settings (right click on project -> Maven -> Update project…).
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.1</version>
          <configuration>
            <source>1.6</source>
            <target>1.6</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  3. Add Spring, Hibernate, Java EE and MySQL maven dependencies to pom.xml. Place following under <pom> element.
    <properties>
      <spring.version>3.2.3.RELEASE</spring.version>
      <hibernate.version>4.2.2.Final</hibernate.version>
    </properties>
    
    <dependencies>
      <!-- Spring -->
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
      </dependency>
      <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
      </dependency>
      
      <!-- Hibernate -->
      <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>${hibernate.version}</version>
      </dependency>
      
      <!-- Java EE -->
      <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>jstl</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
      </dependency>
      
      <!-- Others -->
      <dependency>
        <groupId>commons-dbcp</groupId>
        <artifactId>commons-dbcp</artifactId>
        <version>1.4</version>
      </dependency>
      <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.25</version>
      </dependency>
      
    </dependencies>
    
  4. Create a web.xml descriptor file with Spring MVC servlet setup on it, place it on src/main/webapp/WEB-INF
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    
      <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
          <param-name>contextConfigLocation</param-name>
          <param-value>/WEB-INF/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
       
      <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
      </servlet-mapping>
      
    </web-app>
    
  5. Create src/main/webapp/WEB-INF/servlet-context.xml Spring bean config file for dispatcher servlet. We configure few important stuffs in here:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:beans="http://www.springframework.org/schema/beans"
      xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
      xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
    
      <!-- Enable @Controller annotation support -->
      <mvc:annotation-driven />
    
      <!-- Map simple view name such as "test" into /WEB-INF/views/test.jsp -->
      <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/" />
        <property name="suffix" value=".jsp" />
      </bean>
      
      <!-- Scan classpath for annotations (eg: @Service, @Repository etc) -->
      <context:component-scan base-package="com.gerrytan.pizzashop"/>
      
      <!-- JDBC Data Source. It is assumed you have MySQL running on localhost port 3306 with 
           username root and blank password. Change below if it's not the case -->
      <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/pizzashop"/>
        <property name="username" value="root"/>
        <property name="password" value=""/>
        <property name="validationQuery" value="SELECT 1"/>
      </bean>
      
      <!-- Hibernate Session Factory -->
      <bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="myDataSource"/>
        <property name="packagesToScan">
          <array>
            <value>com.gerrytan.pizzashop</value>
          </array>
        </property>
        <property name="hibernateProperties">
          <value>
            hibernate.dialect=org.hibernate.dialect.MySQLDialect
          </value>
        </property>
      </bean>
      
      <!-- Hibernate Transaction Manager -->
      <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="mySessionFactory"/>
      </bean>
      
      <!-- Activates annotation based transaction management -->
      <tx:annotation-driven transaction-manager="transactionManager"/>
    </beans>
    
    


The Business Functionality

  1. Now all the boilerplate code done, we can start coding the business functionality. For this simple app we will have a pizza database table with just id, name and price column. Create the Pizza entity class representing the table
    package com.gerrytan.pizzashop;
    // imports ..
    
    @Entity
    @Table(name = "pizza")
    public class Pizza {
      @Id @GeneratedValue private long id;
      private String name;
      private double price;
      /* getters & setters */
    }
    
  2. Create a DAO class to obtain Pizza entity persisted on database. Note that we won’t create service layer classes for the sake of simplicity (on real-life complex business application adding service layer is a good practice).
    package com.gerrytan.pizzashop;
    // imports..
    
    @Repository
    @SuppressWarnings({"unchecked", "rawtypes"})
    public class PizzaDAO {
      @Autowired private SessionFactory sessionFactory;
      
      /**
       * @Transactional annotation below will trigger Spring Hibernate transaction manager to automatically create
       * a hibernate session. See src/main/webapp/WEB-INF/servlet-context.xml
       */
      @Transactional
      public List<Pizza> findAll() {
        Session session = sessionFactory.getCurrentSession();
        List pizzas = session.createQuery("from Pizza").list();
        return pizzas;
      }
    }
    
  3. Create a Spring MVC controller to handle request from the main page. Note that PizzaDAO reference is injected, and after collection of Pizza entity objects are obtained it’s passed to the view using Model object.
    package com.gerrytan.pizzashop;
    // imports..
    
    @Controller
    @RequestMapping("/")
    public class PizzaController {
      
      @Autowired private PizzaDAO pizzaDAO;
      
      /**
       * This handler method is invoked when
       * http://localhost:8080/pizzashop is requested.
       * The method returns view name "index"
       * which will be resolved into /WEB-INF/index.jsp.
       *  See src/main/webapp/WEB-INF/servlet-context.xml
       */
      @RequestMapping(method = RequestMethod.GET)
      public String list(Model model) {
        List<Pizza> pizzas = pizzaDAO.findAll();
        model.addAttribute("pizzas", pizzas);
        return "index";
      }
    }
    
  4. Add a JSP view to list all the pizza entities passed by the controller
    src/main/webapp/WEB-INF/index.jsp

    <%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
    <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
    <title>Pizzashop</title>
    </head>
    <body>
    	<h1>List of All Pizzas</h1>
    	<ul>
    		<c:forEach var="p" items="${pizzas}">
    			<li>${p.id} - ${p.name} - ${p.price}</li>
    		</c:forEach>
    	</ul>
    </body>
    </html>
    
  5. And finally prepare MySQL pizzashop database schema:
    CREATE SCHEMA `pizzashop`;

    And pizza table:

    CREATE  TABLE `pizzashop`.`pizza` (
      `id` BIGINT NOT NULL AUTO_INCREMENT ,
      `name` VARCHAR(45) NULL ,
      `price` DOUBLE NULL ,
      PRIMARY KEY (`id`) );
    

    And insert some data into it

    INSERT INTO `pizzashop`.`pizza` (`id`, `name`, `price`) VALUES ('I', 'Italian', '7.5');
    INSERT INTO `pizzashop`.`pizza` (`id`, `name`, `price`) VALUES ('2', 'Thin Crust', '6');
    INSERT INTO `pizzashop`.`pizza` (`id`, `name`, `price`) VALUES ('3', 'Pepperoni', '6.2');
    

    It is assumed you have MySQL server running on your local machine (localhost) on the default port 3306. The name of the schema is pizzashop and table pizza. Have a look at Spring beans configuration section above to reconfigure this.

Running The Code

  1. The easiest way to run your code is using in-memory Maven Tomcat plugin. It will launch on-the-fly Tomcat server with your code deployed. Make sure your project is selected on the project exploded and setup a maven run configuration on STS. Go to Run -> Run Configurations.. and create a new maven build entry like this:
    maven goal
  2. Click Run and navigate your browser to http://localhost:8080/pizzashop

Download The Source Code

Source code for this demonstration can be obtained using git:

git clone https://bitbucket.org/gerrytan/pizzashop.git -b basic

Congratz!

Well done on making it this far. Hopefully that was a quick and nice introduction to Spring MVC + Hibernate + MySQL and you can see how the tedious database to java class mapping is now simplified. You might have lots of questions in your mind by now — feel free to ask in the comment section below. Following are few official references and community article links you can browse around:

Also checkout my next tutorial about Hibernate One To Many Entity Relationship.

Java EE & Spring Cheatsheet

JSTL 1.2 Core JSP Taglib (aka c tag)

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

JSTL implementation jar has to exist on the classpath, eg add following dependency (in provided scope if container doesn’t provides it)

<dependency>
  <groupId>jstl</groupId>
  <artifactId>jstl</artifactId>
  <version>1.2</version>
</dependency>

See this blog post for more info.

Spring Taglib

  • Spring:
    <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
  • Form:
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>

JSTL fmt Taglib

<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>

web.xml deployment descriptor

  • v2.3
    <!DOCTYPE web-app PUBLIC
     "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
     "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <web-app>
      
    </web-app>
    
  • v3.0
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="3.0"
             xmlns="http://java.sun.com/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
        
    </web-app>
    

Commons DBCP / MySQL Spring Bean Config

<!-- JDBC Data Source -->
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/hibernatefiddle"/>
  <property name="username" value="root"/>
  <property name="password" value=""/>
  <property name="validationQuery" value="SELECT 1"/>
  <property name="testWhileIdle" value="true"/>
</bean>

Hibernate / MySQL Session Factory & Transaction Manager Spring Bean Config

<!-- Hibernate Session Factory -->
<bean id="mySessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
  <property name="dataSource" ref="myDataSource"/>
  <property name="packagesToScan">
    <array>
      <value>com.gerrytan.hibernatefiddle</value>
    </array>
  </property>
  <property name="hibernateProperties">
    <value>
      hibernate.dialect=org.hibernate.dialect.MySQLDialect
    </value>
  </property>
</bean>

<!-- Hibernate Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
  <property name="sessionFactory" ref="mySessionFactory"/>
</bean>

Log4j Properties File

# Root logger option
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%L - %m%n

This will print log in following format:

2013-06-26 10:15:35 DEBUG com.gerrytan.hibernatefiddle.StudentDAO:21 - StudentDAO created

To create file logger, limited to 5 file with max size 1000KB:

# Root logger option
log4j.rootLogger=WARN, file
 
# Rolling file appender
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=MyLog.log
log4j.appender.file.MaxFileSize=1000KB
log4j.appender.file.MaxBackupIndex=5
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c:%L - %m%n

Hibernate Logging

log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.engine.transaction=DEBUG

On Hibernate 3 or earlier transaction logger is:

log4j.logger.org.hibernate.transaction=DEBUG

Hibernate 4.2.2 Transactions Logging

I just a hard time trying to enable transactions logging for hibernate. Checked the manual many times and it said the logger name is

org.hibernate.transaction=DEBUG

But not a single logging output is given.

I then dig a bit deeper into the source code. Realized my hibernate version is 4.2.2 (latest doc as of writing this post is 4.2.1) and they seem to have refactored the package name into

org.hibernate.engine.transaction=DEBUG

And yes finally I can see my transactions being logged.