© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
L. Jungmann et al.Pro Jakarta Persistence in Jakarta EE 10https://doi.org/10.1007/978-1-4842-7443-9_6

6. Entity Manager

Lukas Jungmann1  , Mike Keith2, Merrick Schincariol3 and Massimo Nardone4
(1)
Prague, Czech Republic
(2)
Ottawa, ON, Canada
(3)
Almonte, ON, Canada
(4)
HELSINKI, Finland
 

Entities do not persist themselves when they are created. Nor do they remove themselves from the database when they are garbage-collected. It is the logic of the application that must manipulate entities to manage their persistent lifecycle. Jakarta Persistence provides the EntityManager interface for this purpose in order to let applications manage and search for entities in the relational database.

At first, this might seem like a limitation of Jakarta Persistence. If the persistence runtime knows which objects are persistent, why should the application have to be involved in the process? Rest assured that this design is both deliberate and far more beneficial to the application than any transparent persistence solution. Persistence is a partnership between the application and persistence provider. Jakarta Persistence brings a level of control and flexibility that could not be achieved without the active participation of the application.

In Chapter 2 we introduced the EntityManager interface and described some of the basic operations that it provides for operating on entities. We extended that discussion in Chapter 3 to include an overview of the Jakarta EE environment and the types of services that impact persistence applications. Finally, in Chapters 4 and 5, we described object-relational mapping, the key to building entities out of objects. With that groundwork in place, we are ready to revisit entity managers, persistence contexts, and persistence units, and to begin a more in-depth discussion of these concepts.

Persistence Contexts

Let’s begin by reintroducing the core terms of Jakarta Persistence. A persistence unit is a named configuration of entity classes. A persistence context is a managed set of entity instances. Every persistence context is associated with a persistence unit, restricting the classes of the managed instances to the set defined by the persistence unit. Saying that an entity instance is managed means that it is contained within a persistence context and it can be acted upon by an entity manager. It is for this reason that we say that an entity manager manages a persistence context.

Understanding the persistence context is the key to understanding the entity manager. An entity’s inclusion or exclusion from a persistence context will determine the outcome of any persistent operations on it. If the persistence context participates in a transaction, the in-memory state of the managed entities will get synchronized to the database. Yet despite the important role that it plays, the persistence context is never actually visible to the application. It is always accessed indirectly through the entity manager and assumed to be there when we need it.

So far so good, but how does the persistence context get created and when does this occur? How does the entity manager figure in the equation? This is where it starts to get interesting.

Entity Managers

Up to this point, we have demonstrated only basic entity manager operations in both the Java SE and Jakarta EE environments. We have reached a point, however, where we can finally reveal the full range of entity manager configurations. Jakarta Persistence defines no fewer than three different types of entity managers, each of which has a different approach to persistence context management that is tailored to a different application need. As you will see, the persistence context is just one part of the puzzle.

Container-Managed Entity Managers

In the Jakarta EE environment, the most common way to acquire an entity manager is by using the @PersistenceContext annotation to inject one. An entity manager obtained in this way is called container-managed because the container manages the lifecycle of the entity manager, typically by proxying the one that it gets from the persistence provider. The application does not have to create it or close it. This is the style of entity manager we demonstrated in Chapter 3.

Container-managed entity managers come in two varieties. The style of a container-managed entity manager determines how it works with persistence contexts. The first and most common style is called transaction-scoped. This means that the persistence contexts managed by the entity manager are scoped by the active Jakarta Transactions transaction, ending when the transaction is complete. The second style is called extended. Extended entity managers work with a single persistence context that is tied to the lifecycle of a stateful session bean and are scoped to the life of that stateful session bean, potentially spanning multiple transactions.

Note that by default, an application-managed persistence context that is associated with a Jakarta Transactions entity manager and is created within the scope of an active transaction will be automatically joined to that transaction

Transaction-Scoped

All the entity manager examples that we have shown so far for the Jakarta EE environment have been transaction-scoped entity managers. A transaction-scoped entity manager is returned whenever the reference created by the @PersistenceContext annotation is resolved. As we mentioned in Chapter 3, a transaction-scoped entity manager is stateless, meaning that it can be safely stored on any Jakarta EE component. Because the container manages it for us, it is also basically maintenance-free.

Once again, let’s introduce a stateless session bean that uses a transaction-scoped entity manager. Listing 6-1 shows the bean class for a session bean that manages project information. The entity manager is injected into the em field using the @PersistenceContext annotation and is then used in the business methods of the bean.
@Stateless
public class ProjectService {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public void assignEmployeeToProject(int empId, int projectId) {
        Project project = em.find(Project.class, projectId);
        Employee employee = em.find(Employee.class, empId);
        project.getEmployees().add(employee);
        employee.getProjects().add(project);
    }
    // ...
}
Listing 6-1

The ProjectService Session Bean

We described the transaction-scoped entity manager as stateless. If that is the case, how can it work with a persistence context? The answer lies with the Jakarta Transactions transaction. All container-managed entity managers depend on Jakarta Transactions transactions because they can use the transaction as a way to track persistence contexts. Every time an operation is invoked on the entity manager, the container proxy for that entity manager checks to see whether a persistence context is associated with the container Jakarta Transactions transaction. If it finds one, the entity manager will use this persistence context. If it doesn’t find one, it creates a new persistence context and associates it with the transaction. When the transaction ends, the persistence context goes away.

Let’s walk through an example. Consider the assignEmployeeToProject() method from Listing 6-1. The first thing the method does is search for the Employee and Project instances using the find() operation. When the first find() method is invoked, the container checks for a transaction. By default, the container will ensure that a transaction is active whenever a session bean method starts, so the entity manager in this example will find one ready. It then checks for a persistence context. This is the first time any entity manager call has occurred, so there isn’t a persistence context yet. The entity manager creates a new one and uses it to find the project.

When the entity manager is used to search for the employee, it checks the transaction again and this time finds the one it created when searching for the project. It then reuses this persistence context to search for the employee. At this point, employee and project are both managed entity instances. The employee is then added to the project, updating both the employee and project entities. When the method call ends, the transaction is committed. Because the employee and project instances were managed, the persistence context can detect any state changes in them, and it updates the database during the commit. When the transaction is over, the persistence context goes away.

This process is repeated every time one or more entity manager operations are invoked within a transaction.

Extended

In order to describe the extended entity manager , we must first talk a little about stateful session beans. As you learned in Chapter 3, stateful session beans are designed to hold conversational state. Once acquired by a client, the same bean instance is used for the life of the conversation until the client invokes one of the methods marked @Remove on the bean. While the conversation is active, the business methods of the client can store and access information using the fields of the bean.

Let’s try using a stateful session bean to help manage a department. Our goal is to create a business object for a Department entity that provides business operations relating to that entity. Listing 6-2 shows our first attempt. The business method init() is called by the client to initialize the department ID. We then store this department ID on the bean instance, and the addEmployee() method uses it to find the department and make the necessary changes. From the perspective of the client, they only have to set the department ID once, and then subsequent operations always refer to the same department.
@Stateful
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    int deptId;
    public void init(int deptId) {
        this.deptId = deptId;
    }
    public void setName(String name) {
        Department dept = em.find(Department.class, deptId);
        dept.setName(name);
    }
    public void addEmployee(int empId) {
        Department dept = em.find(Department.class, deptId);
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
    }
    // ...
    @Remove
    public void finished() {
    }
}
Listing 6-2

First Attempt at Department Manager Bean

The first thing that should stand out when looking at this bean is that it seems unnecessary to have to search for the department every time. After all, we have the department ID, so why not just store the Department entity instance as well? Listing 6-3 revises our first attempt by searching for the department once during the init() method and then reusing the entity instance for each business method.
@Stateful
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    Department dept;
    public void init(int deptId) {
        dept = em.find(Department.class, deptId);
    }
    public void setName(String name) {
        dept.setName(name);
    }
    public void addEmployee(int empId) {
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
    }
    // ...
    @Remove
    public void finished() {
    }
}
Listing 6-3

Second Attempt at Department Manager Bean

This version looks better suited to the capabilities of a stateful session bean. It is certainly more natural to reuse the Department entity instance instead of searching for it each time. But there is a problem. The entity manager in Listing 6-3 is transaction-scoped. Assuming there is no active transaction from the client, every method on the bean will start and commit a new transaction because the default transaction attribute for each method is REQUIRED. Because there is a new transaction for each method, the entity manager will use a different persistence context each time.

Even though the Department instance still exists, the persistence context that used to manage it went away when the transaction associated with the init() call ended. We refer to the Department entity in this case as being detached from a persistence context. The instance is still around and can be used, but any changes to its state will be ignored. For example, invoking setName() will change the name in the entity instance, but the changes will never be reflected in the database.

This is the situation that the extended entity manager is designed to solve. Designed specifically for stateful session beans, it prevents entities from becoming detached when transactions end. Before we go too much further, let’s introduce our third and final attempt at a department manager bean. Listing 6-4 shows our previous example updated to use an extended persistence context.
@Stateful
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService",
                        type=PersistenceContextType.EXTENDED)
    EntityManager em;
    Department dept;
    public void init(int deptId) {
        dept = em.find(Department.class, deptId);
    }
    public void setName(String name) {
        dept.setName(name);
    }
    public void addEmployee(int empId) {
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
    }
    // ...
    @Remove
    public void finished() {
    }
}
Listing 6-4

Using an Extended Entity Manager

As you can see, we changed only one line. The @PersistenceContext annotation that we introduced in Chapter 3 has a special type attribute that can be set to either TRANSACTION or EXTENDED. These constants are defined by the PersistenceContextType enumerated type. TRANSACTION is the default and corresponds to the transaction-scoped entity managers we have been using up to now. EXTENDED means that an extended entity manager should be used.

With this change made, the department manager bean now works as expected. Extended entity managers create a persistence context when a stateful session bean instance is created that lasts until the bean is removed. Unlike the persistence context of a transaction-scoped entity manager, which begins when the transaction begins and lasts until the end of a transaction, the persistence context of an extended entity manager will last for the entire length of the conversation. Because the Department entity is still managed by the same persistence context, whenever it is used in a transaction, any changes will be automatically written to the database.

The extended persistence context allows stateful session beans to be written in a way that is more suited to their capabilities. Later we discuss special limitations on the transaction management of extended entity managers, but by and large they are well suited to the type of example we have shown here.

Application-Managed Entity Managers

In Chapter 2 we introduced Jakarta Persistence with an example written using Java SE. The entity manager in that example, and any entity manager that is created from the createEntityManager() call of an EntityManagerFactory instance, is what we call an application-managed entity manager. This name comes from the fact that the application, rather than the container, manages the lifecycle of the entity manager. Note that all open entity managers, whether container-managed or application-managed, are associated with an EntityManagerFactory instance. The factory used to create the entity manager can be accessed from the getEntityManagerFactory() call on the EntityManager interface.

Although we expect the majority of applications to be written using container-managed entity managers, application-managed entity managers still have a role to play. They are the only entity manager type available in Java SE, and as you will see, they can be used in Jakarta EE as well.

Creating an application-managed entity manager is simple enough. All you need is an EntityManagerFactory to create the instance. What separates Java SE and Jakarta EE for application-managed entity managers is not how you create the entity manager but how you get the factory. Listing 6-5 demonstrates use of the Persistence class to bootstrap an EntityManagerFactory instance that is then used to create an entity manager.
public class EmployeeClient {
    public static void main(String[] args) {
        EntityManagerFactory emf =
            Persistence.createEntityManagerFactory("EmployeeService");
        EntityManager em = emf.createEntityManager();
        List<Employee> emps = em.createQuery("SELECT e FROM Employee e")
                                .getResultList();
        for (Employee e : emps) {
            System.out.println(e.getId() + ", " + e.getName());
        }
        em.close();
        emf.close();
    }
}
Listing 6-5

Application-Managed Entity Managers in Java SE

The Persistence class offers two variations of the same createEntityManager() method that can be used to create an EntityManagerFactory instance for a given persistence unit name. The first, specifying only the persistence unit name, returns the factory created with the default properties defined in the persistence.xml file. The second form of the method call allows a map of properties to be passed in, adding to, or overriding the properties specified in persistence.xml. This form is useful when required JDBC properties might not be known until the application is started, perhaps with information provided as command-line parameters. The set of active properties for an entity manager can be determined via the getProperties() method on the EntityManager interface. We discuss persistence unit properties in Chapter 14.

The best way to create an application-managed entity manager in Jakarta EE is to use the @PersistenceUnit annotation to declare a reference to the EntityManagerFactory for a persistence unit. Once acquired, the factory can be used to create an entity manager, which can be used just as it would in Java SE. Listing 6-6 demonstrates injection of an EntityManagerFactory into a servlet and its use to create a short-lived entity manager in order to verify a user ID.
public class LoginServlet extends HttpServlet {
    @PersistenceUnit(unitName="EmployeeService")
    EntityManagerFactory emf;
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response) {
        String userId = request.getParameter("user");
        // check valid user
        EntityManager em = emf.createEntityManager();
        try {
            User user = em.find(User.class, userId);
            if (user == null) {
                // return error page
                // ...
            }
        } finally {
            em.close();
        }
        // ...
    }
}
Listing 6-6

Application-Managed Entity Managers in Jakarta EE

One thing common to both of these examples is that the entity manager is explicitly closed with the close() call when it is no longer needed. This is one of the lifecycle requirements of an entity manager that must be performed manually in the case of application-managed entity managers; it is normally taken care of automatically by container-managed entity managers. Likewise, the EntityManagerFactory instance must also be closed, but only in the Java SE application. In Jakarta EE, the container closes the factory automatically, so no extra steps are required.

