Java Persistence API (JPA) Part 1: Getting Started ☕

|

The Java Persistence API (JPA) is the java community specification for persisting data between objects and a database. It has collected the best ideas from existing persistence technologies like Hibernate, TopLink, and JDO. The result is a standardized specification that helps you build a persistence layer that is independent of any particular persistence provider.

Version History 📖🔗

  • JPA 1.0 was released on May 11, 2006 using JSR 220.
  • JPA 2.0 was released with the specifications of JAVA EE6 on December 10, 2009 using JSR 317.
  • JPA 2.1 was released with the specification of JAVA EE7 on April 22, 2013 using JSR 338.
  • JPA 2.2 is a maintenance release that was approved on 19 June 2017.

Providers 🎁

A lot of the persistence vendors support JPA:

Features 📋

  • Object-relational mappings can be done through annotations, or in XML.
  • JPA defines a runtime EntityManager API for processing queries and transaction on objects.
  • JPA defines an object query language called JPQL.
  • Criteria API can be used for dynamic query creation, and allows type checking on criteria. The Criteria API is a complement, and alternative to JPQL queries.
  • Validation support. Entities can be annotated to enforce data constraints.
  • Applications can be run inside a container, or standalone. So, developers can use JPA for web applications and desktop applications.
  • JPA supports inheritance, polymorphism, and polymorphic queries.

The JPA 2.1 specification added new features that are not available in the JPA 2.0 specification. These features include:

  • Schema generation.
  • Mapping of native queries.
  • Type conversion methods.
  • Entity graphs in queries and find operations.
  • Unsynchronized persistence contexts.
  • Stored procedure invocation.
  • Injection into Entity listener classes.
  • Some JPQL enhancements.
  • Some Criteria API enhancements.

How to use? 🔬

1. Create an entity class

We annotate a class with @Entity to make it persistent.

@Entity
@Table(name = "student")
public class Student{

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int studentID;

}

The mappings are done through JPA annotations here, but can be done through a configuration file called orm.xml also.

There are a few points to keep in mind when creating an entity class:

  • The entity class should have a default no-argument constructor.
  • All fields are automatically persisted, there is no need to use @column annotation. However, if the column name is different than the variable name, you must include it.
  • Using a SQL keyword like order for a class name will cause problems. Either name the class something else (CustomerOrder), or specify the table name as something else (as below).
@Entity(name="orders")
public class Order{}
  • The entity class should not be final.
  • If a persistent class inherits from a non-persistent class, the fields of the non-persistent superclass cannot be persisted.

2. A persistence.xml file

A configuration file named persistence.xml is required, and should be placed in the META-INF folder. It is used to specify a persistence unit, which is a logical group of persistent classes and database settings.

<persistence version="2.1" xmlns="http://xmlns.jcp.org/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_1.xsd">
<persistence-unit name="school"  transaction-type="RESOURCE_LOCAL">
    <class>org.redi.backend.Student</class>
    <class>org.redi.backend.Course</class>
    <class>org.redi.backend.Teacher</class>
    <properties>
      <property name="hibernate.connection.driver_class" value="org.sqlite.JDBC"/>
      <property name="hibernate.connection.url" value="jdbc:sqlite:redi.sqlite"/>
      <property name="hibernate.connection.pool_size" value="0"/>
      <property name="hibernate.dialect" value="com.enigmabridge.hibernate.dialect.SQLiteDialect"/>
      <property name="hibernate.connection.username" value=""/>
      <property name="hibernate.connection.password" value=""/>
      <property name="hibernate.show_sql" value="true"/>
      <property name="hibernate.format_sql" value="true"/>
    </properties>
</persistence-unit>
</persistence>

Some important things to note about persistence.xml:

  • it can have multiple persistence units. Each unit can be used for a different JPA vendor; or can be used to persist to different databases.
  • The vendor-specific persistence provider name can be specified in the tag.
  • The entity class names are specified in the tag.
  • The database connection properties are specified within the tag.

3. Write a class that uses an EntityManager

The JPA specification classifies two types of entity managers:

  • container-managed: typically a Java Enterprise Edition (JEE) container such as an application server or a web container.
  • standalone: standalone program.

In a standalone environment, you can create the EntityManager as shown below, you pass the persistence unit name to createEntityManagerFactory().

