JavaTechnology

Java Persistence API Guide

Welcome to the Java Persistence API Guide, which covers how JPA was created, highlights, and major parts. It also covers JPQL, Criteria, Entity and Entity Beans with code examples. This tutorial is part of an ongoing series of Learning JPA and Spring Data JPA. However, independently this tutorial covers a complete of Java Persistence API (JPA).

1 Overview

Java Persistence API is a Java Specification and Standard for Object Relational Mapping (ORM). In Object Relational Mapping we create Java Objects which represents the database entities. ORM also provides an EntityManager which provides methods to create, delete, update and find the objects from database. We don’t need to write low level queries, we just need to use entity manager and access the entities through java objects.

Initially, JPA was an internal part of Enterprise Java Beans specifications. Where the business entity beans used to be mapped with relational databases. In the EJB 3.0 the specifications regarding data access layer were moved out as an independent specification which was named as Java Persistence API.

2 Java Persistence API

We have already discussed Java Persistence API in the above sections. This is the core part of the JPA which defines the actual Object Relational Mapping specifications.

One thing to remember that JPA is just a specification and not implementation. To use JPA, we need to use any of the available JPA implementations in the market. The JPA implementation frameworks are responsible for converting the entity manager database calls to actual low level native sql queries, based on the specified database provider.Here are some of the highlights of JPA.

2.1 Highlights of JPA Specs

  1. Same standard for Enterprise Java Beans (EJB) and Plain Old Java Objects (POJO).
  2. Entities can be inherited.
  3. Mappings can be defined using annotations or XML. If both are specified XML will override annotations.
  4. Ability to generate schema/database objects. To see how Entities are used to generated database objects, please visit Spring Boot with Spring Data JPA.
  5. Support for Streaming results, like stream of entity collection.

There are many JPA implementations available in the market to chose from. Hibernate JPA is one of the most popular JPA Implementation. The other popular implementations are EclipseLink and OpenJpa.

2.2 JPA Parts

On a high level JPA specifications can be divided into:

  1. Entity Beans and Entity Manager
  2. Java Persistence Query Language
  3. Java Persistence Criteria API

3 Entity Beans and Entity Manager

Entity beans are the plain old java objects used to represent an instance of a database object. In the simplest word, we can say a Collection of a entity bean can represent entire table of a database.
Like any POJO entity beans have fields and access methods. To make an Entity Bean out of a POJO we need to declare it as an Entity Bean and also provide some mapping metadata either in the XML or by used annotations.

Entity Manager on the other hand is the one who does manage the Entity Bean to data transformation. It does store the Entity Beans as a table row, or update an existing row or find rows as per given criteria and return collection of Entity beans.

3.1 Entity Beans (Not all POJOs can become Entities)

Not all POJOs can become Entity Beans. To become Entity Bean, a POJO must,

  1. Have annotated with @Entity annotation
  2. Not be final neither its fields or methods be.
  3. Implement Serializable
  4. Have a No-Argument constructor. Other constructors are allowed along with it.

See the below Song entity beans where I have tried to some of the JPA annotations and described what they are. Note: I have skipped the public access methods.

package com.amitph.spring.songs.repo;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import java.util.Date;

@Entity
public class Song {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "title", updatable = false) private String name;

    @ManyToOne
    @JoinColumn(name = "artist_id")
    private Artist artist;
    private int duration;

    @Enumerated(EnumType.STRING) private SongGenre genre;

    @Temporal(value = TemporalType.DATE) private Date releaseDate;

    private double rating;
    private String downloadUrl;
}

There are few thing to notice here.

  • @Entity: annotates the POJO as an Entity Bean.
  • @Id: represents primary column
  • @GeneratedValue:  Specifies that the ID is auto-increment. The field should be left blank when inserting a new Song in database
  • @Column: Optional if the field name and table’s column name are same. If different,  use the name field to specify column name. The updatable=false tells not to update this column if table row is updated. Can be can be left blank and column won’t be set as null on update
  • @ManyToOne: Denotes foreign key reference with Many to One relationship. One artist can have many songs. 
  • @JoinColumn: Specifies name of the column which represents the foreign key association. 
  • @Enumerated: Specifies, the column is Enumerated. No other values will be allowed.
  • @Temporal Used to denote a Date or Timestamp field. 