In Jakarta EE 10, both EntityManager and EntityManagerFactory already extend AutoCloseable interface allowing them to be used in try-with-resources statement. While it can be tempting to always use this statement instead of an ordinary try statement, which is usually more verbose, it is important to understand the difference in handling exceptions and the order of execution of possible catch and finally blocks in these two statements. In an ordinary try statement, catch and finally blocks run as they are declared in the code. This allows performing additional steps before closing the EntityManager or EntityManagerFactory, for example, performing transaction rollback before closing the EntityManager. In the try-with-resources statement, at the time the execution reaches the catch or finally block, resources are already closed.

In terms of the persistence context, the application-managed entity manager is similar to an extended container-managed entity manager. When an application-managed entity manager is created, it creates its own private persistence context that lasts until the entity manager is closed. This means that any entities managed by the entity manager will remain that way, independent of any transactions.

The role of the application-managed entity manager in Jakarta EE is somewhat specialized. If resource-local transactions are required for an operation, an application-managed entity manager is the only type of entity manager that can be configured with that transaction type within the server. As we describe in the next section, the transaction requirements of an extended entity manager can make them difficult to deal with in some situations. Application-managed entity managers can be safely used on stateful session beans to accomplish similar goals.

Transaction Management

Developing a persistence application is as much about transaction management as it is about object-relational mapping. Transactions define when new, changed, or removed entities are synchronized to the database. Understanding how persistence contexts interact with transactions is a fundamental part of working with Jakarta Persistence.

Note that we said persistence contexts, not entity managers. There are several different entity manager types, but all use a persistence context internally. The entity manager type determines the lifetime of a persistence context, but all persistence contexts behave the same way when they are associated with a transaction.

There are two transaction-management types supported by Jakarta Persistence. The first is resource-local transactions, which are the native transactions of the JDBC drivers that are referenced by a persistence unit. The second transaction-management type is Jakarta Transactions transactions, which are the transactions of the Jakarta EE server, supporting multiple participating resources, transaction lifecycle management, and distributed XA transactions.

Container-managed entity managers always use Jakarta Transactions transactions, while application-managed entity managers can use either type. Because Jakarta Transactions are typically not available in Java SE applications, the provider needs to support only resource-local transactions in that environment. The default and preferred transaction type for Jakarta EE applications are Jakarta Transactions. As we describe in the next section, propagating persistence contexts with Jakarta Transactions transactions is a major benefit to enterprise persistence applications.

The transaction type is defined for a persistence unit and is configured using the persistence.xml file. We discuss this setting and how to apply it in Chapter 14.

Jakarta Transactions Transaction Management

In order to talk about Jakarta Transactions transactions, we must first discuss the difference between transaction synchronization, transaction association, and transaction propagation. Transaction synchronization is the process by which a persistence context is registered with a transaction so that the persistence context can be notified when a transaction commits. The provider uses this notification to ensure that a given persistence context is correctly flushed to the database. Transaction association is the act of binding a persistence context to a transaction. You can also think of this as the active persistence context within the scope of that transaction. Transaction propagation is the process of sharing a persistence context between multiple container-managed entity managers in a single transaction.

There can be only one persistence context associated with and propagated across a Jakarta Transactions transaction. All container-managed entity managers in the same transaction must share the same propagated persistence context.

Transaction-Scoped Persistence Contexts

As the name suggests, a transaction-scoped persistence context is tied to the lifecycle of the transaction. It is created by the container during a transaction and will be closed when the transaction completes. Transaction-scoped entity managers are responsible for creating transaction-scoped persistence contexts automatically when needed. We say only when needed because transaction-scoped persistence context creation is lazy. An entity manager will create a persistence context only when a method is invoked on the entity manager and when there is no persistence context available.

When a method is invoked on the transaction-scoped entity manager, it must first see whether there is a propagated persistence context. If one exists, the entity manager uses this persistence context to carry out the operation. If one does not exist, the entity manager requests a new persistence context from the persistence provider and then marks this new persistence context as the propagated persistence context for the transaction before carrying out the method call. All subsequent transaction-scoped entity manager operations, in this component or any other, will thereafter use this newly created persistence context. This behavior works independently of whether container-managed or bean-managed transaction demarcation has been used.

Propagation of the persistence context simplifies the building of enterprise applications. When an entity is updated by a component inside of a transaction, any subsequent references to the same entity will always correspond to the correct instance, no matter what component obtains the entity reference. Propagating the persistence context gives developers the freedom to build loosely coupled applications, knowing that they will always get the right data even though they are not sharing the same entity manager instance.

To demonstrate propagation of a transaction-scoped persistence context, we introduce an audit service bean that stores information about a successfully completed transaction. Listing 6-7 shows the complete bean implementation. The logTransaction() method ensures that an employee ID is valid by attempting to find the employee using the entity manager.
@Stateless
public class AuditService {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    public void logTransaction(int empId, String action) {
        // verify employee number is valid
        if (em.find(Employee.class, empId) == null) {
            throw new IllegalArgumentException("Unknown employee id");
        }
        LogRecord lr = new LogRecord(empId, action);
        em.persist(lr);
    }
}
Listing 6-7

AuditService Session Bean

Now consider the fragment from the EmployeeService session bean example shown in Listing 6-8. After an employee is created, the logTransaction() method of the AuditService session bean is invoked to record the “created employee” event.
@Stateless
public class EmployeeService {
    @PersistenceContext(unitName="EmployeeService")
    EntityManager em;
    @EJB AuditService audit;
    public void createEmployee(Employee emp) {
        em.persist(emp);
        audit.logTransaction(emp.getId(), "created employee");
    }
    // ...
}
Listing 6-8

Logging EmployeeService Transactions

Even though the newly created Employee is not yet in the database, the audit bean can find the entity and verify that it exists. This works because the two beans are actually sharing the same persistence context. The transaction attribute of the createEmployee() method is REQUIRED by default because no attribute has been explicitly set. The container will guarantee that a transaction is started before the method is invoked. When persist()is called on the entity manager, the container checks to see whether a persistence context is already associated with the transaction. Let’s assume in this case that this was the first entity manager operation in the transaction, so the container creates a new persistence context and marks it as the propagated one.

When the logTransaction() method starts, it issues a find() call on the entity manager from the AuditService. We are guaranteed to be in a transaction because the transaction attribute is also REQUIRED, and the container-managed transaction from createEmployee() has been extended to this method by the container. When the find() method is invoked, the container again checks for an active persistence context. It finds the one created in the createEmployee() method and uses that persistence context to search for the entity. Because the newly created Employee instance is managed by this persistence context, it is returned successfully.

Now consider the case where logTransaction() has been declared with the REQUIRES_NEW transaction attribute instead of the default REQUIRED. Before the logTransaction() method call starts, the container will suspend the transaction inherited from createEmployee() and start a new transaction. When the find() method is invoked on the entity manager, it will check the current transaction for an active persistence context only to determine that one does not exist. A new persistence context will be created starting with the find() call, and this persistence context will be the active persistence context for the remainder of the logTransaction() call. Because the transaction started in createEmployee() has not yet committed, the newly created Employee instance is not in the database and therefore is not visible to this new persistence context. The find() method will return null, and the logTransaction() method will throw an exception as a result.

The rule of thumb for persistence context propagation is that the persistence context propagates as the Jakarta Transactions transaction propagates. Therefore, it is important to understand not only when transactions begin and end but also when a business method expects to inherit the transaction context from another method and when doing so would be incorrect. Having a clear plan for transaction management in your application is key to getting the most out of persistence context propagation.

Extended Persistence Contexts

The lifecycle of an extended persistence context is tied to the stateful session bean to which it is bound. Unlike a transaction-scoped entity manager that creates a new persistence context for each transaction, the extended entity manager of a stateful session bean always uses the same persistence context. The stateful session bean is associated with a single extended persistence context that is created when the bean instance is created and closed when the bean instance is removed. This has implications for both the association and propagation characteristics of the extended persistence context.

Transaction association for extended persistence contexts is eager. In the case of container-managed transactions, as soon as a method call starts on the bean, the container automatically associates the persistence context with the transaction. Likewise in the case of bean-managed transactions, as soon as UserTransaction.begin() is invoked within a bean method, the container intercepts the call and performs the same association.

Because a transaction-scoped entity manager will use an existing persistence context associated with the transaction before it will create a new persistence context, it is possible to share an extended persistence context with other transaction-scoped entity managers. As long as the extended persistence context is propagated before any transaction-scoped entity managers are accessed, the same extended persistence context will be shared by all components.

Similar to the auditing EmployeeService bean demonstrated in Listing 6-8, consider the same change made to a DepartmentManager stateful session bean to audit when an employee is added to a department. Listing 6-9 shows this example.
@Stateful
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService",
                        type=PersistenceContextType.EXTENDED)
    EntityManager em;
    Department dept;
    @EJB AuditService audit;
    public void init(int deptId) {
        dept = em.find(Department.class, deptId);
    }
    public void addEmployee(int empId) {
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
        audit.logTransaction(emp.getId(),
                             "added to department " + dept.getName());
    }
    // ...
}
Listing 6-9

Logging Department Changes

The addEmployee() method has a default transaction attribute of REQUIRED. Because the container eagerly associates extended persistence contexts, the extended persistence context stored on the session bean will be immediately associated with the transaction when the method call starts. This will cause the relationship between the managed Department and Employee entities to be persisted to the database when the transaction commits. It also means that the extended persistence context will now be shared by other transaction-scoped persistence contexts used in methods called from addEmployee().

The logTransaction() method in this example will inherit the transaction context from addEmployee() because its transaction attribute is the default REQUIRED, and a transaction is active during the call to addEmployee(). When the find() method is invoked, the transaction-scoped entity manager checks for an active persistence context and will find the extended persistence context from the DepartmentManager. It will then use this persistence context to execute the operation. All the managed entities from the extended persistence context become visible to the transaction-scoped entity manager.

Persistence Context Collision
We said earlier that only one persistence context could be propagated with a Jakarta Transactions transaction. We also said that the extended persistence context would always try to make itself the active persistence context. This can lead to situations in which the two persistence contexts collide with each other. Consider, for example, that a stateless session bean with a transaction-scoped entity manager creates a new persistence context and then invokes a method on a stateful session bean with an extended persistence context. During the eager association of the extended persistence context, the container will check to see whether there is already an active persistence context. If there is, it must be the same as the extended persistence context that it is trying to associate, or an exception will be thrown. In this example, the stateful session bean will find the transaction-scoped persistence context created by the stateless session bean, and the call into the stateful session bean method will fail. There can be only one active persistence context for a transaction. Figure 6-1 illustrates this case.
Figure 6-1

Persistence context collision

While extended persistence context propagation is useful if a stateful session bean with an extended persistence context is the first Enterprise Bean to be invoked in a call chain, it limits the situations in which other components can call into the stateful session bean if they are also using entity managers. This might or might not be common depending on your application architecture, but it is something to keep in mind when planning dependencies between components.

One way to work around this problem is to change the default transaction attribute for the stateful session bean that uses the extended persistence context. If the default transaction attribute is REQUIRES_NEW, any active transaction will be suspended before the stateful session bean method starts, allowing it to associate its extended persistence context with the new transaction. This is a good strategy if the stateful session bean calls in to other stateless session beans and needs to propagate the persistence context. Note that excessive use of the REQUIRES_NEW transaction attribute can lead to application performance problems because many more transactions than normal will be created, and active transactions will be suspended and resumed.

If the stateful session bean is largely self-contained, that is, it does not call other session beans and does not need its persistence context propagated, a default transaction attribute type of NOT_SUPPORTED can be worth considering. In this case, any active transaction will be suspended before the stateful session bean method starts, but no new transaction will be started. If there are some methods that need to write data to the database, those methods can be overridden to use the REQUIRES_NEW transaction attribute.

Listing 6-10 repeats the DepartmentManager bean, this time with some additional getter methods and customized transaction attributes. We have set the default transaction attribute to REQUIRES_NEW to force a new transaction by default when a business method is invoked. For the getName() method , we don’t need a new transaction because no changes are being made, so it has been set to NOT_SUPPORTED. This will suspend the current transaction, but won’t result in a new transaction being created. With these changes, the DepartmentManager bean can be accessed in any situation, even if there is already an active persistence context.
@Stateful
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService",
                        type=PersistenceContextType.EXTENDED)
    EntityManager em;
    Department dept;
    @EJB AuditService audit;
    public void init(int deptId) {
        dept = em.find(Department.class, deptId);
    }
    @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
    public String getName() { return dept.getName(); }
    public void setName(String name) { dept.setName(name); }
    public void addEmployee(int empId) {
        Employee emp = em.find(empId, Employee.class);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
        audit.logTransaction(emp.getId(),
                             "added to department " + dept.getName());
    }
    // ...
}
Listing 6-10

Customizing Transaction Attributes to Avoid Collision

Finally, one last option to consider is using an application-managed entity manager instead of an extended entity manager. If there is no need to propagate the persistence context, the extended entity manager is not adding a lot of value over an application-managed entity manager. The stateful session bean can safely create an application-managed entity manager, store it on the bean instance, and use it for persistence operations without having to worry about whether an active transaction already has a propagated persistence context. An example of this technique is demonstrated later in the “Application-Managed Persistence Contexts” section.

Persistence Context Inheritance

The restriction of only one stateful session bean with an extended persistence context being able to participate in a Jakarta Transactions transaction can cause difficulties in some situations. For example, the pattern we followed earlier in this chapter for the extended persistence context was to encapsulate the behavior of an entity behind a stateful session façade. In our example, clients worked with a DepartmentManager session bean instead of the actual Department entity instance. Because a department has a manager, it makes sense to extend this façade to the Employee entity as well.

Listing 6-11 shows changes to the DepartmentManager bean so that it returns an EmployeeManager bean from the getManager() method in order to represent the manager of the department. The EmployeeManager bean is injected and then initialized during the invocation of the init() method.
@Stateful
public class DepartmentManager {
    @PersistenceContext(unitName="EmployeeService",
                        type=PersistenceContextType.EXTENDED)
    EntityManager em;
    Department dept;
    @EJB EmployeeManager manager;
    public void init(int deptId) {
        dept = em.find(Department.class, deptId);
        manager.init();
    }
    public EmployeeManager getManager() {
        return manager;
    }
    // ...
}
Listing 6-11

Creating and Returning a Stateful Session Bean

Should the init() method succeed or fail? So far based on what we have described, it looks like it should fail. When init() is invoked on the DepartmentManager bean, its extended persistence context will be propagated with the transaction. In the subsequent call to init() on the EmployeeManager bean, it will attempt to associate its own extended persistence context with the transaction, causing a collision between the two.

Perhaps surprisingly, this example actually works. When a stateful session bean with an extended persistence context creates another stateful session bean that also uses an extended persistence context, the child will inherit the parent’s persistence context. The EmployeeManager bean inherits the persistence context from the DepartmentManager bean when it is injected into the DepartmentManager instance. The two beans can now be used together within the same transaction .

Application-Managed Persistence Contexts

Like container-managed persistence contexts, application-managed persistence contexts can be synchronized with Jakarta Transactions transactions. Synchronizing the persistence context with the transaction means that a flush will occur if the transaction commits, but the persistence context will not be considered associated by any container-managed entity managers. There is no limit to the number of application-managed persistence contexts that can be synchronized with a transaction, but only one container-managed persistence context will ever be associated. This is one of the most important differences between application-managed and container-managed entity managers.

An application-managed entity manager participates in a Jakarta Transactions transaction in one of two ways. If the persistence context is created inside the transaction, the persistence provider will automatically synchronize the persistence context with the transaction. If the persistence context was created earlier (outside of a transaction or in a transaction that has since ended), the persistence context can be manually synchronized with the transaction by calling joinTransaction() on the EntityManager interface. Once synchronized, the persistence context will automatically be flushed when the transaction commits.

Listing 6-12 shows a variation of the DepartmentManager from Listing 6-11 that uses an application-managed entity manager instead of an extended entity manager.
@Stateful
public class DepartmentManager {
    @PersistenceUnit(unitName="EmployeeService")
    EntityManagerFactory emf;
    EntityManager em;
    Department dept;
    public void init(int deptId) {
        em = emf.createEntityManager();
        dept = em.find(Department.class, deptId);
    }
    public String getName() {
        return dept.getName();
    }
    public void addEmployee(int empId) {
        em.joinTransaction();
        Employee emp = em.find(Employee.class, empId);
        dept.getEmployees().add(emp);
        emp.setDepartment(dept);
    }
    // ...
    @Remove
    public void finished() {
        em.close();
    }
}
Listing 6-12

Using Application-Managed Entity Managers with Jakarta Transactions

Instead of injecting an entity manager, we are injecting an entity manager factory. Prior to searching for the entity, we manually create a new application-managed entity manager using the factory. Because the container does not manage its lifecycle, we have to close it later when the bean is removed during the call to finished(). Like the container-managed extended persistence context, the Department entity remains managed after the call to init(). When addEmployee() is called, there is the extra step of calling joinTransaction() to notify the persistence context that it should synchronize itself with the current Jakarta Transactions transaction. Without this call, the changes to the department would not be flushed to the database when the transaction commits.

Because application-managed entity managers do not propagate, the only way to share managed entities with other components is to share the EntityManager instance. This can be achieved by passing the entity manager around as an argument to local methods or by storing the entity manager in a common place such as an HTTP session or singleton bean. Listing 6-13 demonstrates a servlet creating an application-managed entity manager and using it to instantiate the EmployeeService class we defined in Chapter 2. In these cases, care must be taken to ensure that access to the entity manager is done in a thread-safe manner. While EntityManagerFactory instances are thread-safe, EntityManager instances are not. Also, application code must not call joinTransaction() on the same entity manager in multiple concurrent transactions.
public class EmployeeServlet extends HttpServlet {
    @PersistenceUnit(unitName="EmployeeService")
    EntityManagerFactory emf;
    @Resource UserTransaction tx;
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
        // ...
        int id = Integer.parseInt(request.getParameter("id"));
        String name = request.getParameter("name");
        long salary = Long.parseLong(request.getParameter("salary"));
        tx.begin();
        EntityManager em = emf.createEntityManager();
        try {
            EmployeeService service = new EmployeeService(em);
            service.createEmployee(id, name, salary);
        } finally {
            em.close();
        }
        tx.commit();
        // ...
    }
}
Listing 6-13

Sharing an Application-Managed Entity Manager

Listing 6-13 demonstrates an additional characteristic of the application-managed entity manager in the presence of transactions. If the persistence context becomes synchronized with a transaction, changes will still be written to the database when the transaction commits, even if the entity manager is closed. This allows entity managers to be closed at the point where they are created, removing the need to worry about closing them after the transaction ends. Note that closing an application-managed entity manager still prevents any further use of the entity manager. It is only the persistence context that continues until the transaction has completed.

There is a danger in mixing multiple persistence contexts in the same Jakarta Transactions transaction. This occurs when multiple application-managed persistence contexts become synchronized with the transaction or when application-managed persistence contexts become mixed with container-managed persistence contexts. When the transaction commits, each persistence context will receive notification from the transaction manager that changes should be written to the database. This will cause each persistence context to be flushed.

What happens if an entity with the same primary key is used in more than one persistence context? Which version of the entity gets stored? The unfortunate answer is that there is no way to know for sure. The container does not guarantee any ordering when notifying persistence contexts of transaction completion. As a result, it is critical for data integrity that entities never be used by more than one persistence context in the same transaction. When designing your application, we recommend picking a single persistence context strategy (container-managed or application-managed) and sticking to that strategy consistently.

Unsynchronized Persistence Contexts

Under normal circumstances a Jakarta Transactions entity manager is synchronized with the Jakarta Transactions transaction, and its managed entity changes will be saved when the transaction commits. However, an exception to this rule was illustrated in Listing 6-12 when an application-managed entity manager that was created in a previous transaction needed to be explicitly joined to a subsequent transaction using the joinTransaction() call in order to cause its managed entity changes to be transactionally committed. It turns out that there is another persistence context option that exhibits a similar behavior. An entity manager can be explicitly specified to have an unsynchronized persistence context, requiring it to manually join any Jakarta Transactions transaction it wants to participate in.

Before we describe how to configure and use such a beast, we should first provide some motivation for its existence in the first place. Why, among all the varieties of entity managers and accompanying persistence contexts, is there yet another degree of parameterization? The answer lies in the proverbial “conversational” use case, characterized by the following scenario.

An application wants to perform a number of persistence operations over what is possibly an extended period of time, but does not want to maintain a single transaction for the duration. This group of operations can be called a conversation since it involves multiple interactions between the client and the server. Due to container-managed transaction demarcation, multiple transactions may have been started and completed over the course of the conversation, but the persistence operations should not be enlisted in the transactions since they should not be persisted until the conversation status has been determined. At some point the conversation comes to an end, and the application wants to either commit or roll back all of the operations as a group. At this point all of the changes contained in the persistence context need to be transactionally written out to the data store, or discarded.

This scenario could almost be satisfied by an application-managed Jakarta Transactions entity manager except for two things. The first is that application-managed entity managers are automatically synchronized with the transaction if they are created when a transaction is active. While this could be worked around simply by ensuring that the entity manager is always created outside the scope of a transaction, the appearance of an ostensibly synchronized Jakarta Transactions entity manager, that because of a technicality does not happen to be synchronized, is not a very clean solution to the conversation use case. Add to that the second point, that it would be really nice for the programming model if the entity manager could be injected into components, or in other words be a container-managed entity manager. The most appropriate solution was just to allow entity managers the option of having a persistence context that is only synchronized with the transaction if it is explicitly joined.

To obtain a container-managed entity manager with an unsynchronized persistence context, a value of UNSYNCHRONIZED, a SynchronizationType enum constant, can be supplied in the synchronization element of the @PersistenceContext annotation:
@PersistenceContext(unitName="EmployeeService",
                    synchronization=UNSYNCHRONIZED)
EntityManager em;
An application-managed entity manager can be programmatically created with an unsynchronized persistence context by passing the same UNSYNCHRONIZED value to the overloaded EntityManagerFactory createEntityManager() method:
@PersistenceUnit(unitName="EmployeeService")
EntityManagerFactory emf;
...
EntityManager em = emf.createEntityManager(UNSYNCHRONIZED);

Neither of these persistence contexts will be synchronized with the Jakarta Transactions transaction unless they are explicitly joined using joinTransaction() . Once joined to the transaction, they will remain joined until that transaction completes, but being joined to one transaction will not imply being joined to subsequent transactions. An unsynchronized persistence context must be explicitly joined to each and any transaction it wants to be enlisted in.

Given the conversation use case, and the possibility of multiple transactions occurring along the way, it clearly makes the most sense for unsynchronized persistence contexts to be used with extended entity managers. The persist, remove, and refresh operations can then be executed either inside or outside transactional contexts, making it easier to queue up the persistence context changes before finally joining a transaction to cause them to be written out.

Note

When an unsynchronized persistence context has not been joined to a transaction, no writes to the database, such as those resulting from a user-initiated flush() call, may occur. Attempts to do so will cause an exception to be thrown.

For a simple example of an unsynchronized persistence context, look back at the shopping cart example (see Listings 3-25 and 3-26). We set the transaction attribute to be optional for the addItems() method of the ShoppingCart bean because a transaction was not required to add the items. That was fine for a fairly simplistic example. We didn’t need to actually persist anything or save any managed entities to a persistence context as part of the user interactions. Extending that example to use persistence to create/modify/delete independent entities, we could use an unsynchronized extended persistence context. The updated code with some additional methods is shown in Listing 6-14.
@Stateful
public class ShoppingCart {
    @PersistenceContext(unitName="productInventory",
                        type=EXTENDED,
                        synchronization=UNSYNCHRONIZED)
    EntityManager em;
    CustomerOrder order;
    public void addItem(String itemName, Integer quantity) {
        if (order == null) {
            order = new CustomerOrder();
            em.persist(order);
        }
        OrderItem item = order.getItem(itemName);
        if (item == null) {
            item = new OrderItem(itemName);
            item.setOrder(order);
            order.addItem(item);
            em.persist(item);
        }
        item.setQuantity(item.getQuantity() + quantity);;
    }
    @Remove
    public void process() {
       // Process the order. Join the tx and we are done.
        em.joinTransaction();
    }
    @Remove
    public void cancel() {
        em.clear();
    }
    // ...
}
Listing 6-14

Using an Unsynchronized Persistence Context

There are a few interesting bits in Listing 6-14 worth highlighting. The first is that the process() operation is trivial. All it does is call joinTransaction() so that the active container-managed transaction will cause the changes in the persistence context to be written out when the method ends. The second is that the cancel() method clears the persistence context. Though unnecessary in this case, since the bean will be removed at that point anyway, we are covered in case we decide to use the bean for a longer period of time and take off the @Remove annotation.

Earlier in the chapter, we discussed propagation of persistence contexts and how they get propagated with the Jakarta Transactions transaction. The same rule holds true with unsynchronized persistence contexts. Regardless of whether the persistence context has been joined to the transaction or not, the unsynchronized persistence context will be propagated when the Jakarta Transactions transaction is propagated. There is, however, one exception to this rule for unsynchronized persistence contexts. An unsynchronized persistence context, regardless of whether it is joined or not, is never propagated into a synchronized one.

Resource-Local Transactions

Resource-local transactions are controlled explicitly by the application. The application server, if there is one, has no part in the management of the transaction. Applications interact with resource-local transactions by acquiring an implementation of the jakarta.persistence.EntityTransaction interface from the entity manager. The getTransaction() method of the EntityManager interface is used for this purpose.

The EntityTransaction interface is designed to imitate the UserTransaction interface defined by Jakarta Transactions, and the two behave very similarly. The main difference is that EntityTransaction operations are implemented in terms of the transaction methods on the JDBC Connection interface. Listing 6-15 shows the complete EntityTransaction interface.
public interface EntityTransaction {
    public void begin();
    public void commit();
    public void rollback();
    public void setRollbackOnly();
    public boolean getRollbackOnly();
    public boolean isActive();
}
Listing 6-15

The EntityTransaction Interface

There are only six methods on the EntityTransaction interface. The begin() method starts a new resource transaction. If a transaction is active, isActive() will return true. Attempting to start a new transaction while a transaction is active will result in an IllegalStateException being thrown. Once active, the transaction can be committed by invoking commit() or rolled back by invoking rollback(). Both operations will fail with an IllegalStateException if there is no active transaction. A PersistenceException will be thrown if an error occurs during rollback, while a RollbackException, a PersistenceException subclass, will be thrown to indicate the transaction has rolled back because of a commit failure.

If a persistence operation fails while an EntityTransaction is active, the provider will mark it for rollback. It is the application’s responsibility to ensure that the rollback actually occurs by calling rollback(). If the transaction is marked for rollback, and a commit is attempted, a RollbackException will be thrown. To avoid this exception, the getRollbackOnly() method can be called to determine whether the transaction is in a failed state. Until the transaction is rolled back, it is still active and will cause any subsequent commit or begin operation to fail.

Listing 6-16 shows a Java SE application that uses the EntityTransaction API to perform a password change for users who failed to update their passwords before they expired.
public class ExpirePasswords {
    public static void main(String[] args) {
        int maxAge = Integer.parseInt(args[0]);
        String defaultPassword = args[1];
        try (EntityManagerFactory emf = Persistence.createEntityManagerFactory("admin")) {
            EntityManager em = emf.createEntityManager();
            Calendar cal = Calendar.getInstance();
            cal.add(Calendar.DAY_OF_YEAR, -maxAge);
            em.getTransaction().begin();
            Collection expired =
                em.createQuery("SELECT u FROM User u" +"WHERE u.lastChange <= ?1")
                  .setParameter(1, cal)
                  .getResultList();
            for (Iterator i = expired.iterator(); i.hasNext();) {
                User u = (User) i.next();
                System.out.println("Expiring password for " + u.getName());
                u.setPassword(defaultPassword);
            }
            em.getTransaction().commit();
            em.close();
        }
    }
}
Listing 6-16

