What’s the difference between JPA, Hibernate and EclipseLink

By Thorben Janssen


When people are new to JPA, Hibernate or EclipseLink, they are often confused about the difference between them and which one they should use in their project. If you’re one of them, don’t worry. It’s a lot easier than it seems.

Let’s take a look at the JPA specification first.

Java Persistence API (JPA)

JPA is an abbreviation that stands for Java Persistence API. It’s a specification which is part of Java EE and defines an API for object-relational mappings and for managing persistent objects. You can use this API in Java SE and Java EE environments.

The specification is currently available in version 2.2. You can download the document at https://jcp.org/en/jsr/detail?id=338. The API jar is available at the following Maven coordinates:

<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>

JPA itself doesn’t provide any implementation classes. The API jar just contains a set of interfaces which you can use to implement your persistence layer. But you can’t use JPA on its own. You need a JPA provider which implements the specification. There are several options available. The most popular ones are Hibernate and EclipseLink. But more about that later.

Until recently, JPA was managed and developed by an expert group following the Java Community Process (JCP). That changed when Oracle announced to transfer all Java EE specifications to the Eclipse Foundation. We’re now in the middle of the transition process, and a new specification process will be defined soon.

What’s defined by the JPA specification

The specification defines most of the features that I explained in the tutorials and videos on this site. You can use them with all compliant JPA implementations.

Let’s take a look at some of the most important ones.

Bootstrapping and basic entity mappings

Before you can start using JPA, you need to add it to your project, configure a persistence unit, map entities to your database tables and bootstrap it. You probably already know how to do that, and I explained it in great detail in my Getting Started with Hibernate article.

So, let’s skip this part here and talk about the more interesting features.

Mapping Associations

JPA doesn’t only enable you to map simple entity attributes to database columns, but it also allows you to map associations between database tables to entity attributes.

@Entity
public class Review {

	...
	
	@ManyToOne
	private Book book;
	
	...
}

That often makes your entity model very comfortable to use because you just need to call a getter method on an entity to load the associated entities. In the background, the persistence provider performs all the required database operations to retrieve and manage the association.

As comfortable to use as this might be, this features often causes performance problems. Before you start to model associations between your entities, please make sure you understand the effect of JPA’s FetchTypes to avoid n+1 select issues.

JPQL and Native Queries

JPA defines its own query language, called JPQL. It’s similar to SQL but enables you to define queries based on the mapped domain model instead of the database’s table model.

The following code snippet shows a simple JPQL query. You can define an ad-hoc query by calling the createQuery method on the EntityManager em. As you can see, the syntax looks very similar to SQL. If you’re not familiar with JPQL, please take a look at my JPQL Guide in which I explain its syntax and capabilities in great details.

TypedQuery<Book> q = em.createQuery("SELECT b FROM Book b WHERE b.id = :id", Book.class);
q.setParameter("id", 1L);
Book b = q.getSingleResult();

When you execute such a query, your persistence provider interprets the JPQL statement and generates an SQL query for it. By doing that, the persistence provider adapts the query to the database-specific SQL dialect and improves the portability of your application.

Unfortunately, that also limits you to the query features defined by the specification or proprietarily supported by your persistence provider. This feature set is significantly smaller than the one offered by SQL and doesn’t include any proprietary database features.

But that doesn’t mean that you can’t use any advanced or complex queries with JPA. It’s designed as a leaky abstraction and allows you to execute native SQL queries. These are not parsed by your persistence provider, and you can use all features supported by your database. But please be aware that this might negatively affect your database portability.

Executing a native query is pretty simple. You just need to call the createNativeQuery method instead of the createQuery method on your EntityManager with a native SQL query.

Query q = em.createNativeQuery("SELECT * FROM book b WHERE id = :id", Book.class);
q.setParameter("id", 1L);
Book b = (Book) q.getSingleResult();

Custom Data Types

The JPA specification defines the mapping for most standard types without limiting you to them. Since JPA 2.1, you can easily support custom data types with an AttributeConverter. You just need to implement the AttributeConverter interface and annotate the class with a @Converter annotation.

Here’s an example of an attribute converter that defines a custom mapping for my AuthorStatus enum.

@Converter(autoApply = true)
public class AuthorStatusConverter implements AttributeConverter<AuthorStatus, String> {
	
	Logger log = Logger.getLogger(AuthorStatusConverter.class.getSimpleName());
	
	@Override
	public String convertToDatabaseColumn(AuthorStatus status) {
		switch (status) {
		case NOT_PUBLISHED:
			logDbConversion(status, "N");
			return "N";
			
		case PUBLISHED:
			logDbConversion(status, "P");
			return "P";
			
		case SELF_PUBLISHED:
			logDbConversion(status, "S");
			return "S";

		default:
			throw new IllegalArgumentException("AuthorStatus ["+status+"] not supported.");
		}
	}