private EntityManagerFactory factory = null;
private EntityManager entityManager = null;

private void init() {
  factory = Persistence.createEntityManagerFactory("school");
  entityManager = factory.createEntityManager();
}

In a container-managed environment, the container does the set-up work for you. The class with a reference to an EntityManager (typically DAO objects) will be injected with an existing EntityManager.

Core Annotations

Annotation Target Purpose
Column F, M Can provide the table column name if you want it to be different from the variable name. By default, the two names are assumed to be the same.
id F, M Specify the primary key.
Entity T Declare the class as entity to be persisted.
Table T Specify the table for the entity. The default table name is the name of the class with the first letter in small letter. You can set a name by: @Table(name=”EMPLOYEE”)
UniqueConstraint Pm Enforce an unique constraint for the table in the schema.
Key to Target Column: Pk = Package, T = Type, M = Method, F= Field, Pm = parameter

Primary keys

You use the @id annotation for a single column primary key.

Id Generation

Name Target Purpose
GeneratedValue F, M Specify a strategy to assign a unique value to your identity fields automatically. The types of strategies available are IDENTITY, SEQUENCE, TABLE, and AUTO. The default strategy is auto.
SequenceGenerator Pk, T, M, and F Allows a named primary key generator to be defined for use by one or more entities.
TableGenerator Pk, T, M, and F Defines a primary key generator that may be referenced by name when generator element is specified for the GeneratedValue.
Key to Target Column: Pk = Package, T = Type, M = Method, F= Field, Pm = parameter


By default, the @Id annotation will automatically determine the most appropriate primary key generation strategy to use—you can override this by also applying the @GeneratedValue annotation. This takes a pair of attributes: strategy and generator.The strategy attribute must be a value from the javax.persistence.GeneratorType enumeration:

  • AUTO: JPA decides which generator type to use, based on the database’s support for primary key generation.
  • IDENTITY: The database is responsible for determining and assigning the next primary key.
  • SEQUENCE: Some databases support a SEQUENCE column type.
  • TABLE: This type keeps a separate table with the primary key values.

Generating Primary Key Values with @SequenceGenerator

A sequence is a vendor-specific database object that generates an id that is independent of any particular table, and can therefore be used by multiple tables. Only the name attribute is mandatory, the other attributes will take sensible default values.

@Id
@SequenceGenerator(name="seq1",sequenceName="SEQ")
@GeneratedValue(strategy=SEQUENCE,generator="seq1")
private long id;

Generating Primary Key Values with @TableGenerator

The @TableGenerator annotation is similar to the @SequenceGenerator annotation, but because it uses a standard database table to obtain its primary key values, it is guaranteed to be portable between database platforms.

@Id
@TableGenerator(name="tablegen",
table="ID_TABLE",
pkColumnName="ID",
valueColumnName="NEXT_ID")
@GeneratedValue(strategy=TABLE,generator="tablegen")
private long id;

Compound Primary Keys

However, when the primary key consists of multiple columns, you must create a class to represent the primary key.

The class must:

  • be public,
  • have a default constructor,
  • be serializable,
  • implement hashCode() and equals() methods to test for collisions

The strategies for using this primary key class once it has been created are as follows:

  1. Mark it as @Embeddable and add to your entity class a normal property for it, marked with @Id.
  2. Add to your entity class a normal property for it, marked with @EmbeddableId.
  3. Add properties to your entity class for all of its fields, mark them with @Id, and mark your entity class with @IdClass, supplying the class of your primary key class.

Access Mode

Annotation Target Purpose
Access T, F, M Set if the field or property (getter/setter) is used to access values.
Key to Target Column: Pk = Package, T = Type, M = Method, F= Field, Pm = parameter


  • There is no big difference in which you use.
  • If you annotate variables and a getter method, it changes the access type of the entity to property access, and can cause mapping issues. In that case, you need to be explicit with the access for everything. Below, we say that the access type is on fields for everything, except getId().
@Entity
@Access(AccessType.FIELD)
public class Student{
    private long id;
    private String name;

    @id
    @Access(AccessType.PROPERTY)
    public Long getId(){
      return id;
    }
}

Java Persistence API (JPA) Part 2: Building Relationships »


Additional Reading

Related Posts