Chapter 12. Jakarta Persistence
12.1. About Jakarta Persistence
The Jakarta Persistence is a Jakarta EE specification for accessing, persisting, and managing data between Java objects or classes and a relational database. The Jakarta Persistence specification recognizes the interest and the success of the transparent object or relational mapping paradigm. It standardizes the basic APIs and the metadata needed for any object or relational persistence mechanism.
Jakarta Persistence itself is just a specification, not a product; it cannot perform persistence or anything else by itself. Jakarta Persistence is just a set of interfaces, and requires an implementation.
12.2. Create a Simple JPA Application
Follow the procedure below to create a simple JPA application in Red Hat CodeReady Studio.
Procedure
Create a JPA project in Red Hat CodeReady Studio.
In Red Hat CodeReady Studio, click File
New Project. Find JPA in the list, expand it, and select JPA Project. You are presented with the following dialog. Figure 12.1. New JPA Project Dialog
- Enter a Project name.
Select a Target runtime. If no target runtime is available, follow these instructions to define a new server and runtime: Downloading, Installing, and Setting Up JBoss EAP from within the IDE in the Getting Started with CodeReady Studio Tools guide.
NoteIf you set the Target runtime to 7.4 or a later runtime version in Red Hat CodeReady Studio, your project is compatible with the Jakarta EE 8 specification.
- Under JPA version, ensure 2.1 is selected.
- Under Configuration, choose Basic JPA Configuration.
- Click Finish.
- If prompted, choose whether you wish to associate this type of project with the JPA perspective window.
Create and configure a new persistence settings file.
- Open an EJB 3.x project in Red Hat CodeReady Studio.
- Right click the project root directory in the Project Explorer panel.
-
Select New
Other…. - Select XML File from the XML folder and click Next.
-
Select the
ejbModule/META-INF/
folder as the parent directory. -
Name the file
persistence.xml
and click Next. - Select Create XML file from an XML schema file and click Next.
Select
http://java.sun.com/xml/ns/persistence/persistence_2.0.xsd
from the Select XML Catalog entry list and click Next.Figure 12.2. Persistence XML Schema
Click Finish to create the file. The
persistence.xml
has been created in theMETA-INF/
folder and is ready to be configured.Example: Persistence Settings File
<persistence xmlns="http://java.sun.com/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_2.xsd" version="2.2"> <persistence-unit name="example" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <mapping-file>ormap.xml</mapping-file> <jar-file>TestApp.jar</jar-file> <class>org.test.Test</class> <shared-cache-mode>NONE</shared-cache-mode> <validation-mode>CALLBACK</validation-mode> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit> </persistence>
12.3. Jakarta Persistence Entities
Once you have established the connection from your application to the database, you can start mapping the data in the database to Java objects. Java objects that are used to map against database tables are called entity objects.
Entities have relationships with other entities, which are expressed through object-relational metadata. The object-relational metadata can be specified either directly in the entity class file by using annotations, or in an XML descriptor file called persistence.xml
included with the application.
The high-level mapping of Java objects to the database is as follows:
- Java classes map to the database tables.
- Java instances map to the database rows.
- Java fields map to the database columns.
12.4. Persistence Context
The Jakarta Persistence persistence context contains the entities managed by the persistence provider. The persistence context acts like a first level transactional cache for interacting with the datasource. It manages the entity instances and their lifecycle. Loaded entities are placed into the persistence context before being returned to the application. Entity changes are also placed into the persistence context to be saved in the database when the transaction commits.
The lifetime of a container-managed persistence context can either be scoped to a transaction, which is referred to as a transaction-scoped persistence context, or have a lifetime scope that extends beyond that of a single transaction, which is referred to as an extended persistence context. The PersistenceContextType
property, which has the enum
datatype, is used to define the persistence context lifetime scope for container-managed entity managers. The persistence context lifetime scope is defined when the EntityManager
instance is created.
12.4.1. Transaction-Scoped Persistence Context
The transaction-scoped persistence context works with the active Jakarta Transactions transaction. When the transaction commits, the persistence context is flushed to the datasource; the entity objects are detached but might still be referenced by the application code. All the entity changes that are expected to be saved to the datasource must be made during a transaction. Entities that are read outside the transaction are detached when the EntityManager
invocation completes.
12.4.2. Extended Persistence Context
The extended persistence context spans multiple transactions and allows data modifications to be queued without an active Jakarta Transactions transaction. The container-managed extended persistence context can only be injected into a stateful session bean.
12.5. Jakarta Persistence EntityManager
Jakarta Persistence entity manager represents a connection to the persistence context. You can read from and write to the database defined by the persistence context using the entity manager.
Persistence context is provided through the Java annotation @PersistenceContext
in the javax.persistence
package. The entity manager is provided through the Java class javax.persistence.EntityManager
. In any managed bean, the EntityManager
instance can be injected as shown below:
Example: Entity Manager Injection
@Stateless public class UserBean { @PersistenceContext EntityManager entitymanager; ... }
12.5.1. Application-Managed EntityManager
Application-managed entity managers provide direct access to the underlying persistence provider, org.hibernate.jpa.HibernatePersistenceProvider
. The scope of the application-managed entity manager is from when the application creates it and lasts until the application closes it. You can use the @PersistenceUnit
annotation to inject a persistence unit into the javax.persistence.EntityManagerFactory
interface, which returns an application-managed entity manager.
Application-managed entity managers can be used when your application needs to access a persistence context that is not propagated with the Jakarta Transactions transaction across EntityManager
instances in a particular persistence unit. In this case, each EntityManager
instance creates a new, isolated persistence context. The EntityManager
instance and its associated PersistenceContext
is created and destroyed explicitly by your application. Application-managed entity managers can also be used when you cannot inject EntityManager
instances directly, because the EntityManager
instances are not thread-safe. EntityManagerFactory
instances are thread-safe.
Example: Application-Managed Entity Manager
@PersistenceUnit EntityManagerFactory emf; EntityManager em; @Resource UserTransaction utx; ... em = emf.createEntityManager(); try { utx.begin(); em.persist(SomeEntity); em.merge(AnotherEntity); em.remove(ThirdEntity); utx.commit(); } catch (Exception e) { utx.rollback(); }
12.5.2. Container-Managed EntityManager
Container-managed entity managers manage the underlying persistence provider for the application. They can use the transaction-scoped persistence contexts or the extended persistence contexts. The container-managed entity manager creates instances of the underlying persistence provider as needed. Every time a new underlying persistence provider org.hibernate.jpa.HibernatePersistenceProvider
instance is created, a new persistence context is also created.
12.6. Working with the EntityManager
When you have the persistence.xml
file located in the /META-INF
directory, the entity manager is loaded and has an active connection to the database. The EntityManager
property can be used to bind the entity manager to JNDI and to add, update, remove and query entities.
If you plan to use a security manager with Hibernate, be aware that Hibernate supports it only when EntityManagerFactory
is bootstrapped by the JBoss EAP server. It is not supported when the EntityManagerFactory
or SessionFactory
is bootstrapped by the application. See Java Security Manager in How to Configure Server Security for more information about security managers.
12.6.1. Binding the EntityManager to JNDI
By default, JBoss EAP does not bind the EntityManagerFactory
to JNDI. You can explicitly configure this in the persistence.xml
file of your application by setting the jboss.entity.manager.factory.jndi.name
property. The value of this property should be the JNDI name to which you want to bind the EntityManagerFactory
.
You can also bind a container-managed transaction-scoped entity manager to JNDI by using the jboss.entity.manager.jndi.name
property.
Example: Binding the EntityManager
and the EntityManagerFactory
to JNDI
<property name="jboss.entity.manager.jndi.name" value="java:/MyEntityManager"/> <property name="jboss.entity.manager.factory.jndi.name" value="java:/MyEntityManagerFactory"/>
Example: Storing an Entity using the EntityManager
public User createUser(User user) { entityManager.persist(user); return user; }
Example: Updating an Entity using the EntityManager
public void updateUser(User user) { entityManager.merge(user); }
Example: Removing an Entity using the EntityManager
public void deleteUser(String user) { User user = findUser(username); if (user != null) entityManager.remove(user); }
Example: Querying an Entity using the EntityManager
public User findUser(String username) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteria = builder.createQuery(User.class); Root<User> root = criteria.from(User.class); TypedQuery<User> query = entityManager .createQuery(criteria.select(root).where( builder.equal(root.<String> get("username"), username))); try { return query.getSingleResult(); } catch (NoResultException e) { return null; } }
12.7. Deploying the Persistence Unit
A persistence unit is a logical grouping that includes:
- Configuration information for an entity manager factory and its entity managers.
- Classes managed by the entity managers.
- Mapping metadata specifying the mapping of the classes to the database.
The persistence.xml
file contains persistence unit configuration, including the datasource name. The JAR file or the directory whose /META-INF/
directory contains the persistence.xml
file is termed as the root of the persistence unit.
In Jakarta EE environments, the root of the persistence unit must be one of the following:
- An EJB-JAR file
-
The
/WEB-INF/classes/
directory of a WAR file -
A JAR file in the
/WEB-INF/lib/
directory of a WAR file - A JAR file in the EAR library directory
- An application client JAR file
Example: Persistence Settings File
<persistence xmlns="http://java.sun.com/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_2.xsd" version="2.2"> <persistence-unit name="example" transaction-type="JTA"> <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider> <jta-data-source>java:jboss/datasources/ExampleDS</jta-data-source> <mapping-file>ormap.xml</mapping-file> <jar-file>TestApp.jar</jar-file> <class>org.test.Test</class> <shared-cache-mode>NONE</shared-cache-mode> <validation-mode>CALLBACK</validation-mode> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.hbm2ddl.auto" value="create-drop"/> </properties> </persistence-unit> </persistence>
12.8. Second-level Caches
12.8.1. About Second-level Caches
A second-level cache is a local data store that holds information persisted outside the application session. The cache is managed by the persistence provider, improving runtime by keeping the data separate from the application.
JBoss EAP supports caching for the following purposes:
- Web Session Clustering
- Stateful Session Bean Clustering
- SSO Clustering
- Hibernate Second-level Cache
- Jakarta Persistence Second-level Cache
Each cache container defines a repl
and a dist
cache. These caches should not be used directly by user applications.
12.8.1.1. Default Second-level Cache Provider
Infinispan is the default second-level cache provider for JBoss EAP. Infinispan is a distributed in-memory key/value data store with optional schema, available under the Apache License 2.0.
12.8.1.1.1. Configuring a Second-level Cache in the Persistence Unit
To ensure compatibility with future JBoss EAP releases, cache configuration should be customized using the Infinispan subsystem rather than persistence.xml
property overrides.
You can use the shared-cache-mode
element of the persistence unit to configure the second-level cache.
-
See Create a Simple Jakarta Persistence Application to create the
persistence.xml
file in Red Hat CodeReady Studio. Add the following to the
persistence.xml
file:<persistence-unit name="..."> (...) <!-- other configuration --> <shared-cache-mode>SHARED_CACHE_MODE</shared-cache-mode> <properties> <property name="hibernate.cache.use_second_level_cache" value="true" /> <property name="hibernate.cache.use_query_cache" value="true" /> </properties> </persistence-unit>
The
SHARED_CACHE_MODE
element can take the following values:-
ALL
: All entities should be considered cacheable. -
NONE
: No entities should be considered cacheable. -
ENABLE_SELECTIVE
: Only entities marked as cacheable should be considered cacheable. -
DISABLE_SELECTIVE
: All entities except the ones explicitly marked as not cacheable should be considered cacheable. -
UNSPECIFIED
: Behavior is not defined. Provider-specific defaults are applicable.
-
Example: Changing the properties of entity
and local-query
caches using persistence.xml
<persistence ... version="2.2"> <persistence-unit ...> ... <properties> <!-- Values below are not recommendations. Appropriate values should be determined based on system use/capacity. --> <!-- entity default overrides --> <property name="hibernate.cache.infinispan.entity.memory.size" value="5000"/> <property name="hibernate.cache.infinispan.entity.expiration.max_idle" value="300000"/> <!-- 5 minutes --> <property name="hibernate.cache.infinispan.entity.expiration.lifespan" value="1800000"/> <!-- 30 minutes --> <property name="hibernate.cache.infinispan.entity.expiration.wake_up_interval" value="300000"/> <!-- 5 minutes --> <!-- local-query default overrides --> <property name="hibernate.cache.infinispan.query.memory.size" value="5000"/> <property name="hibernate.cache.infinispan.query.expiration.max_idle" value="300000"/> <!-- 5 minutes --> <property name="hibernate.cache.infinispan.query.expiration.lifespan" value="1800000"/> <!-- 30 minutes --> <property name="hibernate.cache.infinispan.query.expiration.wake_up_interval" value="300000"/> <!-- 5 minutes --> </properties> </persistence-unit> </persistence>
Property | Description |
---|---|
| Denotes the object-memory size. |
| Denotes the maximum idle time (in milliseconds) a cache entry is maintained in the cache. |
| Denotes the maximum lifespan (in milliseconds) after which a cache entry is expired. Defaults to 60 seconds. Infinite lifespan may be specified using -1. |
| Denotes the interval (in milliseconds) between subsequent runs to purge expired entries from the cache. Expiration can be disabled using -1. |