Using the EntityTransaction Interface

Within the application server, Jakarta Transactions transaction management is the default and should be used by most applications. One example use of resource-local transactions in the Jakarta EE environment might be for logging. If your application requires an audit log stored in the database that must be written regardless of the outcome of any Jakarta Transactions transactions, a resource-local entity manager can be used to persist data outside of the current transaction. Resource transactions can be freely started and committed any number of times within a Jakarta Transactions transaction without impacting the state of the Jakarta Transactions transactions.

Listing 6-17 shows an example of a stateless session bean that provides audit logging that will succeed even if the active Jakarta Transactions transaction fails.
@Stateless
public class LogService {
    @PersistenceUnit(unitName="logging")
    EntityManagerFactory emf;
    public void logAccess(int userId, String action) {
        EntityManager em = emf.createEntityManager();
        try {
            LogRecord lr = new LogRecord(userId, action);
            em.getTransaction().begin();
            em.persist(lr);
            em.getTransaction().commit();
        } finally {
            em.close();
        }
    }
}
Listing 6-17

Using Resource-Local Transactions in the Jakarta EE Environment

Of course, you could make the argument that this is overkill for a simple logging bean. Direct JDBC would probably work just as easily, but these same log records can have uses elsewhere in the application. It is a trade-off in configuration (defining a completely separate persistence unit in order to enable the resource-local transactions) vs. the convenience of having an object-oriented representation of a log record.

Transaction Rollback and Entity State

When a database transaction is rolled back, all the changes made during the transaction are abandoned. The database reverts to whatever state it was in before the transaction began. But as mentioned in Chapter 2, the Java memory model is not transactional. There is no way to take a snapshot of object state and revert to it later if something goes wrong. One of the harder parts of using an object-relational mapping solution is that while you can use transactional semantics in your application to control whether data is committed to the database, you can’t truly apply the same techniques to the in-memory persistence context that manages your entity instances.

Any time you are working with changes that must be persisted to the database at a specific point in time, you are working with a persistence context synchronized with a transaction. At some point during the life of the transaction, usually just before it commits, the changes you require will be translated into the appropriate SQL statements and sent to the database. Whether you are using Jakarta Transactions transactions or resource-local transactions is irrelevant. You have a persistence context participating in a transaction with changes that need to be made.

If that transaction rolls back, two things happen. The first is that the database transaction will be rolled back. The next thing that happens is that the persistence context is cleared, detaching all our managed entity instances. If the persistence context was transaction-scoped, it is removed.

Because the Java memory model is not transactional, you are basically left with a bunch of detached entity instances. More importantly, these detached instances reflect the entity state exactly as it was at the point when the rollback occurred. Faced with a rolled-back transaction and detached entities, you might be tempted to start a new transaction, merge the entities into the new persistence context, and start over. The following issues need to be considered in this case:
  • If there is a new entity that uses automatic primary key generation, there can be a primary key value assigned to the detached entity. If this primary key was generated from a database sequence or table, the operation to generate the number might have been rolled back with the transaction. This means that the same sequence number could be given out again to a different object. Clear the primary key before attempting to persist the entity again, and do not rely on the primary key value in the detached entity.

  • If your entity uses a version field for locking purposes that is automatically maintained by the persistence provider, it might be set to an incorrect value. The value in the entity will not match the correct value stored in the database. We cover locking and versioning in Chapter 12.

If you need to reapply some of the changes that failed and are currently sitting in the detached entities, consider selectively copying the changed data into new managed entities. This guarantees that the merge operation will not be compromised by stale data left in the detached entity. To merge failed entities into a new persistence context, some providers might offer additional options that avoid some or all these issues. The safe and sure approach is to ensure the transaction boundaries are well enough defined so in the event of a failure the transaction can be retried, including retrieving all managed state and reapplying the transactional operations.

One last point worth mentioning is that rollbacks have no direct effect on persistence contexts that are not synchronized with the rolled-back transaction. The changes made to unsynchronized persistence contexts are not transactional and are therefore mostly immune to transaction failures. We say mostly immune because there is a case, described in the next paragraph, when it can be affected.

If a persistence unit is configured to access a transactional data source (e.g., by setting the jta-data-source element in persistence.xml), then that data source will be used to read entities from the database, even by entity managers with unsynchronized persistence contexts. If a transaction is active and changes to an entity were written out by another independent entity manager with a synchronized persistence context, then those entity changes will be visible to, and possibly read into, the unsynchronized persistence context through a connection from the transactional data source. If the transaction then rolls back, the uncommitted entity changes will be left sitting in the unsynchronized persistence context.

The situation we just described is a fairly unlikely corner case. However, if you think this corner case could apply to you, there is a remedy. You can define a nontransactional data source in the server and reference it in the persistence unit (by setting the non-jta-data-source element in persistence.xml to the nontransactional data source) in addition to the transactional one, as shown here:
<persistence>
    <persistence-unit name="EmployeeService">
        <jta-data-source>jdbc/EmployeeJtaDS</jta-data-source>
        <non-jta-data-source>jdbc/EmployeeNonJtaDS</non-jta-data-source>
    </persistence-unit>
</persistence>

The unsynchronized persistence context would then issue queries through connections from the nontransactional data source, thereby being isolated from any concurrent transactional changes made by others. Only when the persistence context became synchronized with a transaction would it go on to use the transactional data source.

Choosing an Entity Manager

With all the different entity manager types, each with a different lifecycle and different rules about transaction association and propagation, it can all be a little overwhelming. What style is right for your application? Application-managed or container-managed? Transaction-scoped or extended? Synchronized or unsynchronized?

Generally speaking, we believe that container-managed, transaction-scoped entity managers are going to be a very convenient and appropriate model for many applications. This is the design that originally inspired Jakarta Persistence and is the model that commercial persistence providers have been using for years. The selection of this style to be the default for Jakarta EE applications was no accident. It offers the best combination of flexible transaction propagation with easy-to-understand semantics.

Container-managed, extended persistence contexts offer a different programming model, with entities remaining managed after commit, but they are tied to the lifecycle of a Jakarta EE component—in this case, the stateful session bean. There are some interesting new techniques possible with the extended persistence context (some of which we describe later in this chapter), but they might not apply to all applications.

In some enterprise applications, application-managed entity managers may be of use if they need to be accessed by unmanaged classes. The lack of propagation means that they must be passed around as method arguments or stored in a shared object in order to share the persistence context. Evaluate application-managed entity managers based on your expected transactional needs, and the size and complexity of your application.

More than anything, we recommend that you try to be consistent in how entity managers are selected and applied. Mixing different types of entity managers in an application is going to be hard for an application maintainer to understand and follow, and will likely be frustrating to debug because the different entity manager types can intersect in unexpected ways.

Entity Manager Operations

Armed with information about the different entity manager types and how they work with persistence contexts, we can now revisit the basic entity manager operations we introduced in Chapter 2 and reveal more of the details. The following sections describe the entity manager operations with respect to the different entity manager and persistence context types. Locking modes and the locking variants of the following operations are discussed in Chapter 12.

Persisting an Entity

The persist() method of the EntityManager interface accepts a new entity instance and causes it to become managed. If the entity to be persisted is already managed by the persistence context, it is ignored. The contains() operation can be used to check whether an entity is already managed, but it is very rare that this should be required. It should not come as a surprise to the application to find out which entities are managed and which are not. The design of the application dictates when entities become managed.

For an entity to be managed does not mean that it is persisted to the database right away. The actual SQL to create the necessary relational data will not be generated until the persistence context is synchronized with the database, typically only when the transaction commits. However, once a new entity is managed, any changes to that entity can be tracked by the persistence context. Whatever state exists on the entity when the transaction commits is what will be written to the database.

When persist() is invoked outside of a transaction, the behavior depends on the type of entity manager. A transaction-scoped entity manager will throw a TransactionRequiredException because there is no persistence context available in which to make the entity managed. Application-managed and extended entity managers will accept the persist request, causing the entity to become managed, but no immediate action will be taken until a new transaction begins and the persistence context becomes synchronized with the transaction. In effect, this queues up the change to happen at a later time. It is only when the transaction commits that changes will be written out to the database.

The persist() operation is intended for new entities that do not already exist in the database. If the provider immediately determines that it is not true, an EntityExistsException will be thrown. If the provider does not make this determination (because it has deferred the existence check and the insert until flush or commit time), and the primary key is in fact a duplicate, an exception will be thrown when the persistence context is synchronized to the database.

Up to this point, we have been discussing the persistence of entities only without relationships. But, as we learned in Chapter 4, Jakarta Persistence supports a wide variety of relationship types. In practice, most entities are in a relationship with at least one other entity. Consider the following sequence of operations:
Department dept = em.find(Department.class, 30);
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);

Despite the brevity of this example, we have covered a lot of points relating to persisting a relationship. We begin by retrieving a pre-existing Department instance. A new Employee instance is then created, supplying the primary key and basic information about the Employee. We then assign the employee to the department, by setting the department attribute of the Employee to point to the Department instance we retrieved earlier. Because the relationship is bidirectional, we then add the new Employee instance to the employees collection in the Department instance. Finally the new Employee instance is persisted with the call to persist(). Assuming a transaction then commits, and the persistence context is synchronized to it, the new entity will be stored in the database.

An interesting thing about this example is that the Department is a passive participant despite the Employee instance being added to its collection. The Employee entity is the owner of the relationship because it is in a many-to-one relationship with the Department. As we mentioned in Chapter 4, the source side of the relationship is the owner, while the target is the inverse in this type of relationship. When the Employee is persisted, the foreign key to the Department is written out to the table mapped by the Employee, and no actual change is made to the Department entity’s physical representation. Had we only added the employee to the collection and not updated the other side of the relationship, nothing would have been persisted to the database.

Finding an Entity

The ever-present find() method is the workhorse of the entity manager. Whenever an entity needs to be located by its primary key, find() is usually the best way to go. Not only does it have simple semantics, but most persistence providers will also optimize this operation to use an in-memory cache that minimizes trips to the database.

The find() operation returns a managed entity instance in all cases except when invoked outside of a transaction on a transaction-scoped entity manager. In this case, the entity instance is returned in a detached state. It is not associated with any persistence context.

There exists a special version of find() that can be used in one particular situation. That situation is when a relationship is being created between two entities in a one-to-one or many-to-one relationship in which the target entity already exists and its primary key is well known. Because we are only creating a relationship, it might not be necessary to fully load the target entity to create the foreign key reference to it. Only its primary key is required. The getReference() operation can be used for this purpose. Consider the following example:
Department dept = em.getReference(Department.class, 30);
Employee emp = new Employee();
emp.setId(53);
emp.setName("Peter");
emp.setDepartment(dept);
dept.getEmployees().add(emp);
em.persist(emp);

The only difference between this sequence of operations and the ones we demonstrated earlier is that the find() call has been replaced with a call to getReference(). When the getReference() call is invoked, the provider can return a proxy to the Department entity without actually retrieving it from the database. As long as only its primary key is accessed, Department data does not need to be fetched. Instead, when the Employee is persisted, the primary key value will be used to create the foreign key to the corresponding Department entry. The getReference() call is effectively a performance optimization that removes the need to retrieve the target entity instance.

There are some drawbacks to using getReference() that must be understood. The first is that if a proxy is used, it might throw an EntityNotFoundException exception if it is unable to locate the real entity instance when an attribute other than the primary key is accessed. The assumption with getReference() is that you are sure the entity with the correct primary key exists. If, for some reason, an attribute other than the primary key is accessed, and the entity does not exist, an exception will be thrown. A corollary to this is that the object returned from getReference() might not be safe to use if it is no longer managed. If the provider returns a proxy, it will be dependent on there being an active persistence context to load entity state.

Given the very specific situation in which getReference() can be used, find() should be used in virtually all cases. The in-memory cache of a good persistence provider is effective enough that the performance cost of accessing an entity via its primary key will not usually be noticed. In the case of EclipseLink, it has a fully integrated shared object cache, so not only is local persistence context management efficient but also all threads on the same server can benefit from the shared contents of the cache. The getReference() call is a performance optimization that should be used only when there is evidence to suggest that it will actually benefit the application .

Removing an Entity

Removing an entity is not a complex task, but it can require several steps depending on the number of relationships in the entity to be removed. At its most basic, removing an entity is simply a case of passing a managed entity instance to the remove() method of an entity manager. As soon as the associated persistence context becomes synchronized with a transaction and commits, the entity is removed. At least that is what we would like to happen. As we soon show, removing an entity requires some attention to its relationships, or else the integrity of the database can be compromised in the process.

Let’s walk through a simple example. Consider the Employee and ParkingSpace relationship that we demonstrated in Chapter 4. The Employee has a unidirectional one-to-one relationship with the ParkingSpace entity. Now imagine that we execute the following code inside a transaction, where empId corresponds to an Employee primary key:
Employee emp = em.find(Employee.class, empId);
em.remove(emp.getParkingSpace());
When the transaction commits, we see the DELETE statement for the PARKING_SPACE table get generated, but then we get an exception containing a database error that shows that we have violated a foreign key constraint. It turns out that a referential integrity constraint exists between the EMPLOYEE table and the PARKING_SPACE table. The row was deleted from the PARKING_SPACE table, but the corresponding foreign key in the EMPLOYEE table was not set to NULL. To correct the problem, we have to explicitly set the parkingSpace attribute of the Employee entity to null before the transaction commits:
Employee emp = em.find(Employee.class, empId);
ParkingSpace ps = emp.getParkingSpace();
emp.setParkingSpace(null);
em.remove(ps);

