Java Persistence API -JPA-


Creative Commons License
This -Java Persistence API- -JPA- tutorial is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License
Preamble

This tutorial is about the Java Persistence API -JPA-. JPA is a Java SE technology (see API here…).

Persistence
What is JPA?
JPA engine most famous implementations
Packages
Case study
The notion of POJO (a.k.a. Entity Beans in Java EE)
Getting a persistence manager

Entity Beans can be managed through a persistence manager only.

Example Java EE

@javax.persistence.PersistenceContext(unitName = "New_York_City_Penitentiary", type = javax.persistence.PersistenceContextType.TRANSACTION))
private javax.persistence.EntityManager _entityManager; // No instantiation required; this is resource injection...

Example Java SE (persistence.xml file must be setup)

javax.persistence.EntityManagerFactory _emf = javax.persistence.Persistence.createEntityManagerFactory("New_York_City_Penitentiary");
javax.persistence.EntityManager _entityManager = _emf.createEntityManager();

Example of persistence.xml file (Java SE)

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
  <persistence-unit name="New_York_City_Penitentiary" transaction-type="RESOURCE_LOCAL">
    <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
    <class>com.franckbarbier.nycp_java_se.Prisoner</class>
    <class>com.franckbarbier.nycp_java_se.Shortened_sentence</class>
    <class>com.franckbarbier.nycp_java_se.Final_discharge</class>
    <class>com.franckbarbier.nycp_java_se.Conviction</class>
    <class>com.franckbarbier.nycp_java_se.Motive</class>
    <class>com.franckbarbier.nycp_java_se.Criminal_case</class>
    <class>com.franckbarbier.nycp_java_se.Judicial_decision</class>
    <class>com.franckbarbier.nycp_java_se.Incarceration</class>
    <properties>
      <property name="javax.persistence.jdbc.url" value="jdbc:derby://localhost:1527/New_York_City_Penitentiary_database"/>
      <property name="javax.persistence.jdbc.user" value="FranckBarbier"/>
      <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.ClientDriver"/>
      <property name="javax.persistence.jdbc.password" value="FranckBarbier"/>
    </properties>
  </persistence-unit>
</persistence>

Property setup for a specific persistence provider

<properties>
    <property name="hibernate.show_sql" value="true"/>
</properties>
Entity Beans' lifecycle
Entity Beans states
Key operations on Entity Beans
_entityManager.persist(prisoner);
Prisoner updated_prisoner = (Prisoner) _entityManager.merge(prisoner); // 'updated_prisoner' is managed while 'prisoner' is no longer managed
_entityManager.remove(prisoner);
Prisoner prisoner = _entityManager.find(Prisoner.class,primary_key);
Prisoner prisoner = _entityManager.getReference(Prisoner.class,primary_key);
_entityManager.refresh(prisoner); // Re-synchronization with the database's real-time state. All modifications are lost
Synchronization with Entity Beans' lifecycle

@javax.persistence.PrePersist, @javax.persistence.PostPersist, @javax.persistence.PreRemove, @javax.persistence.PostRemove, @javax.persistence.PreUpdate, @javax.persistence.PostUpdate, and @javax.persistence.PostLoad

Example

@javax.persistence.PostLoad
public void just_loaded() {
    System.out.println(this.toString() + " just loaded...");
}
Key operations on the global pool of Entity Beans
_entityManager.clear(); // All managed Entity Beans are detached. All modifications are lost
_entityManager.flush(); // All managed Entity Beans are detached. All modifications will be definitely made at commit time
Transaction management

Transaction support

A transaction support is injected as resource as in the following example (BMT standing for Bean-Managed Transaction).

@javax.annotation.Resource
private javax.transaction.UserTransaction _userTransaction; // No instantiation required!

JTS (standing for Java Transaction Service) is by default enabled in the persistence unit; it can be disabled as in the following example.

<persistence-unit name="New_York_City_Penitentiary" transaction-type="RESOURCE_LOCAL">
    …
    <non-jta-data-source>New_York_City_Penitentiary</non-jta-data-source>      
    …
</persistence-unit>

Managing transactions manually is done as in the following example.

// Transactions are managed in the Bean-Managed Transaction mode (Java SE for instance):
private javax.persistence.EntityTransaction _entityTransaction = _entityManager.getTransaction();

Dealing with transactions (Bean-Managed Transaction mode)…

Transaction-scoped persistence context (javax.persistence.PersistenceContextType.TRANSACTION)

_userTransaction.begin(); // '_entityManager' is enabled
_entityManager.persist(prisoner);
assert(_entityManager.contains(prisoner) == true);
_userTransaction.commit(); // '_entityManager' is disabled