	@Override
	public AuthorStatus convertToEntityAttribute(String dbData) {
		switch (dbData) {
		case "N":
			logEntityConversion(AuthorStatus.NOT_PUBLISHED, "N");
			return AuthorStatus.NOT_PUBLISHED;
			
		case "P":
			logEntityConversion(AuthorStatus.PUBLISHED, "P");
			return AuthorStatus.PUBLISHED;
			
		case "S":
			logEntityConversion(AuthorStatus.SELF_PUBLISHED, "S");
			return AuthorStatus.SELF_PUBLISHED;
			
		default:
			throw new IllegalArgumentException("AuthorStatus ["+dbData+"] not supported.");
		}
	}

	private void logDbConversion(AuthorStatus status, String dbData) {
		log.debug("Convert AuthorStatus enum ["+status+"] to ["+dbData+"].");
	}
	
	private void logEntityConversion(AuthorStatus status, String dbData) {
		log.debug("Convert DB value ["+dbData+"] to AuthorStatus enum ["+status+"].");
	}
}

EclipseLink and Hibernate

As I said before, you need a JPA provider, if you want to use the JPA specification in your project. It implements the interfaces as defined by the specification. The most popular ones are EclipseLink and Hibernate.

One advantage of the standardized API provided by JPA is that you just need to add its implementation at runtime and that you can replace it with a different one without changing any code. The standardized API makes EclipseLink and Hibernate interchangeable.

So, why do you need different implementations?

The JPA implementations are managed by independent teams, and you can choose the one that provides the best performance or support for your application and technology stack. They also differentiate themselves by providing additional, non-standard functionalities. This is often used to drive innovation. Today’s popular, proprietary feature might be the first step to the next addition to the JPA standard. Using any of these proprietary features, obviously, makes it a lot harder to replace a specific JPA implementation.

EclipseLink

EclipseLink is JPA’s reference implementation and implements JPA version 2.2. It was one of the first projects that became part of EE4J.

The easiest way to add EclipseLink to your project is to use the following Maven coordinates.

<dependency>
    <groupId>org.eclipse.persistence</groupId>
    <artifactId>eclipselink</artifactId>
    <version>2.7.1</version>
</dependency>

Interesting proprietary features

In addition to the features defined by the JPA standard, EclipseLink also offers several interesting, proprietary features, like:

Hibernate

Hibernate is Red Hat’s very popular implementation of the JPA specification. It implements almost all features defined by JPA 2.2 and will release a fully compliant version soon.

The following Maven dependency adds Hibernate to your project.

<dependency>
	<groupId>org.hibernate</groupId>
	<artifactId>hibernate-core</artifactId>
	<version>5.1.11</version>
</dependency>

Interesting proprietary features

Similar to EclipseLink, Hibernate provides a bunch of interesting, proprietary features, like:


Tags


About the author

Thorben is an independent consultant, international speaker, and trainer specialized in solving Java persistence problems with JPA and Hibernate.
He is also the author of Amazon’s bestselling book Hibernate Tips - More than 70 solutions to common Hibernate problems.

Books and Courses

Coaching and Consulting

Leave a Reply

Your email address will not be published. Required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  1. One of the points I think worth making is the fact that people often use the term Hibernate when in fact they mean any implementation of JPA. When JPA first came out, Hibernate was by far the most popular open source ORM project. The word Hibernate ended up becoming synonymous with JPA. Even today, I hear plenty of people talking about Hiberante, when in fact, all they are referring to is an ORM implementation of JPA.

    https://www.theserverside.com/blog/Coffee-Talk-Java-News-Stories-and-Opinions/Set-the-record-straight-on-the-JPA-and-Hibernate-debate

    1. Yes, a lot of developers use JPA and Hibernate as synonyms. As I explained in the article, that’s wrong.
      But if you consider that by far the most of Hibernate’s features are standardized by the JPA spec and that Hibernate its most popular implementation, it isn’t as bad as it seems. But it’s, of course, still wrong and shouldn’t be done 😉

  2. Good article. I just wish these articles had dates at the top or bottom so we have a time reference of when this was written/posted. So seeing statements like ‘ We’re now in the middle of the transition process ‘ we have some idea or time reference as to when “now” really is.

    1. Hi Keith,

      I’m keeping all articles up to date (or I at least try to do that). So, the date when the first version of the article was posted isn’t really relevant.

      Regarding the “in the middle of the transition process” statement. I think we’re still (stuck) in the transition process until we get the first release that contains features that were specified under the stewardship of the Eclipse Foundation.

      Regards,
      Thorben

    1. The JPA specification only supports named bind parameters for JPQL and Criteria queries but not for native queries.
      As far as I know, EclipseLink implements it as defined by the spec. So, no. EclipseLink doesn’t support named bind parameters for native queries.

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}