Using the Entity definition, when the Song and Artist tables are auto-generated in the database the SQL source looks like below. You can see primary key, foreign key and all the data types are mapped correctly. 

CREATE TABLE IF NOT EXISTS song
(
   id            BIGINT         NOT NULL,
   download_url  VARCHAR(255),
   duration      INT            NOT NULL,
   genre         VARCHAR(255),
   title         VARCHAR(255),
   rating        DOUBLE         NOT NULL,
   release_date  DATE,
   artist_id     BIGINT,
   PRIMARY KEY (id)
);

ALTER TABLE song
  ADD CONSTRAINT FKa21ft97nj7thwrp5d31xdaxr FOREIGN KEY (artist_id)
  REFERENCES artist (id)
  ON UPDATE NO ACTION
  ON DELETE NO ACTION;

3.2 Entity Manager

The Entity Manager is the one who does most of the ground work behind it’s abstraction. It is responsible for storing, updating, and retrieving entities from the database.

To create an Entity Manager we first need to create Entity Manager factory. Let’s create an Entity Manager instance.

EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("PERSISTENCE_UNIT_NAME");
EntityManager em = emFactory.createEntityManager();
        

An Entity Manager is usually associated with a group of Entities it is going to manage. These group of entities can be all the entities into an application or related entities into a module and must belong to the same database. We can chose a logical name to the group of Entities and associate the factory to that group by providing the PERSISTENCE_UNIT_NAME.

Well, Spring Boot saves you from creating the Entity Manager manually. Spring Boot’s auto-configuration does it already for you. What just need to add a dependency of spring-boot-starter-data-jpa then provide datasource configurations in application properties and autowire the Entity Manager it is needed. For the details about Spring Boot and auto-configurations please visit Spring Boot Rest Service.
Let’s perform basic crud using the EntityManager.

Create new Song

Song song = createNewSong();
entityManager.persist(song);

Find a Song and then Delete

Song song = entityManager.find(Song.class, 12312L);
entityManager.remove(song);

Update a song. Update is find, change, and save combination. 

Song song = entityManager.find(Song.class, 12312L);
song.setDownloadUrl("This is new url");
entityManager.persist(song);

4 Java Persistence Query Language (JPQL)

Sometimes, though the Entity Manager api isn’t enough to do certain searches or format data in certain ways. JPA provides a way to write SQL queries  which eases complex searches or formatting. Well, actually those are not SQL queries those are JPQL (Java Persistence Query Language) queries which are SQL like.

JPQL queries, unlike SQL queries can run on any type of relational databases. One thing to remember is, the queries are written against the Entities and not tables and they use the same abstract Entity persistence context along with the Entity Mapping and metadata. Let’s fetch Songs from database using JPQL way.  Which means, the table name and field names are not actually the names from database, they are the names of the Entity Beans and its fields.

4.1 Find by JPQL Query

It is simply creating a query instance with a JPQL query string. Then setting up values if there are variables in the query and then executing it.

Query songQuery = entityManager.createQuery(
                "SELECT song FROM Song song " +
                        "WHERE song.artise = :artistName " +
                        "ORDER BY song.releasedate DESC");
songQuery.setParameter("artistName", "elvis presley");
        
List<Song> elvisSongs = songQuery.getResultList();

4.2 Find by JPQL `@NamedQuery`

The @NamedQuery comes really handy when it is about separating concerns and believe me always it is. Let’s first see how the named queries are written.

@Entity
@NamedQuery(name = "song by id", query = "SELECT s from Song s WHERE s.id = :id")
@NamedQuery(name = "songs by artist", query = "SELECT s from Song s WHERE s.artist = :artist")
public class Song {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private long id;

    @Column(name = "title", updatable = false) private String name;

    @ManyToOne
    @JoinColumn(name = "artist_id")
    private Artist artist;
    private int duration;