Extended persistence context (javax.persistence.PersistenceContextType.EXTENDED)

_userTransaction.begin();
_entityManager.joinTransaction();
_entityManager.persist(prisoner); // It may also be called outside a transaction scope
_userTransaction.commit();
Constructing Entity Beans

Building a JPA application or simple module is mostly concerned with creating Entity Beans that map to tables in a database. However, this mapping has not to be, in any case, a one-to-one mapping, i.e., 1 table does not systematically amount to 1 and only 1 Entity Bean.

The mapping is thus ruled by creation processes, key and recurrent navigation between objects, performance with suitable dependency loading (lazy or eager modes).

Constraints (not illustrated in examples)

Simple Entity Bean

Basic annotations

Example

@javax.persistence.Entity(name = "My_prisoner") // Class name by default when 'name' is not used
@javax.persistence.Table(name = "Prisoner") // It is optional when the table name is equal to the Java class name
public class Prisoner implements java.io.Serializable { …

Persistent field

  • All non-static, non-transient fields are by default persistent unless annotated by @javax.persistence.Transient.
  • The @javax.persistence.Basic annotation confirms the persistent nature of a field with the possibility of configuring properties.

Example

@javax.persistence.Basic(fetch=javax.persistence.FetchType.LAZY /* By default */, optional=true) // The other value for the fetch attribute is 'javax.persistence.FetchType.EAGER'
@javax.persistence.Column(name = "CUSTOMER_NICKNAME")
private String _customer_nickname;

Temporal persistent field

  • Fields of type java.util.Date or java.util.Calendar must be annotated with @javax.persistence.Temporal.

Example

@javax.persistence.Column(name = "DATE_OF_INCARCERATION")
@javax.persistence.Temporal(javax.persistence.TemporalType.DATE)
private java.util.Date _date_of_incarceration;

Enumerated persistent field

