JPA vs Hibernate in a nutshell

A fairly common question for developers approaching Object Relational Mapping (ORM) tools like Hibernate is what is the difference between Hibernate and JPA.

In this article we are going to explain it in a nutshell.

  • From an high level view, JPA is just like an interface without a concrete implementation of it.
  • On the other hand, Hibernate is an Object Relational Mapping (ORM) tool and also a JPA Provider which has the implementation of the functions in JPA. It can also have some extra functions which might not be there in JPA.

It follows, that you can use Hibernate as standalone product and, behind the hoods, as JPA Provider.

On the other hand, to be able to use JPA you have to choose a valid Provider. Each vendor adopts a JPA implementation. For example, WildFly (or JBoss EAP) uses Hibernate as JPA Provider. Therefore WildFly (and JBoss EAP) bundles Hibernate.

When using Hibernate vs JPA

In order to include new features in JPA, a specification needs to be voted and updated, therefore Hibernate will always include newer features as it is a more agile project.

For example, some features which are implemented in Hibernate but are still not available in JPA are:

  • Extended identifier generators (hi/lo, pooled, pooled-lo)
  • Custom CRUD SQL Statements (@SQLInsert, @SQLUpdate, @SQLDelete)
  • Adding Collection filters (e.g. @FilterDef, @Filter, @Where) and Entity filters (e.g. @Where)
  • Mapping properties to SQL fragments (e.g. @Formula)
  • Immutable entities (e.g. @Immutable)
  • Querying the second-level cache by the natural key of a given entity
  • Entity-level cache concurrency strategies (e.g. Cache(usage = CacheConcurrencyStrategy.READ_WRITE))
  • Exclude fields from optimistic locking check (e.g. @OptimisticLock(excluded = true))
  • Version-less optimistic locking (e.g. OptimisticLockType.ALL, OptimisticLockType.DIRTY)
  • Support for skipping (without waiting) pessimistic lock requests
  • Support for multi tenancy
  • Support for soft delete (e.g. @Where, @Filter)

In such cases, you can extend the power of your JPA applications by using the required Hibernate extension.

For example, in the following article, we have covered how to use Custom CRUD SQL Statements (@SQLInsert, @SQLUpdate, @SQLDelete) in a JPA application: Using custom SQL for Hibernate CRUD Operations

Hibernate vs JPA: configuration

As far as configuration is concerned, a JPA application uses a configuration file named persistence.xml which defines the Persistence units, the Provider, the Datasource to be used and may also Map Entity Objects and Properties

<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_2_0.xsd"
	version="2.0">
	<persistence-unit name="unit01"
		transaction-type="RESOURCE_LOCAL">
		<description>JPA Demo</description>
		<provider>org.hibernate.ejb.HibernatePersistence</provider>

		<!-- Mapping of classes. -->
		<class>com.sample.jpa.book.Book</class>
		<class>com.sample.jpa.book.Author</class>
		<class>com.sample.jpa.book.BookCategory</class>
                
                <jta-data-source>java:jboss/datasources/KitchensinkQuickstartDS</jta-data-source>
		<!-- Setting of Hibernate ORM Properties. -->
		<properties>
			<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
			<property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" />
			<property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/test" />
			<property name="hibernate.connection.username" value="root" />
			<property name="hibernate.connection.password" value="himserver" />
			<property name="hibernate.hbm2ddl.auto" value="update" />
			<property name="hibernate.show_sql" value="true" />
			<property name="hibernate.format_sql" value="true" />
		</properties>
	</persistence-unit>
</persistence>

On the other hand, Hibernate relies on the hibernate.cfg.xml file to map database Tables with Entity objects and to specify Connection properties:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC 
  "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 
  "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
  <hibernate-configuration>
    <session-factory>
      <property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
      <!-- property name="connection.driver_class">com.mysql.jdbc.Driver</property -->
      <property name="connection.url">jdbc:mysql://localhost/hibernate_examples</property>
      <property name="connection.username">user</property>
      <property name="connection.password">password</property>
      <property name="connection.pool_size">10</property>
      <property name="dialect">org.hibernate.dialect.MySQL8Dialect</property>
      <property name="current_session_context_class">thread</property>
      <property name="show_sql">true</property>
      <property name="format_sql">true</property>
      <property name="hbm2ddl.auto">update</property>
      <!-- mapping  class="com.sample.Book" / -->
  </session-factory>
</hibernate-configuration>

So, as a basic rule of thumb, if you are using JPA application, configure your persistence settings in persistence.xml applications.

On the other hand, if you are running Hibernate standalone applications configure the persistence settings in hibernate.cfg.xml.

Hibernate vs JPA: managing persistence

Unless you are relying on Hibernate extensions which are not available in JPA, you will be using the classes in the javax.persistence package (or jakartaee if using Jakarta EE 9).

As a matter of fact, to manage the persistence with JPA, we retreive the EntityManagerFactory and EntityManager, all situated in javax.persistence package.

import org.jboss.as.quickstarts.kitchensink.model.Member;

import javax.ejb.Stateless;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.persistence.EntityManager;
import java.util.logging.Logger;

// The @Stateless annotation eliminates the need for manual transaction demarcation
@Stateless
public class MemberRegistration {

    @Inject
    private Logger log;

    @Inject
    private EntityManager em;

    @Inject
    private Event<Member> memberEventSrc;

    public void register(Member member) throws Exception {
        log.info("Registering " + member.getName());
        em.persist(member);
        memberEventSrc.fire(member);
    }
}

On the other hand, Hibernate uses its own classes to represent persistence context, such as the org.hibernate.SessionFactory. As an example, assuming hiberante.cfg.xml is in the classpath, you can retrieve the SessionFactory as follows

public static Session getSessionFromConfig() {

  Configuration config = new Configuration();
  config.configure();
  SessionFactory sessionFactory = config.buildSessionFactory();
  Session session = sessionFactory.getCurrentSession();
  return session;
}

If required, you can still access Hibernate’s Session Factory in a JPA applications by using the entityManager.unwrap method, once you have retrieved the Entity Manager:

public static SessionFactory getCurrentSessionFromJPA() {
  EntityManagerFactory emf = 
      Persistence.createEntityManagerFactory("my-em-factory");
  EntityManager entityManager = emf.createEntityManager();
  Session session = entityManager.unwrap(org.hibernate.Session.class);
  SessionFactory factory = session.getSessionFactory();
  return factory;
}

Found the article helpful? if so please follow us on Socials