Relationship maintenance is the responsibility of the application. We repeat this statement over the course of this book, but it cannot be emphasized enough. Almost every problem related to removing an entity always comes back to this issue. If the entity to be removed is the target of foreign keys in other tables, those foreign keys must be cleared for the remove to succeed. The remove operation will either fail as it did here or will result in stale data being left in the foreign key columns referring to the removed entity in the event that there is no referential integrity.

An entity can be removed only if it is managed by a persistence context. This means that a transaction-scoped entity manager can be used to remove an entity only if there is an active transaction. Attempting to invoke remove() when there is no transaction will result in a TransactionRequiredException exception. Like the persist() operation described earlier, application-managed and extended entity managers can remove an entity outside of a transaction, but the change will not take place in the database until a transaction, with which the persistence context is synchronized, is committed.

After the transaction has committed, all entities that were removed in that transaction are left in the state that they were in before they were removed. A removed entity instance can be persisted again with the persist() operation, but the same issues with generated state that we discussed in the “Transaction Rollback and Entity State” section apply here as well.

Cascading Operations

By default, every entity manager operation applies only to the entity supplied as an argument to the operation. The operation will not cascade to other entities that have a relationship with the entity that is being operated on. For some operations, such as remove(), this is usually the desired behavior. We wouldn’t want the entity manager to make incorrect assumptions about which entity instances should be removed as a side effect from some other operation. But the same does not hold true for operations such as persist(). Chances are that if we have a new entity and it has a relationship to another new entity, the two must be persisted together.

Consider the sequence of operations in Listing 6-18 that are required to create a new Employee entity with an associated Address entity and make the two persistent. The second call to persist() that makes the Address entity managed is bothersome. An Address entity is coupled to the Employee entity that holds on to it. Whenever a new Employee is created, it makes sense to cascade the persist() operation to the Address entity if it is present. In Listing 6-18 we are manually cascading by means of an explicit persist() call on the associated Address.
Employee emp = new Employee();
emp.setId(2);
emp.setName("Rob");
Address addr = new Address();
addr.setStreet("645 Stanton Way");
addr.setCity("Manhattan");
addr.setState("NY");
emp.setAddress(addr);
em.persist(addr);
em.persist(emp);
Listing 6-18

Persisting Employee and Address Entities

Fortunately, Jakarta Persistence provides a mechanism to define when operations such as persist() should be automatically cascaded across relationships. The cascade attribute, in all the logical relationship annotations (@OneToOne, @OneToMany, @ManyToOne, and @ManyToMany), defines the list of entity manager operations to be cascaded.

Entity manager operations are identified using the CascadeType enumerated type when listed as part of the cascade attribute. The PERSIST, REFRESH, REMOVE, MERGE, and DETACH constants pertain to the entity manager operation of the same name. The constant ALL is shorthand for declaring that all five operations should be cascaded. By default, relationships have an empty cascade set.

The following sections will define the cascading behavior of the persist() and remove() operation s. We introduce the detach() and merge() operations and their cascading behavior later in this chapter in the “Merging Detached Entities” section. Likewise, we introduce the refresh() operation and its cascading behavior in Chapter 12.

Cascade Persist

To begin, let’s consider the changes required to make the persist() operation cascade from Employee to Address. In the definition of the Employee class, there is a @ManyToOne annotation defined for the address relationship. To enable the cascade, we must add the PERSIST operation to the list of cascading operations for this relationship. Listing 6-19 shows a fragment of the Employee entity that demonstrates this change.
@Entity
public class Employee {
    // ...
    @ManyToOne(cascade=CascadeType.PERSIST)
    Address address;
    // ...
}
Listing 6-19

Enabling Cascade Persist

To leverage this change, we need only ensure that the Address entity has been set on the Employee instance before invoking persist() on it. As the entity manager encounters the Employee instance and adds it to the persistence context, it will navigate across the address relationship looking for a new Address entity to manage as well. In comparison with the approach in Listing 6-18, this change frees us from having to persist the Address separately.

Cascade settings are unidirectional. This means that they must be explicitly set on both sides of a relationship if the same behavior is intended for both situations. For example, in Listing 6-19, we only added the cascade setting to the address relationship in the Employee entity. If Listing 6-18 were changed to persist only the Address entity, not the Employee entity, the Employee entity would not become managed because the entity manager has not been instructed to navigate out from any relationships defined on the Address entity.

Even though it is legal to do so, it is still unlikely that we would add cascading operations from the Address entity to the Employee entity, because it is a child of the Employee entity. While causing the Employee instance to become managed as a side effect of persisting the Address instance is harmless, application code would not expect the same from the remove() operation, for example. Therefore, we must be judicious in applying cascades because there is an expectation of ownership in relationships that influences what developers expect when interacting with these entities.

In the “Persisting an Entity” section, we mentioned that the entity instance is ignored if it is already persisted. This is true, but the entity manager will still honor the PERSIST cascade in this situation. For example, consider our Employee entity again. If the Employee instance is already managed, and a new Address instance is set in it, invoking persist() again on the Employee instance will cause the Address instance to become managed. No changes will be made to the Employee instance because it is already managed.

Because adding the PERSIST cascade is a very common and desirable behavior for relationships, it is possible to make this the default cascade setting for all relationships in the persistence unit. We discuss this technique in Chapter 10.

Cascade Remove

At first glance, having the entity manager automatically cascade remove() operation s might sound attractive. Depending on the cardinality of the relationship, it could eliminate the need to explicitly remove multiple entity instances. And yet, while we could cascade this operation in a number of situations, this should be applied only in certain cases. There are really only two cases in which cascading the remove() operation makes sense, one-to-one and one-to-many relationships, in which there is a clear parent-child relationship. It can’t be blindly applied to all one-to-one and one-to-many relationships because the target entities might also be participating in other relationships or might make sense as stand-alone entities. Care must be taken when using the REMOVE cascade option.

With that warning given, let’s look at a situation in which cascading the remove() operation makes sense. If an Employee entity is removed (hopefully an uncommon occurrence!), it might make sense to cascade the remove() operation to both the ParkingSpace and Phone entities related to the Employee. These are both cases in which the Employee is the parent of the target entities, meaning they are not referenced by other entities in the system. Listing 6-20 demonstrates the changes to the Employee entity class that enables this behavior. Note that we have added the REMOVE cascade in addition to the existing PERSIST option. Chances are, if an owning relationship is safe to use REMOVE, it is also safe to use PERSIST.
@Entity
public class Employee {
    // ...
    @OneToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    ParkingSpace parkingSpace;
    @OneToMany(mappedBy="employee",
               cascade={CascadeType.PERSIST, CascadeType.REMOVE})
    Collection<Phone> phones;
    // ...
}
Listing 6-20

Enabling Cascade Remove

Now let’s take a step back and look at what it means to cascade the remove() operation . As it processes the Employee instance, the entity manager will navigate across the parkingSpace and phones relationships and invoke remove() on those entity instances as well. Like the remove() operation on a single entity, this is a database operation and has no effect at all on the in-memory links between the object instances. When the Employee instance becomes detached, its phones collection will still contain all the Phone instances that were there before the remove() operation took place. The Phone instances are detached because they were removed as well, but the link between the two instances remains.

Because the remove() operation can be safely cascaded only from parent to child, it can’t help the situation encountered earlier in the “Removing an Entity” section. There is no setting that can be applied to a relationship from one entity to another that will cause it to be removed from a parent without also removing the parent in the process. For example, when trying to remove the ParkingSpace entity, we hit an integrity constraint violation from the database unless the parkingSpace field in the Employee entity is set to null. Setting the REMOVE cascade option on the @OneToOne annotation in the ParkingSpace entity would not cause it to be removed from the Employee; instead, it would cause the Employee instance itself to become removed. Clearly this is not the behavior we desire. There are no shortcuts to relationship maintenance.

Clearing the Persistence Context

Occasionally , it might be necessary to clear a persistence context of its managed entities. This is usually required only for application-managed and extended persistence contexts that are long-lived and have grown too large. For example, consider an application-managed entity manager that issues a query returning several hundred entity instances. After changes are made to a handful of these instances and the transaction is committed, you have left in memory hundreds of objects that you have no intention of changing any further. If you don’t want to close the persistence context, you need to be able to clear out the managed entities, or else the persistence context will continue to grow over time.

The clear() method of the EntityManager interface can be used to clear the persistence context. In many respects, this is semantically equivalent to a transaction rollback. All entity instances managed by the persistence context become detached with their state left exactly as it was when the clear() operation was invoked. If a transaction was started at this point and then committed, nothing would be written out to the database because the persistence context is empty. The clear() operation is all or nothing. Selectively cancelling the management of any particular entity instance while the persistence context is still open is achieved via the detach() operation. We discuss this later in the “Detachment and Merging” section.

Although technically possible, clearing the persistence context when there are uncommitted changes is a dangerous operation. The persistence context is an in-memory structure, and clearing it simply detaches the managed entities. If you are in a transaction and changes have already been written to the database, they will not be rolled back when the persistence context is cleared. The detached entities that result from clearing the persistence context also suffer from all the negative effects caused by a transaction rollback even though the transaction is still active. For example, identifier generation and versioning should be considered suspect for any entities detached as a result of using the clear() operation .

Synchronization with the Database

Any time the persistence provider generates SQL and writes it out to the database over a JDBC connection, we say that the persistence context has been flushed. All pending changes that require a SQL statement to become part of the transactional changes in the database have been written out and will be made permanent when the database transaction commits. It also means that any subsequent SQL operation that takes place after the flush will incorporate these changes. This is particularly important for SQL queries that are executed in a transaction that is also changing entity data.

If there are managed entities with changes pending in a synchronized persistence context, a flush is guaranteed to occur in two situations. The first is when the transaction commits. A flush of any required changes will occur before the database transaction has completed. The only other time a flush is guaranteed to occur is when the entity manager flush() operation is invoked. This method allows developers to manually trigger the same process that the entity manager internally uses to flush the persistence context.

That said, a flush of the persistence context could occur at any time if the persistence provider deems it necessary. An example of this is when a query is about to be executed, and it depends on new or changed entities in the persistence context. Some providers will flush the persistence context to ensure that the query incorporates all pending changes. A provider might also flush the persistence context often if it uses an eager-write approach to entity updates. Most persistence providers defer SQL generation to the last possible moment for performance reasons, but this is not guaranteed.

Now that we have covered the circumstances in which a flush can occur, let’s look at exactly what it means to flush the persistence context. A flush basically consists of three components: new entities that need to be persisted, changed entities that need to be updated, and removed entities that need to be deleted from the database. All this information is managed by the persistence context. It maintains links to all the managed entities that will be created or changed as well as the list of entities that need to be removed.

When a flush occurs, the entity manager first iterates over the managed entities and looks for new entities that have been added to relationships with cascade persist enabled. This is logically equivalent to invoking persist() again on each managed entity just before the flush occurs. The entity manager also checks to ensure the integrity of all the relationships. If an entity points to another entity that is not managed or has been removed, an exception can be thrown.

The rules for determining whether the flush fails in the presence of an unmanaged entity can be complicated. Let’s walk through an example that demonstrates the most common issues. Figure 6-2 shows an object diagram for an Employee instance and some of the objects that it is related to. The emp and ps entity objects are managed by the persistence context. The addr object is a detached entity from a previous transaction, and the Phone objects are new objects that have not been part of any persistence operation so far.
Figure 6-2

Links to unmanaged entities from a persistence context

To determine the outcome of flushing the persistence context given the arrangement shown in Figure 6-2, we must first look at the cascade settings of the Employee entity. Listing 6-21 shows the relationships as implemented in the Employee entity. Only the phones relationship has the PERSIST cascade option set. The other relationships are all defaulted so they will not cascade.
@Entity
public class Employee {
    // ...
    @OneToOne
    ParkingSpace parkingSpace;
    @OneToMany(mappedBy="employee", cascade=CascadeType.PERSIST)
    Collection<Phone> phones;
    @ManyToOne
    Address address;
    // ...
}
Listing 6-21

Relationship Cascade Settings for Employee

Starting with the emp object, let’s walk through the flush process as if we are the persistence provider. The emp object is managed and has links to four other objects. The first step in the process is to navigate the relationships from this entity as if we are invoking persist() on it. The first object we encounter in this process is the ps object across the parkingSpace relationship. Because ps is also managed, we don’t have to do anything further.

Next, we navigate the phones relationship to the two Phone objects. These entities are new, and this would normally cause an exception, but because the PERSIST cascade option has been set, we perform the equivalent of invoking persist() on each Phone object. This makes the objects managed, making them part of the persistence context. The Phone objects do not have any further relationships to cascade the persist operation, so we are done here as well.

Next, we reach the addr object across the address relationship. Because this object is detached, we would normally throw an exception, but this particular relationship is a special case in the flush algorithm. Any time a detached object that is the target of the one-to-one or many-to-one relationship is encountered where the source entity is the owner, the flush will still proceed because the act of persisting the owning entity does not depend on the target. The owning entity has the foreign key column and needs to store only the primary key value of the target entity.

This completes the flush of the emp object. The algorithm then moves to the ps object and starts the process again. Because there are no relationships from the ps object to any other, the flush process completes. So in this example even though three of the objects pointed to from the emp object are not managed, the overall flush completes successfully because of the cascade settings and rules of the flush algorithm.

Ideally, during a flush all the objects pointed to by a managed entity will also be managed entities themselves. If this is not the case, the next thing we need to be aware of is the PERSIST cascade setting. If the relationship has this setting, target objects in the relationship will also be persisted, making them managed before the flush completes. If the PERSIST cascade option is not set, an IllegalStateException exception will be thrown whenever the target of the relationship is not managed, except in the special case related to one-to-one and many-to-one relationships that we described previously.

