Tackling RESOURCE_LOCAL Vs. JTA Under Java EE Umbrella and Payara Server
Let's tackle these important features under the Java EE umbrella.
Join the DZone community and get the full member experience.
Join For FreeLearning JPA is a road marked with a lot of questions. Some of the notorious questions include: "What is actually a persistence-unit?," "What is an extended persistence-context?," "Why is wrong to fetch read-only data as entities?," "Why should I care aboutRESOURCE_LOCAL
orJTA
?," and so on and forth.
This article targets people that are confused by the JPA transaction types,RESOURCE_LOCAL,
andJTA
. But, let's take it step by step.
What Is a Persistent-Unit?
Think to the persistent-unit as a box holding all the needed information for creating anEntityManagerFactory
instance. Among this information, we have details about the data source (JDBC URL, user, password, SQL dialect, etc), the list of entities that will be managed, and other specific properties. And, of course, we have the persistent-unit transaction type, which can beRESOURCE_LOCAL
orJTA
. We can specify all these details in an XML file calledpersistence.xml
or, at the API level, via an implementation of javax.persistence.spi.PersistenceUnitInfo
.
In both cases, we identify a persistent-unit by the name that we choose. We can have multiple persistent units and identify them by name.
<!-- persistence.xml -->
<persistence version="2.1" xmlns=...>
<persistence-unit name="MyPU" transaction-type="RESOURCE_LOCAL/JTA">
...
</persistence-unit>
</persistence>
// flavor of PersistenceUnitInfo implementation
import javax.persistence.spi.PersistenceUnitInfo;
...
public class PersistenceUnitInfoImpl implements PersistenceUnitInfo {
private final String persistenceUnitName;
private PersistenceUnitTransactionType transactionType =
PersistenceUnitTransactionType.JTA/RESOURCE_LOCAL;
...
}
What Is EntityManagerFactory?
As its name suggests, anEntityManagerFactory
is a factory capable to create on-demandEntityManager
instances.
Basically, we provide the needed information via the persistent-unit, and JPA can use this information to create anEntityManagerFactory
that exposes a method named,createEntityManager()
, which returns a new application-managedEntityManager
instance at each invocation.
We open an EntityManagerFactory
via injection (@PersistenceUnit
) or via Persistence.createEntityManagerFactory()
. We can check the status of anEntityManagerFactory
via theisOpen()
method, and we can close it via theclose()
method. If we close anEntityManagerFactory
, all its entity managers are considered to be in the closed state.
What Is EntityManager?
In order to understand what is anEntityManager,
let's talk about what's happening with the data extracted from the database.
Fetching data from the database results in a copy of that data in memory (usually referred to as the data snapshot). This zone of memory that holds the fetched data is known and referred to as the persistence-context or the first-level cache or simple the cache. This is a strong reason for NOT fetching data as entities if we don't plan to modify them. Read-only data will consume memory resources adding pointless performance penalties.
After fetching operation, the fetched data snapshot lives outside the database, in memory. We access/manage this data via entities (so, via Java objects), and for facilitating this context, JPA applies specific techniques that transform the fetched raw data into this manageable representation. This is another strong reason for NOT fetching data as entities if we don't plan to modify them. Read-only data should be transformed before storing, and this will consume CPU resources adding pointless performance penalties.
One single active persistence-context should be allocated to the currently active transaction. During the current transaction lifespan, we can manipulate the data stored in the persistence-context via the
EntityManager
methods (verbs). Actions, as finding objects, persisting them, removing objects from the database and so on, are specific to theEntityManager
. In a more simplistic day-to-day chat, there is nothing wrong to say that theEntityManager
instance is the persistence-context. As a rule of thumb, avoid using more than one persistence-context per transaction.
After we modify the data from the persistence-context, we want to propagate these modifications to the database. This action is known as flush, and it can be automatically or manually triggered multiple times during a transaction lifespan. We say that at the flush time we synchronize the persistence-context to the underlying database. There are different flushing strategies, but probably, the most used is
AUTO
(relying on the default flush strategy) andCOMMIT
(flushing occurs prior to transaction commit). Think to the flush action as the bunch of SQL statements needed to be sent to the database in order to propagate our modifications.
Once the current transaction is inactive/completed (by commit or rollback), all objects that were in the persistence-context are detached. Detaching all entities take place when the
EntityManager
is cleared via theclear()
method or closed via theclose()
method. Certain entities can be detached by calling a dedicated method nameddetach()
(orevict()
in Hibernate). This means that further modifications of these objects will not be reflected in the database. This is possible only by re-attaching the objects via themerge()
operation (andupdate()
in Hibernate) in the context of an active transaction. This is the default behavior of a transactional-scopedEntityManager
(known as Transactional Persistence Context). There is also theEntityManager
that span across multiple transactions (extended-scope) known as Extended Persistence Context.At detaching, objects are leaving the persistence-context and continue to live outside of it. This means that JPA will not manage them anymore, no more special treatment, they are just some usual Java objects. For example, calling
EntityManager.remove(detached_entity)
will throw a meaningful exception.
RESOURCE_LOCAL Vs. JTA
Quiz:
1. Who is gonna manage the transactions?
A. You (in the application) B. The application server
2 Who is gonna manage the persistence-context lifespan?
A. You (in the application) B. The application server
3. Transactions should span multiple persistent-units (databases) or systems (e.g., JMS, JCA, etc)?
A. No B. Yes
Well, if you answered "A" to all questions, then you needRESOURCE_LOCAL
. In this case, the transactions are local to the JPA persistence-unit.
But, if you answered at least one time "B," then you needJTA
. In other words, the transactions are managed by the application server's JTA implementation.
Now, let's see what efforts imply the usage ofRESOURCE_LOCAL
respectivelyJTA
.
Using RESOURCE_LOCAL
<persistence-unit transaction-type="RESOURCE_LOCAL">
Once you write this line in thepersistence xml
, you are responsible to manage theEntityManager
lifespan. Having this responsibility on your shoulders requires some skills and knowledge under your toolbelt:
Obtain an EntityManagerFactory
In order to create an EntityManager
, you need anEntityManagerFactory
in your application. You can achieve this in at least two ways: via thePersistence.createEntityManagerFactory()
method or via injection using the@PersistenceUnit
annotation:
// using injection via @PersistenceUnit
@PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf;
// using Persistence.createEntityManagerFactory() method
EntityManagerFactory emf = Persistence.createEntityManagerFactory("MyPU");
Do not confuse@PersistenceUnit
with@PersistenceContext
.
Obtain an EntityManager
Having anEntityManagerFactory
is good but not enough. Further, we need anEntityManager
instance. For this, we call thecreateEntityManager()
method like in the following snippet of code:
// create a brand new EntityManager instance
EntityManager em = emf.createEntityManager();
Notice that each call of thecreateEntityManager()
method will return a brand new application-managed EntityManager
; therefore, a brand new application-managed persistence-context. Is your responsibility to ensure that it is exactly oneEntityManager
instance for the current transaction (only one active persistence-context). Having two or more persistence-context in the same transaction may lead to exceptions and weird behaviors.
Define Transaction Boundaries
Further, each call of anEntityManager
action (e.g., find, persist, remove, etc) should be wrapped by an active transaction. Basically, theEntityManager
verbs should be demarcated by explicit calls of thebegin()
andcommit()
methods. We say that we use theEntityTransaction
API to demarcate the boundaries of a transaction. Betweenbegin()
and commit()
our transaction is active.
EntityTransaction tx = em.getTransaction();
tx.begin();
em.find(...);
em.persist(...);
...
tx.commit();
Rollback
Rollback a failed transaction is your responsibility as well. This means that you should intercept specific exceptions and explicitly call therollback()
method. This ensures that a failed transaction will lead to reverting the database in the state before the transaction begun. Don't conclude from here that read-only queries should not run in a transaction. This is not true! It is advisable to run the read-only query in a transaction as well.
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.find(...);
em.persist(...);
...
tx.commit();
} catch (RuntimeException/Exception e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
// eventually log or throw the exception
}
Clear and Close EntityManager
The last responsibility is pretty straightforward. Once the transaction was committed, we need to detach the entities from the persistence-context. For this, we can callclear()
orclose()
. By callingclear(),
the managed entities become detached, but theEntityManager
can be re-used in other transactions. This is a common technique in batching implementation. During batching, we keep on accumulating managed entities in the persistence-context, and this can lead to significant performance penalties and/or OOM errors. In order to avoid these issues, we must keep the persistence-context as slim as possible and this can be accomplished by periodically calling clear()
.
When theEntityManager
is no more useful, we must close it by calling theclose()
method. In this case, for the next transaction, we need to create a brand newEntityManager
.
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.find(...);
em.persist(...);
...
tx.commit();
} catch (RuntimeException/Exception e) {
if (tx != null && tx.isActive()) {
tx.rollback();
}
// eventually log or throw the exception
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
Using JTA
<persistence-unit transaction-type="JTA">
Once you write this line in persistence.xml
, you pass most of the responsibilities of managingEntityManager
to the container/application server. This should be an application server that has/support a transaction manager (e.g., Payara Server).
Obtain an EntityManager
In order to obtain anEntityManager
, we can use the@PersistenceContext
annotation, as follows:
@PersistenceContext(unitName = "MyPU")
private EntityManager em;
Do not confuse@PersistenceContext
with@PersistenceUnit
.
The injectedEntityManager
is provided by the container. This is a reference to the persistence-context associated with a JTA transaction.
We want exactly one persistence-context per transaction. Using a JTA unit with anEntityManager
created by the container, it will always guarantee this statement.
Once we inject anEntityManager
, we can simply call its methods. Transaction creation, transaction demarcation boundaries, rollback, flushing and clearing the EntityManager
at JTA transaction commit time are the container responsibilities.
So, in the case of classicJTA
, we don't need all the plumbing code needed in case of RESOURCE_LOCAL
. We just exploit theEntityManager
and allow the container to do the rest of the job for us.
Types of Persistence Contexts and Transactions
If we try to categorize persistence-context by its scope in a Java EE application, we obtain:
Transactional-Scoped and Extended Persistent Context
Transactional-scoped persistence-context is specific to stateless beans. Since each invocation of a stateless bean business method may use a different instance of that bean, a transaction must be completed at the end of the business method execution (each business method has its own transaction).
Extended persistence-context is allowed only in a stateful bean. Each invocation of a stateful bean business method will use the same instance; therefore, we can provide a state via an extended persistence-context that lives over multiple transactions. Basically, the persistence-context lives beyond the scope of a transaction.
In JTA, we can control if theEntityManager
is extended or transactional during theEntityManager
injection:
@PersistenceContext(type=javax.persistence.PersistenceContextType.EXTENDED)
EntityManager em;
If we try to categorize persistence-context and transactions depending on who is responsible to manage their lifespans in a JavaEE application, we obtain:
Container-Managed Persistence Context
Specific toJTA
transactions, by Container-Managed Persistence Context, we understand that the container is responsible to provide(inject) a ready-to-go persistence-context that it is aware of the currently active transaction. Moreover, it is container responsibility to flush and close the persistence-context.
@PersistenceContext(unitName = "MyPU")
private EntityManager em;
Application-Managed Persistence Context
Can be used inJTA
andRESOURCE_LOCAL
transactions. By Application-Managed Persistence Context, we understand that we are responsible to create and remove the persistence-context. Every application-managed persistence-context has the extended scope. This means that when the transaction is complete, theEntityManager
is not cleared or closed. Clear/close the EntityManager
is recommended in stateless beans where the extended scope is pointless.
@PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf;
...
EntityManager em = emf.createEntityManager();
...
em.clear()/em.close();
Container-Managed Transactions (CMT)
Specific toJTA
transactions, by Container-Managed Transactions, we understand that the container is responsible for the whole transaction lifespan (create, begin, commit, rollback). Optionally, we can use javax.ejb.TransactionAttribute
for defining the transaction context (defaults toREQUIRED
).
@Stateless
@TransactionAttribute(TransactionAttributeType.NEVER)
public class SomeBean {
...
}
Bean-Managed Transactions (BMT)
Specific toJTA
transactions, by Bean-Managed Transactions, we are responsible for starting, rolling back or committing the transaction by explicitly call begin()
,commit()
androllback()
methods. We’ll be using the JTA transactions level, so we won’t be usingjavax.persistence.EntityTransaction
. BMT must interact with the JTA transaction manager through thejavax.transaction.UserTransaction
interface.
@Resource
private UserTransaction userTransaction;
Application-Managed Transactions
Specific toRESOURCE_LOCAL
transactions, by Application-Managed Transactions, we are responsible for starting, rolling back or committing the transaction by explicitly callbegin()
, commit()
and rollback()
methods. We’ll be using theRESOURCE_LOCAL
transactions level, so we use thejavax.persistence.EntityTransaction
viaEntityManager.getTransaction()
method.
EntityTransaction tx = em.getTransaction();
Time for Applications
The applications developed in this article requires Payara Server.
Please follow this article, steps 1-4 in order to install and start a Payara Server instance and configure a MySQL data source in Payara.
We use the JPA implementation, EclipseLink.
Writing the persistence.xml Descriptor
Thepersistence.xml
descriptor is specific to Java EE applications and it contains all the details related to our persistent-units. Commonly, we have a single persistent-unit, but more are allowed as well. From the transaction-type perspective, the most common persistent-units are:
persistent-unit for
JTA
transaction-type
<?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="MyPU" transaction-type="JTA">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<jta-data-source>jdbc/players</jta-data-source>
<class>com.sample.model.Player</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
persistent-unit for
RESOURCE_LOCAL
transaction-type with data source configured on the application server
<?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="MyPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<non-jta-data-source>jdbc/players</non-jta-data-source>
<class>com.sample.model.Player</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
persistent-unit for
RESOURCE_LOCAL
transaction-type with data source configured via properties:
<?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="MyPU" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>com.sample.model.Player</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver"
value="com.mysql.cj.jdbc.Driver"/>
<property name="javax.persistence.jdbc.url"
value="jdbc:mysql://localhost:3306/db_players
?createDatabaseIfNotExist=true&useSSL=false"/>
<property name="javax.persistence.jdbc.user"
value="root"/>
<property name="javax.persistence.jdbc.password"
value="root"/>
<property name="javax.persistence.schema-generation.database.action"
value="drop-and-create"/>
</properties>
</persistence-unit>
</persistence>
Classic RESOURCE_LOCAL Application
A classicRESOURCE_LOCAL
application relies on application-managed persistence-context and transactions. Notice how we inject the EntityManagerFactory
, create and close theEntityManager
, create and demarcate the transaction boundaries, and rollback it in case of an exception.
@Path("player")
public class PlayerResource {
@PersistenceUnit(name="MyPU")
private EntityManagerFactory emf;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() {
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
try {
tx.begin();
em.persist(player);
tx.commit();
} catch (Exception e) { // or use explicit exception
if (tx != null && tx.isActive()) {
tx.rollback();
}
throw e;
} finally {
if(em != null && em.isOpen()) {
em.close();
}
}
return player;
}
}
The source code can be found on GitHub.
Classic JTA Application
A classic JTA application relies on container-managed persistence-context and transactions. So, the container will inject the JTAEntityManager
and will take care of the JTA transactions:
@Path("player")
@Stateless
public class PlayerResource {
@PersistenceContext(unitName = "MyPU")
private EntityManager em;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() {
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
em.persist(player);
return player;
}
}
The source code can be found on GitHub.
Application-Managed EntityManager and CMT
In this case, we manage theEntityManager
by creating (via the injected EntityManagerFactory
) and closing it. The container is responsible to manage the JTA transactions. This means that you can use the JTA transactions in your application-managedEntityManager
. TheEntityManager
is JTA aware and it will automatically join the container-managed JTA active transaction.
@Path("player")
@Stateless
public class PlayerResource {
@PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() {
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
EntityManager em = emf.createEntityManager();
em.persist(player);
em.close();
return player;
}
}
The source code can be found on GitHub.
Application-Managed EntityManager and BMT
In this case, we manage theEntityManager
by creating (via the injected EntityManagerFactory
) and closing it. We also inject theUserTransaction
and use it to begin, commit and rollback the JTA transaction. In this case, theEntityManager
will automatically join the active JTA transaction only if we create it inside an active JTA transaction. Creating it outside of the JTA transaction will not result in an exception, but then nothing will be committed.
@Path("player")
@TransactionManagement(TransactionManagementType.BEAN)
public class PlayerResource {
@PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf;
@Resource
private UserTransaction userTransaction;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() throws Exception { // or use explicit exception
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
EntityManager em = null;
try {
userTransaction.begin();
em = emf.createEntityManager();
em.persist(player);
userTransaction.commit();
} catch (Exception e) { // or use explicit exceptions
if (userTransaction != null) {
userTransaction.rollback();
}
throw e;
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
return player;
}
}
The source code can be found on GitHub.
Application-Managed EntityManager Join BMT
In this case, the application-managedEntityManager
is created before the JTA transaction starts. In order to join this transaction, we explicitly call thejoin()
method.
@Path("player")
@TransactionManagement(TransactionManagementType.BEAN)
public class PlayerResource {
@PersistenceUnit(unitName = "MyPU")
private EntityManagerFactory emf;
@Resource
private UserTransaction userTransaction;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() throws Exception { // or use explicit exception
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
EntityManager em = emf.createEntityManager();
try {
userTransaction.begin();
em.joinTransaction();
em.persist(player);
userTransaction.commit();
} catch (Exception e) { // or use explicit exceptions
if (userTransaction != null) {
userTransaction.rollback();
}
throw e;
} finally {
if (em != null && em.isOpen()) {
em.close();
}
}
return player;
}
}
The source code can be found on GitHub.
Container-Managed EntityManager and BMT
In this case, we allow the container to inject and manage theEntityManager
and we take care of starting, committing and rollback the JTA transaction. TheEntityManager
will automatically join the JTA active transaction.
@Path("player")
@TransactionManagement(TransactionManagementType.BEAN)
public class PlayerResource {
@PersistenceContext(unitName = "MyPU")
private EntityManager em;
@Resource
private UserTransaction userTransaction;
@GET
@Produces({MediaType.APPLICATION_JSON})
public Player generatePlayer() throws Exception { // or use explicit exceptions
Player player = new Player();
player.setName(...);
player.setCity(...);
player.setAge(...);
try {
userTransaction.begin();
em.persist(player);
userTransaction.commit();
} catch (Exception e) { // or use explicit exceptions
if (userTransaction != null) {
userTransaction.rollback();
}
throw e;
}
return player;
}
}
The source code can be found on GitHub.
Done! Notice that our examples are based on JAX-RS resources. As in the JAX-RS specifications, a resource can be a@Singleton
or@Stateless
EJB. If you need a@Stateful
EJB (e.g., for Extended Persistence Context), then rely on classic EJBs.
Opinions expressed by DZone contributors are their own.
Comments