JAVA PERSISTENCE API (Ahmad Chaaban)

Introduction to the Java Persistence API

The Java Persistence API provides Java developers with an object/relational mapping facility for managing relational data in Java applications. Java Persistence consists of four areas:

  • The Java Persistence API
  • The query language
  • The Java Persistence Criteria API

JPA Entity Classes

To be able to store objects in the database using JPA we need to define an entity class.

A JPA entity class is a POJO (Plain Old Java Object) class, i.e. an ordinary Java class that is marked (annotation) as having the ability to represent objects in the database.

The persistent state of an entity is represented through either persistent fields or persistent properties. These fields or properties use object/relational mapping annotations to map the entities and entity relationships to the relational data in the underlying data store.

The following Point class, which represents points in the plane, is marked as an entity class, and accordingly, provides the ability to store Point objects in the database and retrieve Point objects from the database:

 @Entity 
 public class Point 
 {
      // Persistent Fields:
    private int x;
    private int y;
 
    // Constructor:
    Point (int x, int y) {
        this.x = x;
        this.y = y;
    }
 
    // Accessor Methods:
    public int getX() { return this.x; }
    public int getY() { return this.y; }
 
    // String Representation:
    @Override
    public String toString() {
        return String.format("(%d, %d)", this.x, this.y);
    }
 }

JPA Persistable Types

The term persistable types refers to data types that can be used in storing data in the database.

The JPA persistable types :

  • User defined classes : Entity classes, Mapped superclasses, Embeddable classes.
  • Simple Java data types: Primitive types, Wrappers, String, Date and Math types.
  • Multi value types - Collections, Maps and Arrays.
  • Enum types.


JPA Entity Fields

If the entity class uses persistent fields, the Persistence runtime accesses entity-class instance variables directly.

All fields not annotated javax.persistence.Transient or not marked as Java transient will be persisted to the data store. The object/relational mapping annotations must be applied to the instance variables.


JPA Primary Key

Every entity object that is stored in the database has a primary key. It represents the entity object as long as it exists in the database.

Simple primary keys use the javax.persistence.Id annotation to denote the primary key property or field.

Composite primary keys are used when a primary key consists of more than one attribute, which corresponds to a set of single persistent properties or fields.

Composite primary keys must be defined in a primary key class. Composite primary keys are denoted using the javax.persistence.EmbeddedId and javax.persistence.IdClass annotations.

Generated Values

Marking a field with the @GeneratedValue annotation specifies that a value will be automatically generated for that field.

Several different value generation strategies can be used :

  • The Auto Strategy
  • The Identity Strategy
  • The Sequence Strategy
  • The Table Strategy

Index Definition

Querying without indexes requires iteration over entity objects in the database one by one.

This may take a significant amount of time if many entity objects have to be examined. Using proper indexes the iteration can be avoided and complex queries over millions of objects can be executed quickly.

Index management introduces overhead in terms of maintenance time and storage space, so deciding which fields to define with indexes should be done carefully.

JPA Persistence Unit

A persistence unit defines a set of all entity classes that are managed by EntityManager instances in an application. This set of entity classes represents the data contained within a single data store.

Persistence units are defined in a persistence.xml file, which has to be located in the META-INF directory in the classpath. One persistence.xml file can include definitions for one or more persistence units.

<persistence>
    <persistence-unit name="OrderManagement">
        <description>This unit manages orders and customers.
            It does not rely on any vendor-specific features and can
            therefore be deployed to any persistence provider.
        </description>
        <jta-data-source>jdbc/MyOrderDB</jta-data-source>
        <jar-file>MyOrderApp.jar</jar-file>
        <class>com.widgets.Order</class>
        <class>com.widgets.Customer</class>
    </persistence-unit>
</persistence>

Entity Object Life Cycle

Using JPA

Database Connection

A connection to a database is represented by an EntityManager instance, which also provides functionality for performing operations on a database.

The main role of an EntityManagerFactory instance is to support instantiation of EntityManager instances.

Operations that modify the content of a database require active transactions. Transactions are managed by an EntityTransaction instance obtained from the EntityManager.

An EntityManager instance also functions as a factory for Query instances, which are needed for executing queries on the database.

Persistence Context