In light of how the flush operation works, it is always safer to update relationships pointing to entities that will be removed before carrying out the remove() operation. A flush can occur at any time, so invoking remove() on an entity without clearing any relationships that point to the removed entity could result in an unexpected IllegalStateException exception if the provider decides to flush the persistence context before you get around to updating the relationships.

In Chapter 7, we also discuss techniques to configure the data integrity requirements of queries so that the persistence provider is better able to determine when a flush of the persistence context is really necessary.

Detachment and Merging

Simply put, a detached entity is one that is no longer associated with a persistence context. It was managed at one point, but the persistence context might have ended or the entity might have been transformed so that it has lost its association with the persistence context that used to manage it. The persistence context, if there still is one, is no longer tracking the entity. Any changes made to the entity won’t be persisted to the database, but all the state that was there on the entity when it was detached can still be used by the application. A detached entity cannot be used with any entity manager operation that requires a managed instance.

The opposite of detachment is merging. Merging is the process by which an entity manager integrates detached entity state into a persistence context. Any changes to entity state that were made on the detached entity overwrite the current values in the persistence context. When the transaction commits, those changes will be persisted. Merging allows entities to be changed “offline” and then have those changes incorporated later on.

The following sections will describe detachment and how detached entities can be merged back into a persistence context.

Detachment

There are two views of detachment. On one hand, it is a powerful tool that can be leveraged by applications in order to work with remote applications or to support access to entity data long after a transaction has ended. On the other hand, it can be a frustrating problem when the domain model contains lots of lazy-loading attributes and clients using the detached entities need to access this information.

There are many ways in which an entity can become detached. Each of the following situations will lead to detached entities:
  • When the transaction that a transaction-scoped persistence context is associated with commits, all the entities managed by the persistence context become detached.

  • If an application-managed persistence context is closed, all its managed entities become detached.

  • If a stateful session bean with an extended persistence context is removed, all its managed entities become detached.

  • If the clear() method of an entity manager is used, it detaches all the entities in the persistence context managed by that entity manager.

  • If the detach() method of an entity manager is used, it detaches a single entity instance from the persistence context managed by that entity manager.

  • When transaction rollback occurs, it causes all entities in all persistence contexts associated with the transaction to become detached.

  • When an entity is serialized, the serialized form of the entity is detached from its persistence context.

Some of these situations might be intentional and planned for, such as detachment after the end of the transaction or serialization. Others might be unexpected, such as detachment because of rollback.

Explicit detachment of an entity is achieved through the detach() operation . Unlike the clear() operation discussed earlier, if passed an entity instance as a parameter, the detach() operation will be restricted to a single entity and its relationships. Like other cascading operations, the detach() operation will also navigate across relationships that have the DETACH or ALL cascade options set, detaching additional entities as appropriate. Note that passing a new or removed entity to detach() has different behavior than a normal managed entity. The operation does not detach either new or removed entities, but it will still attempt, when configured to cascade, to cascade across relationships on removed entities and detach any managed entities that are the target of those relationships.

In Chapter 4, we introduced the LAZY fetch type that can be applied to any basic mapping or relationship. This has the effect of hinting to the provider that the loading of a basic or relationship attribute should be deferred until it is accessed for the first time. Although not commonly used on basic mappings, marking relationship mappings to be lazy loaded is an important part of performance tuning.

We need to consider, however, the impact of detachment on lazy loading. Consider the Employee entity shown in Listing 6-22. The address relationship will eagerly load because many-to-one relationships eagerly load by default. In the case of the parkingSpace attribute, which would also normally eagerly load, we have explicitly marked the relationship as being lazily loaded. The phones relationship, as a one-to-many relationship, will also lazy load by default.
@Entity
public class Employee {
    // ...
    @ManyToOne
    private Address address;
    @OneToOne(fetch=FetchType.LAZY)
    private ParkingSpace parkingSpace;
    @OneToMany(mappedBy="employee")
    private Collection<Phone> phones;
    // ...
}
Listing 6-22

Employee with Lazy-Loading Mappings

As long as the Employee entity is managed, everything works as we expect. When the entity is retrieved from the database, only the associated Address entity will be eagerly loaded. The provider will fetch the necessary entities the first time the parkingSpace and phones relationships are accessed.

If this entity becomes detached, the outcome of accessing the parkingSpace and phones relationships is suddenly a more complex issue. If the relationships were accessed while the entity was still managed, the target entities can also be safely accessed while the Employee entity is detached. If the relationships were not accessed while the entity was managed, we have a problem.

The behavior of accessing an unloaded attribute when the entity is detached is not defined. Some vendors might attempt to resolve the relationship, while others might simply throw an exception or leave the attribute uninitialized. If the entity was detached because of serialization, there is virtually no hope of resolving the relationship. The only portable thing to do with attributes that are unloaded is leave them alone. Of course, this implies that you know which attributes have been loaded, and that is not always easy or practical depending on where the entity is (see the isLoaded() method in the “Utility Classes” section in Chapter 12).

In the case where entities have no lazy-loading attributes, detachment is not a big deal. All the entity state that was there in the managed version is still available and ready to use in the detached version of the entity. In the presence of lazy-loading attributes, care must be taken to ensure that all the information you need to access offline is available. When possible, try to define the set of detached entity attributes that can be accessed by the offline component. The supplier of the entities should treat that set as a contract and honor it by triggering those attributes while the entity is still managed. Later in the chapter, we demonstrate a number of strategies for planning for, and working with, detached entities, including how to cause unloaded attributes to be loaded.

Merging Detached Entities

The merge() operation is used to merge the state of a detached entity into a persistence context. The method is straightforward to use, requiring only the detached entity instance as an argument. There are some subtleties to using merge() that make it different to use from other entity manager methods. Consider the following example, which shows a session bean method that accepts a detached Employee parameter and merges it into the current persistence context:
public void updateEmployee(Employee emp) {
    em.merge(emp);
    emp.setLastAccessTime(new Date());
}
Assuming that a transaction begins and ends with this method call, any changes made to the Employee instance while it was detached will be written to the database. What will not be written, however, is the change to the last access time. The argument to merge() does not become managed as a result of the merge. A different managed entity (either a new instance or an existing managed version already in the persistence context) is updated to match the argument, and then this instance is returned from the merge() method. Therefore, to capture this change, we need to use the return value from merge() because it is the managed entity. The following example shows the correct implementation:
public void updateEmployee(Employee emp) {
    Employee managedEmp = em.merge(emp);
    managedEmp.setLastAccessTime(new Date());
}

Returning a managed instance other than the original entity is a critical part of the merge process. If an entity instance with the same identifier already exists in the persistence context, the provider will overwrite its state with the state of the entity that is being merged, but the managed version that existed already must be returned to the client so that it can be used. If the provider did not update the Employee instance in the persistence context, any references to that instance will become inconsistent with the new state being merged in.

When merge() is invoked on a new entity, it behaves similarly to the persist() operation. It adds the entity to the persistence context, but instead of adding the original entity instance, it creates a new copy and manages that instance instead. The copy that is created by the merge() operation is persisted as if the persist() method were invoked on it.

In the presence of relationships, the merge() operation will attempt to update the managed entity to point to managed versions of the entities referenced by the detached entity. If the entity has a relationship to an object that has no persistent identity, the outcome of the merge operation is undefined. Some providers might allow the managed copy to point to the nonpersistent object, whereas others might throw an exception immediately. The merge() operation can be optionally cascaded in these cases to prevent an exception from occurring. We cover cascading of the merge() operation later in this section. If an entity being merged points to a removed entity, an IllegalArgumentException exception will be thrown.

Lazy-loading relationships are a special case in the merge operation. If a lazy-loading relationship was not triggered on an entity before it became detached, that relationship will be ignored when the entity is merged. If the relationship was triggered while managed and then set to null while the entity was detached, the managed version of the entity will likewise have the relationship cleared during the merge.

To illustrate the behavior of merge() with relationships, consider the object diagram shown in Figure 6-3. The detached emp object has relationships to three other objects. The addr and dept objects are detached entities from a previous transaction, whereas the phone1 entity was recently created and persisted using the persist() operation and is now managed as a result. Inside the persistence context there is currently an Employee instance with a relationship to another managed Address. The existing managed Employee instance does not have a relationship to the newly managed Phone instance.
Figure 6-3

Entity state prior to merge

Let’s consider the effect of invoking merge() on the emp object. The first thing that happens is that the provider checks the persistence context for a pre-existing entity instance with the same identifier. In this example, the emp1 object from the persistence context matches the identifier from the emp object we are trying to merge. Therefore, the basic state of the emp object overwrites the state of the emp1 object in the persistence context, and the emp1 object will be returned from the merge() operation.

The provider next considers the Phone and Department entities pointed to from emp. The phone1 object is already managed, so the provider can safely update emp1 to point to this instance. In the case of the dept object, the provider checks to see whether there is already a persistent Department entity with the same identifier. In this case, it finds one in the database and loads it into the persistence context. The emp1 object is then updated to point to this version of the Department entity. The detached dept object does not become managed again.

Finally, the provider checks the addr object referenced from emp. In this case, it finds a pre-existing managed object addr1 with the same identifier. Because the emp1 object already points to the addr1 object, no further changes are made. At this point let’s look at the state of the object model after the merge. Figure 6-4 shows these changes.
Figure 6-4

Entity state after merge

In Figure 6-4 we see that the emp1 object has been updated to reflect the state changes from emp. The dept1 object is new to the persistence context after being loaded from the database. The emp1 object now points to both the phone1 object and the dept1 object in order to match the relationships of the emp object. The addr1 object has not changed at all. The fact that the addr1 object has not changed might come as a surprise. After all, the addr object had pending changes, and it was pointed to by the emp object that was merged.

To understand why, we must return to the issue of cascading operations with the entity manager. By default, no operations are cascaded when an entity manager operation is applied to an entity instance. The merge() operation is no different in this regard. In order for the merge to be cascaded across relationships from an Employee, the MERGE cascade setting must be set on the relationship mappings. Otherwise, we would have to invoke merge() on each related object.

Looking back at our example, the problem with the updated Address entity was that the Employee entity did not cascade the merge() operation to it. This had the unfortunate side effect of effectively discarding the changes we had made to the Address entity in favor of the version already in the persistence context. To obtain the behavior that we intended, we must either invoke merge() explicitly on the addr object or change the relationship mappings of the Employee entity to include the MERGE cascade option. Listing 6-23 shows the changed Employee class.
@Entity
public class Employee {
    @Id private int id;
    private String name;
    private long salary;
    @ManyToOne(cascade=CascadeType.MERGE)
    private Address address;
    @ManyToOne
    private Department department;
    @OneToMany(mappedBy="employee", cascade=CascadeType.MERGE)
    private Collection<Phone> phones;
    // ...
}
Listing 6-23

Employee Entity with Merge Cascade Setting

With the Employee entity changed in this way, the merge operation will be cascaded to the Address and Phone entities pointed to by any Employee instances. This is equivalent to invoking merge() on each instance individually. Note that we did not cascade the merge operation to the Department entity. We generally cascade operations only down from parent to child, not upward from child to parent. Doing so is not harmful, but it requires more effort from the persistence provider to search out changes. If the Department entity changes as well, it is better to cascade the merge from the Department to its associated Employee instances and then merge only a single Department instance instead of multiple Employee instances.

Merging detached entities with relationships can be a tricky operation. Ideally, we want to merge the root of an object graph and have all related entities get merged in the process. This can work, but only if the MERGE cascade setting has been applied to all relationships in the graph. If it hasn’t, you must merge each instance that is the target of a noncascaded relationship one at a time.

Before we leave the topic of merging, we must mention that locking and versioning plays a vital role in ensuring data integrity in these situations. We explore this topic in Chapter 12.

Working with Detached Entities

Let’s begin with a scenario that is very common with modern web applications. A servlet calls out to a session bean to execute a query and receives a collection of entities in return. The servlet then places these entities into the request map and forwards the request to a JSP for presentation. This pattern is called Page Controller,1 a variation of the Front Controller2 pattern in which there is a single controller for each view instead of one central controller for all views. In the context of the familiar Model-View-Controller (MVC) architecture, the session bean provides the model, the JSP page is the view, and the servlet is the controller.

First consider the managed CDI bean that will produce the results that will be rendered by the JSP page. Listing 6-24 shows the bean implementation. In this example, we are looking only at the findAll() method , which returns all the Employee instances stored in the database.
@Dependent
public class EmployeeService {
    @PersistenceContext(unitName="EmployeeService")
    private EntityManager em;
    public List findAll() {
        return em.createQuery("SELECT e FROM Employee e")
                 .getResultList();
    }
    // ...
}
Listing 6-24

The EmployeeService Bean

Listing 6-25 shows the source code for a simple servlet that invokes the findAll() method of the EmployeeService bean to fetch all the Employee entities in the database. It then places the results in the request map and delegates to the listEmployees.jsp JSP page to render the result.
public class EmployeeServlet extends HttpServlet {
    @Inject EmployeeService bean;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        List emps = bean.findAll();
        request.setAttribute("employees", emps);
        getServletContext().getRequestDispatcher("/listEmployees.jsp")
                           .forward(request, response);
    }
}
Listing 6-25

The View Employees Servlet

Finally, Listing 6-26 shows the last part of our MVC architecture, the JSP page to render the results. It uses the JavaServer Pages Standard Tag Library (JSTL) to iterate over the collection of Employee instances and display the name of each employee as well as the name of the department to which that employee is assigned. The employees variable accessed by the <c:forEach/> tag is the List of Employee instances that was placed in the request map by the servlet.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
  <head>
    <title>All Employees</title>
  </head>
  <body>
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Department</th>
        </tr>
      </thead>
      <tbody>
        <c:forEach items="${employees}" var="emp">
          <tr>
            <td><c:out value="${emp.name}"/></td>
            <td><c:out value="${emp.department.name}"/></td>
          </tr>
        </c:forEach>
      </tbody>
    </table>
  </body>
