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. Note 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. Note If 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 . Select the ejbModule/META-INF/ folder as the parent directory. Name the file persistence.xml and click . Select Create XML file from an XML schema file and click . Select http://java.sun.com/xml/ns/persistence/persistence_2.0.xsd from the Select XML Catalog entry list and click . Figure 12.2. Persistence XML Schema Click Finish to create the file. The persistence.xml has been created in the META-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. Important 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 Warning 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 Note 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> Table 12.1. Properties of entity and local-query caches Property Description memory.size Denotes the object-memory size. expiration.max_idle Denotes the maximum idle time (in milliseconds) a cache entry is maintained in the cache. expiration.lifespan Denotes the maximum lifespan (in milliseconds) after which a cache entry is expired. Defaults to 60 seconds. Infinite lifespan may be specified using -1. expiration.wake_up_interval Denotes the interval (in milliseconds) between subsequent runs to purge expired entries from the cache. Expiration can be disabled using -1.

Commands:
<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>
@Stateless public class UserBean { @PersistenceContext EntityManager entitymanager; }
@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(); }
<property name="jboss.entity.manager.jndi.name" value="java:/MyEntityManager"/> <property name="jboss.entity.manager.factory.jndi.name" value="java:/MyEntityManagerFactory"/>
public User createUser(User user) { entityManager.persist(user); return user; }
public void updateUser(User user) { entityManager.merge(user); }
public void deleteUser(String user) { User user = findUser(username); if (user != null) entityManager.remove(user); }
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; } }
<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>
<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>
<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>