The persistence context is the collection of all the managed objects of an EntityManager. If an entity object that has to be retrieved already exists in the persistence context, the existing managed entity object is returned without actually accessing the database (except retrieval by refresh, which always requires accessing the database).

The main role of the persistence context is to make sure that a database entity object is represented by no more than one in-memory entity object within the same EntityManager. Every EntityManager manages its own persistence context. Therefore, a database object can be represented by different memory entity objects in different EntityManager instances. But retrieving the same database object more than once using the same EntityManager should always result in the same in-memory entity object.

The contains method can check if a specified entity object is in the persistence context:

boolean isManaged = em.contains(employee);

Storing JPA Entity Objects

New entity objects can be stored in the database either explicitly by invoking the persist method or implicitly as a result of a cascade operation.

The Java Persistence API (JPA) provides various ways to store objects in the database:

  • Explicit Persist
  • Referenced Embedded Objects
  • Referenced Entity Objects
  • Cascading Persist
  • Global Cascading Persist
  • Batch Store

Retrieving JPA Entity Objects

The retrieval of objects does not require an active transaction because it does not change the content of the database.

The persistence context serves as a cache of retrieved entity objects. If a requested entity object is not found in the persistence context a new object is constructed and filled with data that is retrieved from the database. The new entity object is then added to the persistence context as a managed entity object and returned to the application.

The Java Persistence API (JPA) provides various ways to retrieve objects from the database:

  • Retrieval by Class and Primary Key
  • Retrieval by Query
  • Retrieval by Refresh
  • Cascading Refresh

Updating JPA Entity Objects

Modifying existing entity objects that are stored in the database is based on transparent persistence, which means that changes are detected and handled automatically.

Once an entity object is retrieved from the database ,it can simply be modified in memory from inside an active transaction.

The entity object is physically updated in the database when the transaction is committed. If the transaction is rolled back and not committed the update is discarded.

Deleting JPA Entity Objects

Existing entity objects can be deleted from the database either explicitly by invoking the remove method or implicitly as a result of a cascade operation.

In order to delete an object from the database it has to first be retrieved (no matter which way) and then in an active transaction, it can be deleted using the remove method.

The entity object is physically deleted from the database when the transaction is committed.

JPA Queries

The Java Persistence Query Language (JPQL) is used to define searches against persistent entities independent of the mechanism used to store those entities. As such, JPQL is "portable", and not constrained to any particular data store. The Java Persistence query language is an extension of the Enterprise JavaBeans query language, EJB QL, adding operations such as bulk deletes and updates, join operations, aggregates, projections, and subqueries. Furthermore, JPQL queries can be declared statically in metadata, or can be dynamically built in code.

JPQL Exemples

 TypedQuery<Country> query =em.createQuery("SELECT c FROM Country c", Country.class);
 List<Country> results = query.getResultList();
 SELECT c, l FROM Country c JOIN c.languages l
 WHERE c.population > :p AND l IN :languages
 SELECT c.currency, SUM(c.population)
 FROM Country c
 WHERE 'Europe' MEMBER OF c.continents
 GROUP BY c.currency
 HAVING COUNT(c) > 1

JPA Criteria API

The JPA Criteria API provides an alternative way for defining JPA queries, which is mainly useful for building dynamic queries whose exact structure is only known at runtime.

JPA criteria queries are defined by instantiation of Java objects that represent query elements.

A major advantage of using the criteria API is that errors can be detected earlier, during compilation rather than at runtime.

Building a dynamic query based on fields that a user fills at runtime in a form that contains many optional fields - is expected to be cleaner when using the JPA criteria API, because it eliminates the need for building the query using many string concatenation operations.

JPA Criteria API Exemples

 CriteriaBuilder cb = em.getCriteriaBuilder();
 CriteriaQuery<Country> q = cb.createQuery(Country.class);
 Root<Country> c = q.from(Country.class);
 q.select(c);
  CriteriaBuilder cb = em.getCriteriaBuilder();
  CriteriaQuery<Country> q = cb.createQuery(Country.class);
  Root<Country> c = q.from(Country.class);
  ParameterExpression<Integer> p = cb.parameter(Integer.class);
  q.select(c).where(cb.gt(c.get("population"), p));
Comments