</html>
Listing 6-26

JSP Page to Display Employee Information

The findAll() method of the EmployeeService bean has no transaction. Because the servlet invoking the method has not started a transaction, findAll() is invoked in no transaction context; thus, the results of the query become detached before they are returned to the servlet.

This causes a problem. In this example, the department relationship of the Employee class has been configured to use lazy fetching. As you learned previously in the section on detachment, the only portable thing to do is leave them alone. In this example, however, we don’t want to leave them alone. In order to display the department name for the employee, the JSP expression navigates to the Department entity from the Employee entity. Because this is a lazy-loading relationship, the results are unpredictable. It might work, but then again it might not.

This scenario forms the basis of our challenge. In the following sections, we look at a number of strategies to either prepare the entities needed by the JSP page for detachment or avoid detachment altogether.

Planning for Detachment

Knowing that the results of the findAll() method will be used to display employee information and that the department name will be required as part of this process, we need to ensure that the department relationship of the Employee entity has been resolved before the entities become detached. There are several strategies that can be used to resolve lazy-loaded associations in preparation for detachment. We discuss two of them here, focusing on how to structure application code to plan for detachment. A third strategy, for Jakarta Persistence QL queries called fetch joins, is discussed in Chapter 8, and a fourth, using entity graphs is explained in Chapter 11.

Triggering Lazy Loading
The first strategy to consider in resolving lazy-loading associations is to simply trigger the lazy-loading behavior by accessing the field or relationship. It looks slightly odd in code because the return values of the getter methods are discarded, but nevertheless it has the desired effect. Listing 6-27 shows an alternate implementation of the findAll() method of the EmployeeService session bean. In this case, we iterate over the Employee entities, triggering the department relationship before returning the original list from the method. Because findAll() is executed inside of a transaction, the getDepartment() call completes successfully, and the Department entity instance is guaranteed to be available when the Employee instance is detached.
@Stateless
public class EmployeeService {
    @PersistenceContext(unitName="EmployeeService")
    private EntityManager em;
    public List findAll() {
        List<Employee> emps = (List<Employee>)
            em.createQuery("SELECT e FROM Employee e")
              .getResultList();
        for (Employee emp : emps) {
            Department dept = emp.getDepartment();
            if (dept != null) {
                dept.getName();
            }
        }
        return emps;
    }
    // ...
}
Listing 6-27

Triggering a Lazy-Loading Relationship

One thing that might look odd from Listing 6-27 is that we not only invoked getDepartment() on the Employee instance, but we also invoked getName() on the Department instance. If you recall from Chapter 4, the entity returned from a lazy-loading relationship can actually be a proxy that waits until a method is invoked on the proxy before the entity is faulted in. We have to invoke a method on the entity to guarantee that it is actually retrieved from the database. If this were a collection-valued relationship, the size() method of the Collection would be commonly used to force eager loading.

If lazy-loading basic mappings were used on either the Employee or Department entities, those attributes would not be guaranteed to be present after detachment as well. This is another reason why configuring basic mappings to use lazy loading is not recommended. Developers often expect that a relationship is not eagerly loaded but can be caught off guard if a basic state field such as the name attribute of the Employee instance is missing.

Configuring Eager Loading

When an association is continuously being triggered for detachment scenarios, at some point it is worth revisiting whether the association should be lazy loaded in the first place. Carefully switching some relationships to eager loading can avoid a lot of special cases in code that attempt to trigger the lazy loading.

In this example, Employee has a many-to-one relationship with Department. The default fetch type for a many-to-one relationship is eager loading, but the class was modeled by explicitly using lazy loading. By removing the LAZY fetch type from the department relationship or by specifying the EAGER fetch type explicitly, we ensure that the Department instance is always available to the Employee instance.

Collection-valued relationships lazy load by default, so the EAGER fetch type must be explicitly applied to those mappings if eager loading is desired. Be judicious in configuring collection-valued relationships to be eagerly loaded, however, because it might cause excessive database access in cases where detachment is not a requirement.

Avoiding Detachment

The only complete solution to any detachment scenario is not to detach at all. If your code methodically triggers every lazy-loaded relationship or has marked every association on an entity to be eagerly loaded in anticipation of detachment, this is probably a sign that an alternative approach is required.

Avoiding detachment boils down to just two approaches. Either you don’t work with entities in your JSP page or you must keep a persistence context open for the duration of the JSP rendering process so that lazy-loading relationships can be resolved.

Not using entities means copying entity data into a different data structure that does not have the same lazy-loading behavior. One approach would be to use the Transfer Object3 pattern, but that seems highly redundant given the POJO nature of entities. A better approach, which we discuss in Chapters 7 and 8, is to use projection queries to retrieve only the entity state that will be displayed on the JSP page instead of retrieving full entity instances.

Keeping a persistence context open requires additional planning but allows the JSP page to work with entity data using the JavaBean properties of the entity class. In practical terms, keeping a persistence context open means that there is either an active transaction for entities fetched from transaction-scoped persistence contexts or that an application-managed or extended persistence context is in use. This obviously isn’t an option when entities must be serialized to a separate tier or remote client, but it suits the web application scenario described earlier. We cover each of these strategies here.

Transaction View

The persistence context created by a transaction-scoped entity manager remains open only as long as the transaction in which it was created has not ended. Therefore, in order to use a transaction-scoped entity manager to execute a query and be able to render the query results while resolving lazy-loading relationships, both operations must be part of the same transaction. When a transaction is started in the web tier and includes both session bean invocation and JSP page rendering before it is committed, we call this pattern a Transaction View.

The benefit of this approach is that any lazy-loading relationships encountered during the rendering of the view will be resolved because the entities are still managed by a persistence context. To implement this pattern in our example scenario, we start a bean-managed transaction before the findAll() method is invoked and commit the transaction after the JSP page has rendered the results. Listing 6-28 demonstrates this approach. Note that to save space, we have omitted the handling of the checked exceptions thrown by the UserTransaction operations. The commit() method alone throws no fewer than six checked exceptions.
public class EmployeeServlet extends HttpServlet {
    @Resource UserTransaction tx;
    @EJB EmployeeService bean;
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // ...
        try {
            tx.begin();
            List emps = bean.findAll();
            request.setAttribute("employees", emps);
            getServletContext().getRequestDispatcher("/listEmployees.jsp")
                               .forward(request, response);
        } finally {
            tx.commit
        }
        // ...
    }
}
Listing 6-28

Combining a Session Bean Method and JSP in a Single Transaction

With this solution in place, the lazy-loading relationships of the Employee entity do not have to be eagerly resolved before the JSP page renders the results. The only downside to this approach is that the servlet must now manage transactions and recover from transaction failures. A lot of logic also has to be duplicated between all the servlet controllers that need this behavior.

One way to work around this duplication is to introduce a common superclass for servlets that use the Transaction View pattern that encapsulates the transaction behavior. If, however, you are using the Front Controller pattern and controller actions are implemented using the Command4 pattern, this might become more difficult to manage, particularly if the page flow is complex and multiple controllers collaborate to build a composite view. Then, not only does each controller need to start transactions, but it also needs to be aware of any transactions that were started earlier in the rendering sequence.

Another possible, though nonportable, solution is to move the transaction logic into a servlet filter. It allows us to intercept the HTTP request before the first controller servlet is accessed and wrap the entire request in a transaction. Such coarse-grained use of transactions is something that needs to be managed carefully, however. If applied to all HTTP requests equally, it might also cause trouble for requests that involve updates to the database. Assuming that these operations are implemented as session beans, the REQUIRES_NEW transaction attribute might be required in order to isolate entity updates and handle transaction failure without impacting the overriding global transaction.

Entity Manager per Request

For applications that do not encapsulate their query operations behind session bean façades, an alternative to the Transaction View pattern is to create a new application-managed entity manager to execute reporting queries, closing it only after the JSP page has been rendered. Because the entities returned from the query on the application-managed entity manager will remain managed until the entity manager is closed, it offers the same benefits as the Transaction View pattern without requiring an active transaction.

Listing 6-29 revisits our EmployeeServlet servlet again, this time creating an application-managed entity manager to execute the query. The results are placed in the map as before, and the entity manager is closed after the JSP page has finished rendering.
public class EmployeeServlet extends HttpServlet {
    @PersistenceUnit(unitName="EmployeeService")
    EntityManagerFactory emf;
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws ServletException, IOException {
        EntityManager em = emf.createEntityManager();
        try {
            List emps = em.createQuery("SELECT e FROM Employee e")
                          .getResultList();
            request.setAttribute("employees", emps);
            getServletContext().getRequestDispatcher("/listEmployees.jsp")
                               .forward(request, response);
        } finally {
            em.close();
        }
    }
}
Listing 6-29

Using an Application-Managed Entity Manager for Reporting

Unfortunately, we now have query logic embedded in our servlet implementation. The query is also no longer reusable the way it was when it was part of a stateless session bean. There are a couple of other options we can explore as a solution to this problem. Instead of executing the query directly, we could create a POJO service class that uses the application-managed entity manager created by the servlet to execute queries. This is similar to the first example we created in Chapter 2. We gain the benefit of encapsulating the query behavior inside business methods while being decoupled from a particular style of entity manager.

Alternatively we can place our query methods on a stateful session bean that uses an extended entity manager. When a stateful session bean uses an extended entity manager, its persistence context lasts for the lifetime of the session bean, which ends only when the user invokes a remove method on the bean. If a query is executed against the extended persistence context of a stateful session bean, the results of that query can continue to resolve lazy-loading relationships as long as the bean is still available.

Let’s explore this option and see how it would look instead of the application-managed entity manager we showed in Listing 6-29. Listing 6-30 introduces a stateful session bean equivalent to the EmployeeService stateless session bean that we have been using so far. In addition to using the extended entity manager, we have also set the default transaction type to be NOT_SUPPORTED. There is no need for transactions because the results of the query will never be modified, only displayed.
@Stateful
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class EmployeeQuery {
    @PersistenceContext(type=PersistenceContextType.EXTENDED,
                        unitName="EmployeeService")
    EntityManager em;
    public List<Employee> findAll() {
        return em.createQuery("SELECT e FROM Employee e", Employee.class)
                 .getResultList();
    }
    // ...
    @Remove
    public void finished() {
    }
}
Listing 6-30

Stateful Session Bean with Query Methods

Using this bean is very similar to using the application-managed entity manager. We create an instance of the bean, execute the query, and then remove the bean when the JSP page has finished rendering. Listing 6-31 shows this approach.
@EJB(name="queryBean", beanInterface=EmployeeQuery.class)
public class EmployeeServlet extends HttpServlet
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        EmployeeQuery bean = createQueryBean();
        try {
            List<Employee> emps = bean.findAll();
            request.setAttribute("employees", emps);
            getServletContext().getRequestDispatcher("/listEmployees.jsp")
                               .forward(request, response);
        } finally {
            bean.finished();
        }
    }
    private EmployeeQuery createQueryBean() throws ServletException {
        // look up queryBean
        // ...
    }
}
Listing 6-31

Using an Extended Entity Manager for Reporting

At first glance this might seem like an overengineered solution. We gain the benefit of decoupling queries from the servlet, but we have introduced a new session bean just to accomplish this goal. Furthermore, we are using stateful session beans with very short lifetimes. Doesn’t that go against the accepted practice of how to use a stateful session bean?

To a certain extent, this is true, but the extended persistence context invites us to experiment with new approaches. In practice, stateful session beans do not add a significant amount of overhead to an operation, even when used for short durations. As you will see later in the “Edit Session” section, moving the stateful session bean to the HTTP session instead of limiting it to a single request also opens up new possibilities for web application design.

Merge Strategies

Creating or updating information is a regular part of most enterprise applications. Users typically interact with an application via the Web, using forms to create or change data as required. The most common strategy to handle these changes in a Jakarta EE application that uses Jakarta Persistence is to place the results of the changes into detached entity instances and merge the pending changes into a persistence context so that they can be written to the database.

Let’s revisit our simple web application scenario again. This time, instead of simply viewing Employee information, the user can select an Employee and update basic information about that employee. The entities are queried for presentation in a form in one request and then updated in a second request when the user submits the form with changes entered.

Using a Session Façade pattern, this operation is straightforward. The changed entity is updated and handed off to a stateless session bean to be merged. The only complexity involved is making sure that relationships properly merge by identifying cases where the MERGE cascade setting is required.

Similar to the question of whether we can avoid detaching entities to compensate for lazy-loading concerns, the long-lived nature of application-managed and extended persistence contexts suggests that there might also be a way to apply a similar technique to this situation. Instead of querying entities in one HTTP request and throwing the entity instances away after the view has been rendered, we want to keep these entities around in a managed state so that they can be updated in a subsequent HTTP request and persisted merely by starting and committing a new transaction.

In the following sections, we revisit the traditional Session Façade approach to merging and then look at new techniques possible with the extended entity manager that will keep entities managed for the life of a user’s editing session.

Session Façade
To use a Session Façade pattern to capture changes to entities, we provide a business method that will merge changes made to a detached entity instance. In our example scenario, this means accepting an Employee instance and merging it into a transaction-scoped persistence context. Listing 6-32 shows an implementation of this technique in our EmployeeService session bean.
@Stateless
public class EmployeeService {
    @PersistenceContext(unitName="EmployeeService")
    private EntityManager em;
    public void updateEmployee(Employee emp) {
        if (em.find(Employee.class, emp.getId()) == null) {
            throw new IllegalArgumentException("Unknown employee id: " +
                                               emp.getId());
        }
        em.merge(emp);
    }
    // ...
}
Listing 6-32