  • Fields of type enum in Java may be used in conjunction with JPA with the help of @javax.persistence.Enumerated.

Example (Java)

public enum Fire_truck_statuses {Idle, Dispatched, Arrived , Blocked, Breakdown}

Example -JPA-

@javax.persistence.Column(name = "FIRE_TRUCK_STATUS")
@javax.persistence.Enumerated(javax.persistence.EnumType.STRING)
private Fire_truck_statuses _fire_truck_status;

Example (SQL)

create table BCMS_session_Fire_truck(
…
    FIRE_TRUCK_STATUS varchar(10) constraint FIRE_TRUCK_STATUS_CHECK check (fire_truck_status IN ('Idle', 'Dispatched', 'Arrived', 'Blocked', 'Breakdown')),
…

Embeddable and embedded Entity Beans

Embeddable Entity Beans do not map to tables. Instead, they later become field types in enclosing Entity Beans.

@javax.persistence.Embeddable
public class Address
    …
    @javax.persistence.Column(name = "ZIP_CODE", length = 5)
    private String _zip_code;
    …
…                      
@javax.persistence.Entity // Table name must be 'Customer'
public class Customer implements java.io.Serializable {
    …
    @javax.persistence.Embedded
    @javax.persistence.AttributeOverrides({@javax.persistence.AttributeOverride(name = "_zip_code", column = @javax.persistence.Column(name = "ZIP_CODE_OF_CUSTOMER_INVOICING_ADDRESS"))})
    private Address _invoicing_address;
    @javax.persistence.Embedded
    private Address _shipping_address;
    …
…

More about @javax.persistence.Column

@javax.persistence.Column(name = "MOTIVE_LABEL", nullable = false, unique = true) // With 'constraint MOTIVE_unique unique(MOTIVE_LABEL)'
private String _motive_label;
Other properties of @javax.persistence.Column:
  • insertable (default value is true)
  • updatable (default value is true)

Primary key

Simple primary keys are based on elementary types (long, Long, String…). The @javax.persistence.Id annotation is necessary.

@javax.persistence.Entity // Table name must be 'Customer'
public class Customer implements java.io.Serializable {
    @javax.persistence.Id
    @javax.persistence.Column(name = "CUSTOMER_PIN") // With 'constraint CUSTOMER_key primary key(CUSTOMER_PIN)'
    private String _customer_PIN;
    …

Composite primary key

Composite primary keys are based on non-elementary types that require the construction of devoted classes. The @javax.persistence.Embeddable and @javax.persistence.EmbeddedId annotations are necessary.

@javax.persistence.Embeddable
public class Criminal_casePK implements java.io.Serializable { 
    @javax.persistence.Basic(optional = false)
    @javax.persistence.Column(name = "CRIMINAL_CASE_NUMBER")
    private String _criminal_case_number;
    @javax.persistence.Basic(optional = false)
    @javax.persistence.Column(name = "JURISDICTION_NAME")
    private String _jurisdiction_name;
    …
…
@javax.persistence.Entity
@javax.persistence.Table(name = "CRIMINAL_CASE") // Optional since class name is equal to table name
public class Criminal_case implements java.io.Serializable {
    @javax.persistence.EmbeddedId // With 'constraint CRIMINAL_CASE_key primary key(CRIMINAL_CASE_NUMBER,JURISDICTION_NAME)'
    private Criminal_casePK _criminal_casePK;
    …
…

Alternative method:

public class Criminal_casePK implements java.io.Serializable { … // '@javax.persistence.Embeddable' is no longer used!
…
@javax.persistence.Entity
@javax.persistence.IdClass(Criminal_casePK.class)
@javax.persistence.Table(name = "CRIMINAL_CASE") // Optional since class name is equal to table name
public class Criminal_case implements java.io.Serializable { …

Business versus generated primary key

Instead of having primary keys based on business fields, e.g., CUSTOMER_PIN above, primary keys may be generated by the DBMS (depending upon offered support for that). Strategies are: AUTO (default), IDENTITY, SEQUENCE or TABLE.

@javax.persistence.Entity
public class Order implements java.io.Serializable {
    @javax.persistence.Id
    @javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)
    @javax.persistence.Column(name = "ORDER_ID") // With 'create table ORDER(ORDER_ID integer generated always as identity, …);'
    private Integer _id;
    …

Alternative strategy:

@javax.persistence.Id
@javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.SEQUENCE, generator = "MY_GENERATOR")
@javax.persistence.Column(name = "ORDER_ID") // With 'create sequence MY_GENERATOR;'
private Integer _id;

Entity Bean merging two or more tables

For instance, the CRIMINAL_CASE and JURISDICTION tables are merged into the Criminal_case Entity Bean:

@javax.persistence.Entity
@javax.persistence.Table(name = "CRIMINAL_CASE") // Optional since class name is equal to table name
@javax.persistence.SecondaryTable(name = "JURISDICTION", pkJoinColumns = {@javax.persistence.PrimaryKeyJoinColumn(name = "JURISDICTION_NAME")})
public class Criminal_case implements java.io.Serializable { …


The pkJoinColumns attribute may be omitted here. The Criminal_case Entity Bean gets the _jurisdiction_address persistent field from JURISDICTION table:

@javax.persistence.Column(name = "JURISDICTION_ADDRESS", table = "JURISDICTION")
private String _jurisdiction_address;


Remarks:

Entity Bean relationships

The following JPA annotations are available to manage Entity Bean relationships (foreign keys in tables): The following Java interfaces can be used to manage association ends with the many role:

@javax.persistence.OneToOne

Case 1: the two related entities are linked by means of their primary keys

@javax.persistence.Entity // Table name must be 'Customer'
public class Customer implements java.io.Serializable {
@javax.persistence.Id
@javax.persistence.Column(name = "CUSTOMER_PIN") // With 'constraint CUSTOMER_key primary key(CUSTOMER_PIN)'
private String _customer_PIN;

@javax.persistence.OneToOne
@javax.persistence.PrimaryKeyJoinColumn
private Client _client; // Entity Bean 'Client' offers a primary key similar to '_customer_PIN' in 'Customer' Entity Bean

Case 2 (default mode): the two related entities are linked by means of a foreign key (this one is a priori unique in the target entity)

@javax.persistence.Entity
public class Order implements java.io.Serializable {
@javax.persistence.Id
@javax.persistence.GeneratedValue(strategy = javax.persistence.GenerationType.IDENTITY)
@javax.persistence.Column(name = "ORDER_ID") // With 'create table ORDER(ORDER_ID integer generated always as identity, …);'
private Integer _id;

@javax.persistence.OneToOne // An order leads to one and only one invoice
@javax.persistence.JoinColumn(name = "ORDER_ID", referencedColumnName = "INVOICE_ID")
private Invoice _invoice;

Case 3: the two related entities are linked by means of a third-party table

This case is similar to @javax.persistence.ManyToOne (see below)

Bidirectionality

Entity Bean relationships can be either mono-directional or bidirectional. Example of bidirectionality with @javax.persistence.OneToOne:

@javax.persistence.Entity
public class Invoice implements java.io.Serializable {

@javax.persistence.OneToOne(mappedBy = "_invoice")
private Order _order; // Caution: JPA does not use this side to interact with the DBMS. Moreover, the mutual consistency has to be maintained by the developer

Note: the mappeBy annotation property is similar to the inverse = "true" indicator in Hibernate

@javax.persistence.OneToMany

See association from Prisoner to Conviction in figure below

@javax.persistence.OneToMany(cascade = javax.persistence.CascadeType.REMOVE) // Cascade operations may be setup (no cascade by default) by means of javax.persistence.CascadeType
@javax.persistence.JoinColumn(name = "PRISON_FILE_NUMBER")
private java.util.Set<Conviction> _conviction;

@javax.persistence.ManyToOne

See association from Prisoner to Motive in figure below

@javax.persistence.ManyToOne(optional = false)
@javax.persistence.JoinColumn(name = "MOTIVE_NUMBER", referencedColumnName = "MOTIVE_NUMBER")
private Motive _incarceration_motive;

@javax.persistence.ManyToMany (using a third-party table: in boldface)

See association from Prisoner to Criminal_case (offense role in figure below)

@javax.persistence.ManyToMany(/* cascade = javax.persistence.CascadeType.MERGE */) // 'Prisoner' owns the relationship
@javax.persistence.JoinTable(name = "PRISONER_CRIMINAL_CASE", joinColumns = {
@javax.persistence.JoinColumn(name = "PRISON_FILE_NUMBER", referencedColumnName = "PRISON_FILE_NUMBER")},
// 'CRIMINAL_CASE_NUMBER'-'JURISDICTION_NAME' is a foreign key in 'PRISONER_CRIMINAL_CASE' to 'Criminal_case':
inverseJoinColumns = { @javax.persistence.JoinColumn(name = "CRIMINAL_CASE_NUMBER", referencedColumnName = "CRIMINAL_CASE_NUMBER"), @javax.persistence.JoinColumn(name = "JURISDICTION_NAME", referencedColumnName = "JURISDICTION_NAME") })
private java.util.Set<Criminal_case> _offense;

Bidirectionality

See association from Criminal_case to Prisoner (participant role in figure below)

@javax.persistence.ManyToMany(mappedBy = "_offense") // 'Criminal_case' does not own the relationship (cascading operations with 'javax.persistence.CascadeType' is a priori useless)
private java.util.Set<Prisoner> _participant; // Caution: JPA does not use this side to interact with the DBMS. Moreover, the mutual consistency has to be maintained by the developer

@javax.persistence.ManyToMany side loading

By default, multi-valued associations are ruled by the lazy loading mode (for performance reasons). One may change that as follows (see association from Prisoner to Shortened_sentence in figure below):

@javax.persistence.OneToMany(cascade = javax.persistence.CascadeType.REMOVE, fetch = javax.persistence.FetchType.EAGER) // Each time, a 'Prisoner' Entity Bean is loaded (with 'find' for instance), all of its 'Shortened_sentence' Entity Beans are concomitantly loaded. Instead, the lazy mode leads to the loading of these when one accesses the collection itself using 'get_shortened_sentence'
@javax.persistence.JoinColumn(name = "PRISON_FILE_NUMBER")
private java.util.Set<Shortened_sentence> _shortened_sentence;
JPA Query Language (JPQL)

Principles

Basic requests

Advanced requests

Request

Prisoners “under remand”, i.e., prisoners for which no conviction decision has been taken

SQL-like style

Native queries are standard SQL queries. They do not rely on Entity Beans

Declaration

@javax.persistence.NamedNativeQuery(name = "Prisoner.Under_remand_SQL", query = "SELECT * FROM Prisoner WHERE prison_file_number NOT IN (SELECT prison_file_number FROM Conviction)")

Usage

for (Object o : _entityManager.createNamedQuery("Prisoner.Under_remand_SQL").getResultList()) {
    try {
        Object[] prisoner_properties = (Object[]) o;
        System.out.print("Under remand: - ");
        for (int i = 0; i < prisoner_properties.length; i++) {
            System.out.print(prisoner_properties[i] + " - "); }
            System.out.println();                           
    }
    catch (ClassCastException cce) { // The type of 'o' is not 'Object[]', so? }                    
}

JPQL style (♡)

@javax.persistence.Entity(name = "MY_PRISONER")
@javax.persistence.Table(name = "PRISONER")
@javax.persistence.SecondaryTable(name = "INCARCERATION", pkJoinColumns = {@javax.persistence.PrimaryKeyJoinColumn(name = "PRISON_FILE_NUMBER")})
@javax.persistence.NamedNativeQuery(name = "Prisoner.Under_remand_SQL", query = "SELECT * FROM Prisoner WHERE prison_file_number NOT IN (SELECT prison_file_number FROM Conviction)")
@javax.persistence.NamedQueries({@javax.persistence.NamedQuery(name = "Prisoner.All_JPQL", query = "SELECT p FROM Prisoner"),
                                 @javax.persistence.NamedQuery(name = "Prisoner.Under_remand_JPQL", query = "SELECT p FROM Prisoner WHERE p._conviction IS EMPTY")
                                })    
public class Prisoner implements java.io.Serializable { …

The power of JPQL relies on the strong utilization of navigations between Entity Beans

Collection-based requests

Request

Prisoners who are not involved in a given criminal case (the latter is a parameter in the named query, i.e., :criminal_case)

Declaration

@javax.persistence.NamedQuery(name = "Prisoner.Not_involved_in_criminal_case_V1", query = "SELECT p FROM Prisoner p WHERE :criminal_case NOT MEMBER OF p._offense"), // See 'offense' navigation in figure above
@javax.persistence.NamedQuery(name = "Prisoner.Not_involved_in_criminal_case_V2", query = "SELECT p1 FROM Prisoner p1 WHERE p1 NOT IN (SELECT p2 FROM Criminal_case c INNER JOIN c._participant p2 WHERE c = :criminal_case)") // See 'participant' navigation in figure above

Usage

public java.util.Collection<persistence.Prisoner> non_participants(persistence.Criminal_case criminal_case) {
    assert (_entityManager != null);
    criminal_case = _entityManager.find(persistence.Criminal_case.class, new persistence.Criminal_casePK(criminal_case.get_criminal_case_number(), criminal_case.get_jurisdiction_name()));
    return _entityManager.createNamedQuery("Prisoner.Not_involved_in_criminal_case_V1").setParameter("criminal_case", criminal_case).getResultList();
}

Beyond SELECT

While DELETE and UPDATE JPQL are available, their possible usage is redundant with remove and merge from an entity manager. They may however be useful for bulk processing:
Inheritance

Overview

JPA enables the management of inheritance based on three strategies:
Inheritance in JPA is a support for polymorphic requests

SINGLE_TABLE strategy

Root class
@javax.persistence.Inheritance
@javax.persistence.DiscriminatorColumn(name = "SEX", discriminatorType = javax.persistence.DiscriminatorType.CHAR)
abstract public class Human_being implements java.io.Serializable { …
Descendant class
@javax.persistence.DiscriminatorValue('1')
public class Male extends Human_being { …

TABLE_PER_CLASS strategy

Root class
@javax.persistence.Inheritance(strategy = javax.persistence.InheritanceType.TABLE_PER_CLASS)
abstract public class Human_being implements java.io.Serializable { …
Descendant class
// No special annotation is required!
public class Male extends Human_being { …

JOINED strategy

Root class
@javax.persistence.Inheritance(strategy = javax.persistence.InheritanceType.JOINED)
@javax.persistence.DiscriminatorColumn(name = "DECISION_TYPE_NUMBER", discriminatorType = javax.persistence.DiscriminatorType.STRING)
abstract public class Judicial_decision implements java.io.Serializable { …
Descendant class
@javax.persistence.DiscriminatorValue(Conviction.DECISION_TYPE_NUMBER)
public class Conviction extends Judicial_decision {
public static final String DECISION_TYPE_NUMBER = "1";

Other useful annotations

@javax.persistence.MappedSuperclass: this annotation is used for factorizing JPA properties in a utility class but not for setting up inheritance from the database viewpoint (no concomitant use of @javax.persistence.Entity) is allowed! Example:

@javax.persistence.MappedSuperclass // No table is associated with this class. It just serves as factorization for inheriting classes
public class Customer implements java.io.Serializable {
@javax.persistence.Id
@javax.persistence.Column(name = "CUSTOMER_PIN") // With 'constraint CUSTOMER_key primary key(CUSTOMER_PIN)'
private String _customer_PIN;

@javax.persistence.OneToMany
private java.util.Collection<Order> _order;

@javax.persistence.Entity
@javax.persistence.AttributeOverride(name="_customer_PIN",column=@javax.persistence.Column(name="SPECIAL_CUSTOMER_ID"))
@javax.persistence.AssociationOverride(name="_order",joinColumns=@javax.persistence.JoinColumn(name="SPECIAL_CUSTOMER_ID"))
public class Special_customer extends Customer { …
Metadata