Java Persistence API -JPA-



What is Java Persistence API -JPA-?
Persistence
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 set up)

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
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

Synchronization occurs through the possible use of the following annotations attaced to on-purpose Java methods.

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 follows.

<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 follows.

// 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 RDBMS (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 { …

pkJoinColumns annotation 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;

Rules

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

Example (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
    …

Example (default mode: two related entities are linked by means of a foreign key while 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;
    …

Example (two related entities are linked by means of a third-party table)

This case is similar to @javax.persistence.ManyToOne below.

Bidirectionality

Entity Bean relationships can be either mono-directional or bidirectional.

Example of bidirectionality using @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 RDBMS. Moreover, the mutual consistency has to be maintained by the developer
    …

Note: the mappeBy annotation attribute is similar to the inverse = "true" indicator within Hibernate.

@javax.persistence.OneToMany

Example (see association between Prisoner and 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

Example (see association between Prisoner and 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 (line 2 below)

Example (see association between Prisoner and 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

Example (see association between Prisoner and Criminal_case -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 RDBMS. 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)
// '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

Example (dependency schema between Entity Beans)

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.

Example (collection-based requests)

Look for prisoners who are involved in a given criminal case (?1 parameter represents the first parameterized position) → see offense navigation in figure above

SELECT p FROM Prisoner p WHERE ?1 MEMBER OF p._offense

Look for all decisions linked to all prisoners (two equivalent solutions) → _judicial_decision corresponds to the navigation from Prisoner to Judicial_decision in figure above

SELECT judicial_decision FROM Prisoner p,
IN (p._judicial_decision) judicial_decision
SELECT ELEMENTS(p._judicial_decision) FROM Prisoner p

Longer navigation (_jurisdiction_name is a field of the Criminal_case Entity Bean) → see offense navigation in figure above

SELECT criminal_case FROM Prisoner p,
IN(p._offense) criminal_case,
IN(criminal_case._jurisdiction_name) jurisdiction_name
WHERE jurisdiction_name = 'Pau'

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

DELETE and UPDATE JPQL statements are redundant with remove and merge methods within an entity manager. They may however be useful for bulk processing.

Example

Inheritance

Overview

JPA enables the management of inheritance based on three strategies.

  1. Several Entity Beans involved in an inheritance hierarchy map to only one table in the database (SINGLE_TABLE): this is the default mode.
  2. Only concrete Entity Beans have their own mapping tables without any factorization table (TABLE_PER_CLASS).
  3. All Entity Beans have their own mapping tables (JOINED) including abstract and intermediate classes.

Inheritance in JPA is a support for polymorphic requests.

SINGLE_TABLE strategy

Example

Mother class

@javax.persistence.Inheritance
@javax.persistence.DiscriminatorColumn(name = "SEX", discriminatorType = javax.persistence.DiscriminatorType.CHAR)
abstract public class Human_being implements java.io.Serializable { …

Daughter class

@javax.persistence.DiscriminatorValue('1')
public class Male extends Human_being { …

TABLE_PER_CLASS strategy

Example

Mother class

@javax.persistence.Inheritance(strategy = javax.persistence.InheritanceType.TABLE_PER_CLASS)
abstract public class Human_being implements java.io.Serializable { …

Daughter class

 // No special annotation is required!
public class Male extends Human_being { …

JOINED strategy

Example

Mother 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 { …

Daughter class

@javax.persistence.DiscriminatorValue(Conviction.DECISION_TYPE_NUMBER)
public class Conviction extends Judicial_decision {
    public static final String DECISION_TYPE_NUMBER = "1";

Utility annotations

The @javax.persistence.MappedSuperclass 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;
    …

Other utility annotations

Example

@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