Hibernate with JPA

In the previous articles we were working with Hibernate's specific elements as Session or SessionFactory. But they are not the only possible choices. We can too opt for working with Hibernate's implementation of JPA specification.

This article will cover the basics of JPA implementation in Hibernate. In the first part we'll discover what is JPA and how it's composed. After that, we'll pass to use cases of Hibernate's JPA. The use cases will include all traditional CRUD actions: creation, read, update and delete.

What is JPA ?

JPA is a standardization for all mechanisms handling relational data in Java application. It means that JPA provides a set of common programming rules for all implementations. We can find there as well interfaces to define implementation behavior as annotations to provide a metadata layer used for map POJO object to theirs persistance representations.

Five main pillars compose JPA specification:

  1. Entities: are persistence domain object, i.e. they represent data from persistence storage (for example database's table). An entity can be distinguished from normal Java object thanks to annotation @Entity. In additionally, it can't be final and can contain persistent and non-persistent fields. Both can be marked as it through other annotations, as for example: @Column for persistent and @Transient for non-persistent fields. Entities, exactly as database models, can be associated between them thanks to another annotations (@ManyToOne, @OneToMany, @ManyToMany, @OneToOne). We'll cover it more in detail in one of next articles.
  2. Entity manager: handler for entities. It manages entities life cycle by finding them from the database or pushing the modifications into database. In additionally, it can also control lock or flushing modes. Entity manager is similar to Hibernate's Session instances. It's represented by the implementations of javax.persistence.EntityManager interface.
  3. Entity manager factory: as Hibernate's SessionFactory, entity manager factory is used to create entity manager instances. Represented by implementations of javax.persistence.EntityManagerFactory, it can also access to "the second level" cache that must implement javax.persistence.Cache.
  4. Persistence units: it's a configuration used to define all entities managed by entity manager. Normally, they are defined under tag from persistence.xml file.
  5. Persistence context: regroups all available entities. Entity manager is associated to this context and through this association, it can manage to the entities. So, we can tell that persistence context is a collection of all entities managed by entity manager. The main role of this context is to control the number of instances for every unique entity. I.e. each persistent row can be represented exactly by one entity in this context. For example, suppose that we have an table "products" in the database. A product's row with id (primary key) 4 can't be represented by two different objects in persistence context.
  6. Transactions: represented by implementations of javax.persistence.EntityTransaction, they ensure the atomic character of operations made by entity manager.

After reading previous list, we can simply find some of similarities between Hibernate and JPA specifications. In Hibernate all objects are handled by Session. In JPA, entity manager handles it. SessionFactory returns Session instances and in JPA, it's entity manager factory which produces entity manager instances. Some of common rules are applied in the both sides. One of them is a unique entity representing database row. With this short comparison, we can approach samples part.

Basic Hibernate JPA samples

Our example will be written as unit tests. We'll start by configure persistence unit:



   
      
         
         
         
         
         
      
   

This configuration file is as simple as possible. You can find the name of persistence-unit. It'll be used to create entity manager factory. Next to it, we retreive connection data (javax.persistance.jdbc) and information about Hibernate's dialect. Everything is there ? No, the most important entry miss: point to entity Java classes. In our case, we'll add a link to .classes directory but you can add a JAR path as well. The entry must be added after persistence-unit tag and can look like:

file:///home/bartosz/tests/entity/classes
Without it, a try of entity manager factory won't know where are placed entities:
java.lang.IllegalArgumentException: Unknown entity: com.waitingforcode.data.Product
	at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:819)
	at org.hibernate.ejb.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:781)
	at com.mysite.test.JpaTest.test(JpaTest.java:29)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)

The configuration looks simple. Our test code too. In the test case, we want to: load product with 4, change its name to "banana" and save the modifications. After, we'll delete all products named "apple" and add a single product called "apple". Let's see it in sample code:

@Test
public void test() {
  EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("sampleUnit");
  assertTrue("Entity manager factory can't be null", emFactory != null);
  EntityManager entityManager = emFactory.createEntityManager();
  EntityTransaction transaction = entityManager.getTransaction();
  try {
    transaction.begin();
    assertTrue("Entity manager can't be null", entityManager != null);
    
    // rename 4th product's name to 'banana'
    Product product = entityManager.find(Product.class, 4);
    product.setName("banana"); 
    
    // remove all 'apples'
    Query delApple = entityManager.createQuery("DELETE FROM Product p WHERE p.name = :name").setParameter("name", "apple");
    delApple.executeUpdate();
    
    // add new 'apple' product
    Product apple = new Product();
    apple.setName("apple");
    entityManager.persist(apple);

    transaction.commit();
  } catch (Exception e) {
    e.printStackTrace();
    transaction.rollback();
  }

  // check if apple was added
  Query query = entityManager.createQuery("select p FROM Product AS p WHERE p.name = :name");
  query.setParameter("name", "apple");
  Product dbApple = (Product) query.getSingleResult();
  assertTrue("Apple should be inserted in database but it's not", dbApple != null);
  assertTrue("Apple should have 'apple' name but it hasn't", dbApple.getName().equals("apple"));

  // get all inserted products
  Query productsQuery = entityManager.createQuery("SELECT p FROM Product as p ORDER BY p.name ASC");
  List products = (List) productsQuery.getResultList();
  assertTrue("They are no products in database but at least 2 should be there (apple and banana)", products != null && products.size() > 0);
}

If you remember the article about difference between SessionFactory and EntityManagerFactory, you can simply find a lot of similarities between Hibernate's Session and Hibernate's JPA mechanism. First, we generate new entity manager factory by calling Persistence.createEntityManagerFactory. The name passed in parameter corresponds to the name given to persistence-unit in persistence.xml file. After, we get the instances of EntityManager and Transaction.

As you can see, product edition is made by simple call of setters. They are put into persistent storage (database in our case) when Transaction is committed (commit()). To make new entity persistent, we need to create it first and call EntitiyManager's persist() method, exactly as for apple product. Entities delete is made by creating and executing javax.persistence.Query object.

If we look back at article about Hibernate's Sessions, we see that Hibernate's mechanism is very close to JPA's specification. In the both sides we retrieve a concept of "persistent session". Even the methods used to add new entity or execute a query, are similar: both systems use persist() to insert new row, uniqueResult()/getSingleResult() to retrieve single row corresponding to given query, saveOrUpdate()/executeUpdate() to execute a query. So writting applications with JPA specification isn't very different from writting them with Hibernate's Sessions. At least, for basic operations displayed in this article.


If you liked it, you should read:

📚 Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free - no 3rd party ads, only the information about waitingforcode!