Business Method to Update Employee Information

The updateEmployee() method in Listing 6-32 is straightforward. Given the detached Employee instance, it first attempts to check whether a matching identifier already exists. If no matching Employee is found, an exception is thrown because we don’t want to allow new Employee records to be created. Then, we use the merge() operation to copy the changes into the persistence context, which are then saved when the transaction commits.

Using the façade from a servlet is a two-step approach. During the initial HTTP request to begin an editing session, the Employee instance is queried (typically using a separate method on the same façade) and used to create a web form on which the user can make desired changes. The detached instance is then stored in the HTTP session so it can be updated when the user submits the form from the browser. We need to keep the detached instance around in order to preserve any relationships or other state that will remain unchanged by the edit. Creating a new Employee instance and supplying only partial values could have many negative side effects when the instance is merged .

Listing 6-33 shows an EmployeeUpdateServlet servlet that collects the ID, name, and salary information from the request parameters and invokes the session bean method to perform the update. The previously detached Employee instance is retrieved from the HTTP session, and then the changes indicated by the request parameters are set into it. We have omitted validation of the request parameters to conserve space, but ideally this should happen before the business method on the session bean is invoked.
public class EmployeeUpdateServlet extends HttpServlet {
    @EJB EmployeeService bean;
    protected void doPost(HttpServletRequest request,
                          HttpServletResponse response)
            throws ServletException, IOException {
        int id = Integer.parseInt(request.getParameter("id"));
        String name = request.getParameter("name");
        long salary = Long.parseLong(request.getParameter("salary"));
        HttpSession session = request.getSession();
        Employee emp = (Employee) session.getAttribute("employee.edit");
        emp.setId(id);
        emp.setName(name);
        emp.setSalary(salary);
        bean.updateEmployee(emp);
        // ...
    }
}
Listing 6-33

Using a Session Bean to Perform Entity Updates

If the amount of information being updated is very small, we can avoid the detached object and merge() operation entirely by locating the managed version and manually copying the changes into it. Consider the following example:
public void updateEmployee(int id, String name, long salary) {
    Employee emp = em.find(Employee.class, id);
    if (emp == null) {
        throw new IllegalArgumentException("Unknown employee id: " + id);
    }
    emp.setEmpName(name);
    emp.setSalary(salary);
}

The beauty of this approach is its simplicity, but that is also its primary limitation. Typical web applications today offer the ability to update large amounts of information in a single operation. To accommodate these situations with this pattern, there would either have to be business methods taking large numbers of parameters or many business methods that would have to be invoked in sequence to completely update all the necessary information. And, of course, once you have more than one method involved, it may be necessary to maintain a transaction across all the update methods so that the changes are committed as a single unit.

As a result, despite the availability of this approach, the web tier still commonly collects changes into detached entities or transfer objects and passes the changed state back to session beans to be merged and written to the database .

Edit Session

With the introduction of the extended entity manager, we can take a different approach to building web applications that update entities. As we have discussed in this chapter, entities associated with an extended entity manager remain managed as long as the stateful session bean holding the extended entity manager is not removed. By placing a stateful session bean in a central location such as the HTTP session, we can operate on entities managed by the extended entity manager without having to merge in order to persist changes. We refer to this as the Edit Session pattern to reflect the fact that the primary goal of this pattern is to encapsulate editing use cases using stateful session beans.

Listing 6-34 introduces a stateful session bean that represents an employee editing session. Unlike the EmployeeService session bean that contains a number of reusable business methods, this style of stateful session bean is targeted to a single application use case. In addition to using the extended entity manager, we have also set the default transaction type to be NOT_SUPPORTED with the exception of the save() method. There is no need for transactions for methods that simply access the Employee instance because those methods only operate in memory. It is only when we want to persist the changes to the database that we need a transaction, and that only happens in the save() method.
@Stateful
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
public class EmployeeEdit {
    @PersistenceContext(type=PersistenceContextType.EXTENDED,
                        unitName="EmployeeService")
    EntityManager em;
    Employee emp;
    public void begin(int id) {
        emp = em.find(Employee.class, id);
        if (emp == null) {
            throw new IllegalArgumentException("Unknown employee id:" + id);
        }
    }
    public Employee getEmployee() {
        return emp;
    }
    @Remove
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void save() {}
    @Remove
    public void cancel() {}
}
Listing 6-34

Stateful Session Bean to Manage an Employee Editing Session

Let’s start putting the operations of the EmployeeEdit bean in context. When the HTTP request arrives and starts the editing session, we create a new EmployeeEdit stateful session bean and invoke begin() using the ID of the Employee instance that will be edited. The session bean then loads the Employee instance and caches it on the bean. The bean is then bound to the HTTP session so that it can be accessed again in a subsequent request once the user has changed the Employee information. Listing 6-35 shows the EmployeeEditServlet servlet that handles the HTTP request to begin a new editing session.
@EJB(name="EmployeeEdit", beanInterface=EmployeeEdit.class)
public class EmployeeEditServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        int id = Integer.parseInt(request.getParameter("id"));
        EmployeeEdit bean = getBean();
        bean.begin(id);
        HttpSession session = request.getSession();
        session.setAttribute("employee.edit", bean);
        request.setAttribute("employee", bean.getEmployee());
        getServletContext().getRequestDispatcher("/editEmployee.jsp")
                           .forward(request, response);
    }
    public EmployeeEdit getBean() throws ServletException {
        // lookup EmployeeEdit bean
        // ...
    }
}
Listing 6-35

Beginning an Employee Editing Session

Now let’s look at the other half of the editing session, in which we wish to commit the changes. When the user submits the form that contains the necessary Employee changes, the EmployeeUpdateServlet is invoked. It begins by retrieving the EmployeeEdit bean from the HTTP session. The request parameters with the changed values are then copied into the Employee instance obtained from calling getEmployee() on the EmployeeEdit bean. If everything is in order, the save() method is invoked to write the changes to the database. Listing 6-36 shows the EmployeeUpdateServlet implementation. Note that we need to remove the bean from the HTTP session once the editing session has completed.
public class EmployeeUpdateServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String name = request.getParameter("name");
        long salary = Long.parseLong(request.getParameter("salary"));
        HttpSession session = request.getSession();
        EmployeeEdit bean = (EmployeeEdit)         session.getAttribute("employee.edit");
        session.removeAttribute("employee.edit");
        Employee emp = bean.getEmployee();
        emp.setName(name);
        emp.setSalary(salary);
        bean.save();
        // ...
    }
}
Listing 6-36

Completing an Employee Editing Session

The pattern for using stateful session beans and extended entity managers in the web tier is as follows:
  1. 1.

    For each application use case that modifies entity data, we create a stateful session bean with an extended persistence context. This bean will hold onto all entity instances necessary to make the desired changes.

     
  2. 2.

    The HTTP request that initiates the editing use case creates an instance of the stateful session bean and binds it to the HTTP session. The entities are retrieved at this point and used to populate the web form for editing.

     
  3. 3.

    The HTTP request that completes the editing use case obtains the previously bound stateful session bean instance and writes the changed data from the web form into the entities stored on the bean. A method is then invoked on the bean to commit the changes to the database.

     

In our simple editing scenario, this might seem somewhat excessive, but it can scale to accommodate editing sessions of any complexity. Department, Project, and other information can all be edited in one or even multiple sessions with the results accumulated on the stateful session bean until the application is ready to persist the results.

Another benefit is that web application frameworks such as Jakarta Faces can directly access the bean bound in the HTTP session from within JSP pages. The entity can be accessed both to display the form for editing and as the target of the form when the user submits the results. In this scenario, the developer only has to ensure that the necessary save and cancel methods are invoked at the correct point in the application page flow.

There are a couple of other points that we need to mention about this approach. Once bound to the HTTP session, the session bean will remain there until it is explicitly removed or until the HTTP session expires. It is therefore important to ensure that the bean is removed once the editing session is complete, regardless of whether the changes will be saved or abandoned. The HttpSessionBindingListener callback interface can be used by applications to track when the HTTP session is destroyed and clean up corresponding session beans appropriately.

The HTTP session is not thread-safe, and neither are stateful session bean references. In some circumstances, it might be possible for multiple HTTP requests from the same user to access the HTTP session concurrently. This is mostly an issue when requests take a long time to process and an impatient user refreshes the page or abandons her editing session for another part of the web application. In these circumstances, the web application will either have to deal with possible exceptions occurring if the stateful session bean is accessed by more than one thread or proxy the stateful session bean with a synchronized wrapper.

Conversation

The last strategy is a kind of extension/improvement to the edit session approach, but that engages the unsynchronized persistence context described earlier in the chapter. CDI can also be used to manage the bean instance within our session scope.

As we said in Chapter 1, Jakarta Persistence version allows attribute converters to support CDI injection into AttributeConverter classes. With this feature the persistence provider may support CDI injection into attribute converters in other environments in which the BeanManager is available.

The persistence provider is responsible for
  • Using the CDI SPI to create instances of the attribute converter class

  • Performing injection upon such instances to invoke their PostConstruct and PreDestroy methods

  • Disposing of the attribute converter instances

Notice that the persistence provider is only required to support CDI injection into attribute converters in Jakarta EE container environments. In case the CDI is not enabled, the persistence provider must not invoke attribute converters that depend on CDI injection.

Note

CDI and Jakarta Faces provide support for a dedicated conversation scope, which allows navigating across multiple views and client pages. We refer readers to books dedicated to CDI and Jakarta Faces to find out more about the conversation scope and how Jakarta Faces and CDI interact with each other when using this scope.

The conversation pattern is going to be useful not only when you want to edit multiple objects, or even multiple object types, but when you don’t necessarily want the session bean to have to cache or keep track of all of the things that have been edited throughout the conversation. Any entities that have been edited by the client are managed by the Jakarta Persistence persistence context directly.

In Listing 6-37, the stateful session bean provides a load method for the servlet to load an employee and give the user an opportunity to make changes to that employee. The user can then save the changes or cancel out of those individual employee changes. This process can be repeated an arbitrary number of times until the user decides to either accept the changes to all of the employees or abandon them. If accepted, the changes are joined to the transaction that is started as part of the processAllChanges() method and committed when the method and the transaction complete.
@Stateful
@SessionScoped
public class EmployeeService {
    @PersistenceContext(type=PersistenceContextType.EXTENDED,
                        synchronization=SynchronizationType.UNSYNCHRONIZED,
                        unitName="EmployeeService")
    EntityManager em;
    Employee currentEmployee;
    public Employee getCurrentEmployee() { return currentEmployee; }
    public Employee loadEmployee(int id) {
        Employee emp = em.find(Employee.class, id);
        currentEmployee = emp;
        return emp;
    }
    public void saveChangesToEmployee() {}
    public void cancel() {
        em.detach(currentEmployee);
    }
    public void processAllChanges() {
        em.joinTransaction();
    }
    public void abandonAllChanges() {
        em.clear();
    }
}
Listing 6-37

Stateful Session Bean with Unsynchronized Persistence Context

Looking at the saveChangesToEmployee() method , you may think that some code is missing since the method appears to do nothing. The reason there is no code in the method is because it does not actually need to do anything. The loadEmployee() method returns the managed employee from the persistence context so the employee being modified at the presentation layer is already being tracked by the persistence provider. By contrast, if the user were to cancel the changes, we merely detach the object from the persistence context to ensure that those changes will not be committed.

When the user decides to end the conversation by accepting all of the changes, the processAllChanges() method does the transaction joining. If the user decides to end it by discarding all of the changes, the abandonAllChanges() method clears all of the objects from the persistence context, and the conversation can start again.

Before we leave this example, we make one last comment about the session scope that we used. We used CDI and scoped the EmployeeService bean to the session so it will be available to the requesting session-scoped thread. As a result, we will have leftover entities in the persistence context when a conversation gets committed, since the context is only cleared when the changes are abandoned. This is okay as long as the session does not carry on excessively. If the session is very long-running, or the entities in different conversations never overlap, the persistence context should probably be cleared. However, if you are tempted to simply add a call to em.clear() in the processAllChanges() method after the joinTransaction() call, you would be in for an unpleasant surprise. If the changes were cleared out before the processAllChanges() method has completed, then when the container-managed transaction commits, at the end of that method, there will be no changes in the persistence context to commit! A correct solution would be to clear the persistence context at the beginning of the next conversation instead.

Summary

In this chapter, we presented a thorough treatment of the entity manager and its interactions with entities, persistence contexts, and transactions. As you have seen, the entity manager can be used in many different ways to accommodate a wide variety of application requirements.

We began by reintroducing the core terminology of Jakarta Persistence and explored the persistence context. We then covered the different types of entity manager: transaction-scoped, extended, and application-managed. We looked at how to acquire and use each type and the types of problems they are designed to solve.

In the transaction management section, we looked at each of the entity manager types and how they relate to container-managed Jakarta Transactions transactions and the resource-local transactions of the JDBC driver. We illustrated why transactions play an important role in all aspects of enterprise application development with Jakarta Persistence. We showed a special kind of persistence context that remains unsynchronized with the Jakarta Transactions transaction until it is programmatically joined, and how it can bring flexibility for longer-running interaction sequences.

Next, we revisited the basic operations of the entity manager, this time armed with the full understanding of the different entity manager types and transaction-management strategies. We introduced the notion of cascading and looked at the impact of relationships on persistence.

In our discussion of detachment, we introduced the problem and looked at it both from the perspective of mobile entities to remote tiers and the challenge of merging offline entity changes back into a persistence context. We presented several strategies to minimize the impact of detachment and merging on application design by adopting design patterns specific to Jakarta Persistence.

In the next chapter, we turn our attention to the query facilities of Jakarta Persistence, showing how to create, execute, and work with the results of query operations.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.63.87