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: