Tutorial: Hibernate, JPA – Part 1

This is the first part of tutorial about using Hibernate and JPA. This part is an introduction to to JPA and Hibernate. The second part will look at putting together a Spring MVC application using Spring ORM to reduce the amount of code necessary to create a CRUD application. 

To complete this you’ll want to be familiar with Maven, JUnit, SQL and relational databases.

Dependencies

Firstly we’ll need a couple of basic dependencies. Essentially there are three layers:

  1. The lowest layer is the JDBC drivers used by Hibernate to connect to the database. I’m going to use Derby, a simple embedded database. There’s no server to install or configure so it’s easier to set-up that even MySQL or PostgreSQL; it’s not suitable for production.
  2. The middle layer is the Hibernate libraries. I’m going to use version 3.5.6. This works with Java 1.5, 4.x does not.
  3. The JPA libraries.

Additionally we’ll want JUnit for creating tests and Tomcat so we can using it’s JNDI naming for tests. JNDI is a preferable system to including the server details in a properties file for reasons we’ll come to.

<dependencies>
        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.4.1.3</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>3.6.9.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate.javax.persistence</groupId>
            <artifactId>hibernate-jpa-2.0-api</artifactId>
            <version>1.0.0.Final</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.tomcat</groupId>
            <artifactId>catalina</artifactId>
            <version>6.0.18</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Configuration

The key config file for JPA is persistence.xml. This lives in the META-INF directory. It details what the persistence driver to use and what JNDI data source to connect to. Additional properties can also be specified, in this case we’ll include some Hibernate properties.

I’ve added some comments on the additional properties so you know what they are for. You can configure the data source directly, but using JNDI means we can easily deploy the code in a container, as a standalone or to run unit tests, with minimal code changes.

<?xml version='1.0' encoding='UTF-8'?>
<persistence xmlns='http://java.sun.com/xml/ns/persistence'
 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 xsi:schemaLocation='http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd'
 version='1.0'>

 <persistence-unit name='tutorialPU' transaction-type='RESOURCE_LOCAL'>
  <provider>org.hibernate.ejb.HibernatePersistence</provider>
  <!-- the JNDI data source -->
  <non-jta-data-source>java:comp/env/jdbc/tutorialDS</non-jta-data-source>
  <properties>
   <!-- if this is true, hibernate will print (to stdout) the SQL it executes, 
    so you can check it to ensure it's not doing anything crazy -->
   <property name='hibernate.show_sql' value='true' />
   <property name='hibernate.format_sql' value='true' />
   <!-- since most database servers have slightly different versions of the 
    SQL, Hibernate needs you to choose a dialect so it knows the subtleties of 
    talking to that server -->
   <property name='hibernate.dialect' value='org.hibernate.dialect.DerbyDialect' />
   <!-- this tell Hibernate to update the DDL when it starts, very useful 
    for development, dangerous in production -->
   <property name='hibernate.hbm2ddl.auto' value='update' />
  </properties>
 </persistence-unit>
</persistence>

Entities

JPA talks in terms of entities rather than database records. An entity is an instance of a class maps to a single record in a table (classes map to tables). The entities fields (which should use the JavaBean naming convention) are mapped to columns.

Annotations can be used to add extra information to the class. They mark the class as being an entity, and allow you to specify meta information about the table and columns, such as names, sizes, and constraints.

In our case we’re going to start with the simplest entity possible.

package tutorial;

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

@Entity
@Table(name = 'usr') // @Table is optional, but 'user' is a keyword in many SQL variants 
public class User {
    @Id // @Id indicates that this it a unique primary key
    @GeneratedValue // @GeneratedValue indicates that value is automatically generated by the server
    private Long id;

    @Column(length = 32, unique = true)
    // the optional @Column allows us makes sure that the name is limited to a suitable size and is unique
    private String name;

    // note that no setter for ID is provided, Hibernate will generate the ID for us

    public long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }
}

JPA can use the meta information to create the DDL when it starts up. This is helpful for development as it allows you to quickly get up and running without delving into the SQL needed to create tables. Want to add a column? Just add the column, compile and run. Unfortunately, what you gain in convenience is also an increase in risk (e.g. what does the database server do when a table has millions of records and you add a new column?) and loss of control.

There’s a compromise, once the entities have been created by Hibernate, you can export the DDL and change Hibernate’s config to stop it updating the DDL.

Test Case

There are only two pieces, first we’ll create an abstract test case as a root for all our tests. This will register a data source with JNDI, and we will extend it with other tests so that they access to the database.

package tutorial;

import org.apache.derby.jdbc.EmbeddedDataSource;
import org.apache.naming.java.javaURLContextFactory;
import org.junit.AfterClass;
import org.junit.BeforeClass;

import javax.naming.Context;
import javax.naming.InitialContext;