    @Enumerated(EnumType.STRING) private SongGenre genre;

    @Temporal(value = TemporalType.DATE) private Date releaseDate;

    private double rating;
    private String downloadUrl;
}

There are two @NamedQuery in the above. We will execute then using Entity Manager. 

EntityManager entityManager = emFactory.createEntityManager();

Query query = entityManager.createNamedQuery("songs by artist");
query.setParameter("artist", "elvis");
List<Song> elvisSongs = query.getResultList();

With Named Queries components can be segregated. Like each entity will have its own queries (join queries can be placed along wit the primary entity of the query) and provide them to the outside world. 

5 Java Persistence Criteria API

Writing JPQL may not be easy for everyone. Not everyone has good command over database queries. And JPA specifications understand that. That is why they have provided an API way to build your query which is called as Java Persistence Criteria API.

5.1 Simple Select Using Criteria

Let’s see, how to create and execute a simple Select query on Song.

EntityManager entityManager = emFactory.createEntityManager();

CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
CriteriaQuery criteriaQuery = criteriaBuilder.createQuery(Song.class);

Root<Song> songRoot = criteriaQuery.from(Song.class);
criteriaQuery.select(songRoot);

TypedQuery<Song> typedQuery = entityManager.createQuery(criteriaQuery);
List<Song> songs = typedQuery.getResultList();

Here we are selecting all the Songs from database. Compared to JPQL this my look little too much of objects and method calls. But when you try it for couple of times, it is actually intuitive. Let’s understand what is happening here

  • First Line: An Entity Manager is created
  • Next two lines: A Criteria Builder and a Criteria Query is created specifically for Song.class. It is like FROM part of sql query.
  • Next two lines: Here we specify we want to select entire Song object from Song table. This is like SELECT in sql query.
  • Last two lines: Finally we create the query and execute it, which is similar to executing JPQL queries we saw in the last section. 

So finally the query it is equivalent of is SELECT song FROM SONG song

5.2 Filter Results Using Criteria

We know how to perform a Select operation. We will see how can we add a Where clause to our query. For specifying filter JPA provides Predicate class whose one instance defines one condition. For multiple where statements we can have an array of Predicate.

Root<Song> songRoot = criteriaQuery.from(Song.class);
criteriaQuery.select(songRoot);

Predicate[] predicates = new Predicate[1];
predicates[0] = criteriaBuilder.or(
    criteriaBuilder.equal(songRoot.get("artist"), "elvis"),
    criteriaBuilder.equal(songRoot.get("artist"), "presley")
);

criteriaQuery.orderBy(criteriaBuilder.desc(songRoot.get("releaseDate")));
criteriaQuery.where(predicates);

TypedQuery<Song> typedQuery = entityManager.createQuery(criteriaQuery);
        List<Song> songs = typedQuery.getResultList();

What is happening here?

  • First two lines: like in the last example we create a query and root object.
  • Next Four Lines: Created array of Predicate. Used CriteriaBuilder to create an OR statement. Added the criteria as the only element in the Predicate array.
  • Next Two Lines: Added an Order by clause and where clause to CriteriaQuery
  • Last lines: Created TypedQuery from CriteriaQuery and retrieved results. 

The equivalent JPQL for this example will be 
SELECT song FROM song WHERE song.name = 'elvis' OR song.name = "presley" ORDER BY song.releaseDate DESC

6 Summary

In this lengthy guide we have seen the plain JPA specifications in detail. Intentionally, we have kept the implementations (e.g. Hibernate, EclipseLink) out of the discussion. We learned why JPA was created, what is JPA Highlights and what are the three main parts of JPA e.g. Entity and Entity Manager, JPQL, and Criteria.

  • We then looked into an Entity and Entity Manager, and learnt how to create them, annotate them and use them.
  • We then had overview of the Java Persistence Query Language (JPQL) and executed simple queries to retrieve results. 
  • Finally, we saw JPA Criteria API and learnt any type of query can be built without  writing actual JPQL query.

This guide was a part of an ongoing learning series of JPA and Spring Data JPA