public abstract class AbstractTest {

 @BeforeClass
 public static void setUpClass() throws Exception {
  System.setProperty(Context.INITIAL_CONTEXT_FACTORY, javaURLContextFactory.class.getName());
  System.setProperty(Context.URL_PKG_PREFIXES, 'org.apache.naming');
  InitialContext ic = new InitialContext();

  ic.createSubcontext('java:');
  ic.createSubcontext('java:comp');
  ic.createSubcontext('java:comp/env');
  ic.createSubcontext('java:comp/env/jdbc');

  EmbeddedDataSource ds = new EmbeddedDataSource();
  ds.setDatabaseName('tutorialDB');
  // tell Derby to create the database if it does not already exist
  ds.setCreateDatabase('create');

  ic.bind('java:comp/env/jdbc/tutorialDS', ds);
 }

 @AfterClass
 public static void tearDownClass() throws Exception {

  InitialContext ic = new InitialContext();

  ic.unbind('java:comp/env/jdbc/tutorialDS');
 }
}

The final piece is the test case. The entity manger provide access to the data. The persist operation (which will result in a single insert in this case) must be performed in a transaction. In fact Hibernate will not do any work until the commit. You can see this by adding a Thread.sleep immediately prior to the commit.

@Test
    public void testNewUser() {

        EntityManager entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

        entityManager.getTransaction().begin();

        User user = new User();

        user.setName(Long.toString(new Date().getTime()));

        entityManager.persist(user);

        entityManager.getTransaction().commit();

        // see that the ID of the user was set by Hibernate
        System.out.println('user=' + user + ', user.id=' + user.getId());

        User foundUser = entityManager.find(User.class, user.getId());

        // note that foundUser is the same instance as user and is a concrete class (not a proxy)
        System.out.println('foundUser=' + foundUser);

        assertEquals(user.getName(), foundUser.getName());

        entityManager.close();
    }

Exception Handling

The need for a begin and commit is verbose. Additionally, the last example is incomplete, as it misses any rollback if an exception occurs.

Exception handling is boiler plate code. Like it’s JDBC equivalent, it’s not pretty. Here’s a example:

 @Test(expected = Exception.class)
    public void testNewUserWithTxn() throws Exception {

        EntityManager entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

        entityManager.getTransaction().begin();
        try {
            User user = new User();

            user.setName(Long.toString(new Date().getTime()));

            entityManager.persist(user);

            if (true) {
                throw new Exception();
            }

            entityManager.getTransaction().commit();
        } catch (Exception e) {
            entityManager.getTransaction().rollback();
            throw e;
        }

        entityManager.close();
    }

I’ll leave the exception management out for the moment as there are better ways to do it. Later we’ll look at how JSR-330′s @Inject and Spring Data’s @Transactional can reduce the boiler plate.

Entity Relations

Since we’re using relational databases, we’ll almost certainly want to create a relation between entities. We’ll create a role entity and have a many to many relationship between user and role. To create the role entity, just copy the User entity, name it Role and remove the @Table line. We don’t need to create a UserRole entity. But we will want to add and remove roles from the user.

Add the following field and method to the user table:

    @ManyToMany
    private Set<Role> roles = new HashSet<Role>();

    public boolean addRole(Role role) {
        return roles.add(role);
    }

    public Set<Role> getRoles() {
        return roles;
    }

The @ManyToMany annotation tells JPA that it’s a many-to-many relation. We can test this with a new test case. This test creates a user and role in one transaction and then updates the user in the second using merge. Merges are used to update an entity in the database.

  @Test
    public void testNewUserAndAddRole() {

        EntityManager entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

        entityManager.getTransaction().begin();

        User user = new User();

        user.setName(Long.toString(new Date().getTime()));

        Role role = new Role();

        role.setName(Long.toString(new Date().getTime()));

        entityManager.persist(user);
        entityManager.persist(role);

        entityManager.getTransaction().commit();


        assertEquals(0, user.getRoles().size());


        entityManager.getTransaction().begin();

        user.addRole(role);

        entityManager.merge(user);

        entityManager.getTransaction().commit();


        assertEquals(1, user.getRoles().size());


        entityManager.close();
    }

Queries

JPA allows you to use a query language with a strong similarity to SQL called JPQL. Queries can be written directly, but named queries are easier to control, to maintain and exhibit better performance as Hibernate can prepare the statement. They are specified using the @NamedQuery annotation. Add this line to the User class after the @Table annotation:

@NamedQuery(name='User.findByName', query = 'select u from User u where u.name = :name')

 You can test this as follows:

 @Test
 public void testFindUser() throws Exception {

  EntityManager entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

  entityManager.getTransaction().begin();

  User user = new User();

  String name = Long.toString(new Date().getTime());

  user.setName(name);

  Role role = new Role();

  role.setName(name);

  user.addRole(role);

  entityManager.persist(role);
  entityManager.persist(user);

  entityManager.getTransaction().commit();

  entityManager.close();

  entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

  User foundUser = entityManager.createNamedQuery('User.findByName', User.class).setParameter('name', name)
    .getSingleResult();

  System.out.println(foundUser);

  assertEquals(name, foundUser.getName());

  assertEquals(1, foundUser.getRoles().size());

  System.out.println(foundUser.getRoles().getClass());

  entityManager.close();
 }

In this example I’ve closed and reopened the entity manager. This forces Hibernate to request the user from the database. Notice anything interesting about the output? The SQL for getting the roles appears after the toString of the found user. Hibernate creates a proxy object for the roles (in this case a org.hibernate.collection.PersistentSet), and only populates it when you first access the object. This can result in counter-intuitive behaviour and has its own set of pitfalls.

Try this variation of the above test where we close the entity manager before we first query the roles:

 @Test(expected = LazyInitializationException.class)
 public void testFindUser1() throws Exception {

  EntityManager entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

  entityManager.getTransaction().begin();

  User user = new User();

  String name = Long.toString(new Date().getTime());

  user.setName(name);

  Role role = new Role();

  role.setName(name);

  user.addRole(role);

  entityManager.persist(role);
  entityManager.persist(user);

  entityManager.getTransaction().commit();

  entityManager.close();

  entityManager = Persistence.createEntityManagerFactory('tutorialPU').createEntityManager();

  User foundUser = entityManager.createNamedQuery('User.findByName', User.class).setParameter('name', name)
    .getSingleResult();

  entityManager.close();

  assertEquals(1, foundUser.getRoles().size());
 }

The LazyInitializationException will be thrown on the getRoles() call. This is not a bug. Once the entity manager is closed, any entity can become unusable.

End

This is the basics to get up and running with Hibernate JPA. In the next part of this tutorial, I’ll discuss validation, and look at some other details in more depth.

Reference: Tutorial: Hibernate, JPA – Part 1 from our JCG partner Alex Collins at the Alex Collins ‘s blog blog.

Related Whitepaper:

Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions

Get ready to program in a whole new way!

Functional Programming in Java will help you quickly get on top of the new, essential Java 8 language features and the functional style that will change and improve your code. This short, targeted book will help you make the paradigm shift from the old imperative way to a less error-prone, more elegant, and concise coding style that’s also a breeze to parallelize. You’ll explore the syntax and semantics of lambda expressions, method and constructor references, and functional interfaces. You’ll design and write applications better using the new standards in Java 8 and the JDK.

Get it Now!  

3 Responses to "Tutorial: Hibernate, JPA – Part 1"

  1. Sergii says:

    testNewUserRole():

    Hibernate:

    insert

    into

    User

    (id, name)

    values

    (default, ?)

    javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not insert: [model.User]

    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1387)

    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1315)

    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1321)

    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:843)

    at model.UserTest.testNewUserAndAddRole(UserTest.java:56)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)

    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)

    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)

    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)

    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)

    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)

    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)

    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)

    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)

    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)

    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)

    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)

    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)

    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)

    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

    Caused by: org.hibernate.exception.SQLGrammarException: could not insert: [model.User]

    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:92)

    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)

    at org.hibernate.id.insert.AbstractSelectingDelegate.performInsert

  2. Sergii says:

    and if add persistence to test/META-INF:

    java.lang.IllegalArgumentException: Unknown entity: model.User

    at org.hibernate.ejb.AbstractEntityManagerImpl.persist(AbstractEntityManagerImpl.java:840)

    at model.UserTest.testNewUserAndAddRole(UserTest.java:57)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)

    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)

    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)

    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)

    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)

    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)

    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)

    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)

    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)

    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)

    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)

    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)

    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:30)

    at org.junit.runners.ParentRunner.run(ParentRunner.java:300)

    at org.junit.runner.JUnitCore.run(JUnitCore.java:157)

    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:76)

    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195)

    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

  3. Sergii says:

    fixed by changing the derby version to latest one:

    org.apache.derby

    derby

    10.9.1.0

Leave a Reply


7 + three =



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.

Sign up for our Newsletter

15,153 insiders are already enjoying weekly updates and complimentary whitepapers! Join them now to gain exclusive access to the latest news in the Java world, as well as insights about Android, Scala, Groovy and other related technologies.

As an extra bonus, by joining you will get our brand new e-books, published by Java Code Geeks and their JCG partners for your reading pleasure! Enter your info and stay on top of things,

  • Fresh trends
  • Cases and examples
  • Research and insights
  • Two complimentary e-books
Get tutored by the Geeks! JCG Academy is a fact... Join Now
Hello. Add your message here.