ProJPA-Skripta-Napredne softverske tehnologije-Informatika_2, Skripte' predlog Programiranje složenih softverskih sistema
dcplover
dcplover

ProJPA-Skripta-Napredne softverske tehnologije-Informatika_2, Skripte' predlog Programiranje složenih softverskih sistema

150 str.
583broj poseta
Opis
ProJPA,Skripta,Napredne softverske tehnologije,Informatika, Enterprise Applications,Object-Relational Mapping,Collection Mapping, Entity Manager,Using Queries, Query Language, Criteria API, Advanced Object-Relational Mapping, Advanced Topics, XML Mapping Files,Packaging and Deployment, Testing,Migration
20 poeni
poeni preuzimanja potrebni da se preuzme
ovaj dokument
preuzmi dokument
pregled3 str. / 150
ovo je samo pregled
3 prikazano na 150 str.
ovo je samo pregled
3 prikazano na 150 str.
ovo je samo pregled
3 prikazano na 150 str.
ovo je samo pregled
3 prikazano na 150 str.

CHAPTER 5 ■ COLLECTION MAPPING

123

@AttributeOverride(name="key.last_Name", column=@Column(name="EMP_LNAME")) }) private Map<EmployeeName, EmployeeInfo> empInfos;

Keying by Entity You might be reluctant to use entities as keys because intuition might lead you to think of this as a more resource-intensive option, with higher load and management costs. While that might be true in some cases, it is not necessarily always so. Quite often the entity you are considering keying on will already be in memory, or needed anyway, and keying on it either just accesses a cached instance or causes the instance to be loaded for later use.

One advantage of keying by entity type is that entity instances are globally unique (within the persistence unit) so there will not be any identity problems to deal with across different relationships or collections. A corollary of the basic identity property of entities is that only a foreign key needs to be stored in the mapped table, leading to a more normalized design and data storage schema.

As with the other types of Map keys, the key (in this case, a foreign key to the entity being keyed on) will be stored in the table referred to by the mapping.

Recall that the term used by JPA to represent a foreign key column is join column, and we use join columns in many-to-one and one-to-one relationships, as well as in join tables of collection-valued relationships. We now have a similar situation, except that instead of referring to the target of a relationship, our join column is referring to the entity key in a Map entry. To differentiate join columns that point to map keys from the ones used in relationships, a separate @MapKeyJoinColumn annotation was created. This annotation is used to override the join column defaults for an entity key. When it is not specified, the join column will have the same default column name as basic keys (the name of the relationship or element collection attribute, appended with the string “_KEY”).

To illustrate the case of an entity being used as a key, we can add the notion of the seniority an Employee has within a given Department. We want to have a loose association between an Employee and his seniority; and the seniority has to be local to a Department. By defining an element collection Map in Department, with the seniority as the values and Employee entities as the keys, the seniority any Employee has in a given Department can be looked up by using the Employee instance as a key. The seniority is stored in a collection table, and if an Employee changes departments none of the other Employee objects needs to change. The indirection of the collection table; and the fact that the connections between the Department, the Employee and the seniority value are all maintained by virtue of the Map, provide just the right level of coupling. Only the entries in the collection table would need to be updated.

Listing 5-18 shows the element collection mapping, with the join column being overridden using the @MapKeyJoinColumn annotation and the Map value column being overridden using the standard @Column annotation.

Listing 5-18. Element Collection Map Keyed by EntityType

@Entity public class Department { @Id private int id; private String name; // ... @ElementCollection @CollectionTable(name="EMP_SENIORITY") @MapKeyJoinColumn(name="EMP_ID") @Column(name="SENIORITY") private Map<Employee, Integer> seniorities; // ...

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

124

}

Figure 5-7 shows that the collection table is nothing more than the values of the Map (the seniority) with a foreign key to the Department source entity table and another foreign key to the Employee entity key table.

Figure 5-7. DEPARTMENT and EMPLOYEE entity tables with EMP_SENIORITY collection table

Untyped Maps If we did not want to (or were not able to) use the typed parameter version of Map<KeyType, ValueType>, we would define it using the non-parameterized style of Map shown in Listing 5-19.

Listing 5-19. One-to-Many Relationship Using a Non-parameterized Map

@Entity public class Department { // ... @OneToMany(targetEntity=Employee.class, mappedBy="department") @MapKey private Map employees; // ... }

The targetEntity element only ever indicates the type of the Map value. Of course, if the Map holds an element collection and not a relationship, the targetClass element of @ElementCollection is used to indicate the value type of the Map.

In Listing 5-19, the type of the Map key can be easily deduced because the mapping is an entity relationship. The @MapKey default is to use the identifier attribute, id, of type int or Integer. If @MapKey had been specified and dictated that the key be an attribute that was not an identifier attribute, the type would still have been deducible because the entity attributes are all mapped with known types. However, if the key isn’t an attribute of the target entity, @MapKeyClass might be used instead of @MapKey. It indicates the type of the key class when the Map is not defined in a typed way using generics. It is also used when the Map references an element collection instead of a relationship because basic or embeddable types do not have identifier attributes, and basic types do not even have attributes.

To illustrate how @MapKeyClass is used, let’s take the element collection in Listing 5-7 and assume that it does not define the type parameters on the Map. The typing is filled in through the use of the @MapKeyClass annotation and the targetClass element in @ElementCollection, as shown in Listing 5-20.

Listing 5-20. Untyped Element Collection of Strings with String Keys

@Entity public class Employee { @Id private int id;

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

125

private String name; private long salary; @ElementCollection(targetClass=String.class) @CollectionTable(name="EMP_PHONE") @MapKeyColumn(name="PHONE_TYPE") @MapKeyClass(String.class) @Column(name="PHONE_NUM") private Map phoneNumbers; // ... }

The @MapKeyClass annotation should be used whenever the key class cannot be deduced from the attribute definition or the other mapping metadata.

Rules for Maps Learning about the various Map variants can get kind of confusing given that we can choose any one of three different kinds of key types and three different kinds of value types. Below are some of the basic rules of using a Map.

• Use the @MapKeyClass and targetEntity/targetClass elements of the relationship and element collection mappings to specify the classes when an untyped Map is used.

• Use @MapKey with one-to-many or many-to-many relationship Map that is keyed on an attribute of the target entity.

• Use @MapKeyJoinColumn to override the join column of the entity key.

• Use @Column to override the column storing the values of an element collection of basic types.

• Use @MapKeyColumn to override the column storing the keys when keyed by a basic type.

• Use @MapKeyTemporal and @MapKeyEnumerated if you need to further qualify a basic key that is a temporal or enumerated type.

• Use @AttributeOverride with a “key.” or “value.” prefix to override the column of an embeddable attribute type that is a Map key or a value, respectively.

Table 5-1 summarizes some of the different aspects of using a Map.

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

126

Table 5-1. Summary of Mapping a Map

Map Mapping Key Annotation Value Annotation

Map<Basic,Basic> @ElementCollection @MapKeyColumn,

@MayKeyEnumerated,

@MapKeyTemporal

@Column

Map<Basic,Embeddable> @ElementCollection @MapKeyColumn,

@MayKeyEnumerated,

@MapKeyTemporal

Mapped by embeddable,

@AttributeOverride,

@AssociationOverride

Map<Basic,Entity> @OneToMany, @ManyToMany @MapKey,

@MapKeyColumn,

@MayKeyEnumerated,

@MapKeyTemporal

Mapped by entity

Map<Embeddable,Basic> @ElementCollection Mapped by embeddable,

@AttributeOverride

@Column

Map<Embeddable,Embeddable> @ElementCollection Mapped by embeddable,

@AttributeOverride

Mapped by embeddable,

@AttributeOverride,

@AssociationOverride

Map<Embeddable,Entity> @OneToMany, @ManyToMany Mapped by embeddable Mapped by entity

Map<Entity,Basic> @ElementCollection @MapKeyJoinColumn @Column

Map<Entity,Embeddable> @ElementCollection @MapKeyJoinColumn Mapped by embeddable,

@AttributeOverride,

@AssociationOverride

Map<Entity,Entity> @OneToMany, @ManyToMany @MapKeyJoinColumn Mapped by entity

Duplicates When we were discussing the Set interface we mentioned that it was ideal for preventing duplicates. What we meant was that the Set datatype in Java does not allow them. We didn’t really say anything

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

127

about duplicates in the database. In fact, the JPA specification does not say anything about whether duplicates are allowed in collections, either in the database or in memory, and in most cases they will not be supported. To get a feeling for why supporting duplicates is difficult, we need to go to the data model and uncover some of the gory details. You might prefer to skip this section if duplicates are not interesting to you. Read on, however, if you are in a situation where an external application can come in behind your back and insert a duplicate record, for example.

The Collection interface is very general and allows for a multitude of Collection subinterfaces and implementation classes. So it is very soft in its specification of whether duplicates are allowed, instead allowing the subinterface or implementation class to decide what behavior best fits that Collection type.

For a Collection that does happen to allow duplicates, collection-valued relationship mappings use either a foreign key in the target entity table or a join table. The first case will always be a one-to-many mapping, and in that case there will be only one row for the target entity, and only one column in that row to contain the foreign key to the source entity. That leaves no way to capture the fact that the target entity is in the collection more than once.

In the join table case, each row stores a join column to the source entity and a join column to the target entity, and the primary key of the join table is composed of the combination of the two. Only duplicate rows in that model could link multiple instances of the target to the same source, and duplicate rows in a relational database are highly frowned upon.

An element collection is in a similar situation, except that instead of a foreign key to the target entity there are one or more columns in the collection table storing the basic or embeddable values. These columns just combine with the foreign key to the source entity to make up the primary key, and once again duplicate rows would be required to have duplicate values in a collection.

The persistently ordered List is a little different, however, because it adds an order column to the mix. If the order column were to be included as part of the primary key, multiple relationship entries could exist in the List each of their respective rows potentially having the same element value data and foreign key reference, but differing only by the value of the order column. Thus, the uniqueness of a row is identified not only by the source and target objects but also by its position in the List.

In the case of the foreign key in the target table, it would be bad practice indeed to include the order column in the primary key of an entity table, so we won’t even explore that as an option. However, when a join table or collection table is used, it is a perfectly reasonable thing to do, allowing duplicate values to be inserted in a persistently ordered List. This is possible, though, only if the provider includes the order column in the primary key of the table or gives you the option of configuring it in that way.

Before you rejoice that your provider might allow you to store duplicates, be aware that there is a price to pay. This can be seen in the example of exchanging the order of two elements in the List. If the order column were just a regular column, it wouldn’t be too much of a stretch for the provider to optimize the database operations by simply updating the order columns of the two records with the correct values. However, if the order column is part of the primary key, what you are really saying is that the ordering of the contained object in the List is an integral part of the relationship between the containing and contained objects. Assigning a new order to the contained object is not modifying an aspect of the relationship, but effectively destroying the relationship and creating a new one with a different ordering. That means deleting the two old rows and creating two new ones.

A Map has keys that must be unique, so duplicate keys clearly don’t make sense. It is similar to a List when it comes to its values, however, because it has a key column that can be part of the primary key. Once again, the case of the foreign key in the target table does not allow for multiple keys to point to the same entity, so one-to-many relationships that are mapped that way simply do not permit the same entity to be mapped to multiple keys in the same Map.

For join tables and collection tables, duplicates will be possible in the Map only if it is keyed by something other than an attribute of the entity, or embeddable, and the key column is included in the primary key. The trade-off in the case of the Map is similar to the one that we discussed with List, except that the price of allowing duplicates in a Map is paid when you want to reassign an existing key to a different value.

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

128

Null Values It is probably even less common to insert null values in a collection than it is to have duplicates. This is one reason why the JPA specification is not particularly clear on what happens when you insert null into a collection. As with duplicates, the cases are a little complex and require individual consideration.

The Set, List, and Map interfaces join the Collection interface in being general enough to be wishy- washy when it comes to specifying what happens when null is inserted. They simply delegate the decision to the implementation, so an implementation class might choose to support inserting null values or simply throw an exception. JPA does no better; it ends up falling to the particular vendor’s proxy implementation to allow null or throw a NullPointerException when null is added. Note that you cannot make your collection interface allow null simply by initializing it with an implementation instance, such as HashSet, that allows null. The provider will replace the instance with one of its own implementation classes the next time the object becomes managed, and the new implementation class might or might not allow null values.

In order for a null value to exist in the database the value column or columns must be nullable. This is the obvious part, but the corollary might be less evident, if a little repetitive. It claims once again that only relationships and element collections that use a join table or collection table can have null values in the collection. The proof is left for you to figure out, but (as a hint) try creating an entity that has all null values, including the identifier, with no identifier generation.

There is a further limitation on null values when it comes to element collections of embeddable objects. Entity references in a join table or element collections of basic types in a collection table are single-column values or references. The problem in the embeddable case is that if a combination of columns mapped to an embeddable are all null, there is no way for the provider to know whether it signifies a null value or an empty embeddable object full of null values. Providers might assume that it is an empty embedded object or they might have a controllable option to dictate whether the nulls get treated one way or the other.

Maps are equally non-committal about allowing null keys, but it really doesn’t fit very well with the model of key columns being primary key fields. Most databases do not even allow one of the primary key fields to be nullable, and we would not recommend it even for the odd one that does.

Best Practices With all the options and possibilities that have emerged, we would be cruel indeed if we did not offer at least some measure of guidance to the lonely collections traveler. Of course, the reason why there are so many options is because there are so many different cases to solve, so it is not really appropriate to come up with hard and fast rules. However, some general guidelines will hopefully assist you in picking the right mapping strategy for your specific application use case.

• When using a List, do not assume that it is ordered automatically if you have not actually specified any ordering. The List order might be affected by the database results, which are only partially deterministic with respect to how they are ordered. There are no guarantees that such an ordering will be the same across multiple executions.

• It will generally be possible to order the objects by one of their own attributes. Using the @OrderBy annotation will always be the best approach when compared to a persistent List that must maintain the order of the items within it by updating a specific order column. Use the order column only when it is impossible to do otherwise.

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

129

• Map types are very helpful, but they can be relatively complicated to properly configure. Once you reach that stage, however, the modeling capabilities that they offer and the loose association support that can be leveraged makes them ideal candidates for various kinds of relationships and element collections.

• As with the List, the preferred and most efficient use of a Map is to use an attribute of the target object as a key, making a Map of entities keyed by a basic attribute type the most common and useful. It will often solve most of the problems you encounter. A Map of basic keys and values can be a useful configuration for associating one basic object with another.

• Avoid using embedded objects in a Map, particularly as keys, because their identity is typically not defined. Embeddables in general should be treated with care and used only when absolutely necessary.

• Support for duplicate or null values in collections is not guaranteed, and is not recommended even when possible. They will cause certain types of operations on the collection type to be slower and more database-intensive, sometimes amounting to a combination of record deletion and insertion instead of simple updates.

Summary In this chapter, we took a more in-depth look at various ways of mapping collections to the database. We looked at how the contents of the collection determine how it is mapped, and noted that there are many flexible options for storing different kinds of objects in various types of collections.

We showed that the difference between relationships and element collections was whether entities or basic/embeddable types were being stored in them. We went on to examine the different types of collections, and how Collection and Set can be used for simple container purposes, while List can be used to maintain ordered collections. We saw that there are two different approaches to using a List and that maintaining a persistent List is possible, but not usually the best strategy.

We then elaborated on all the Map types, explaining how combinations of basic, embeddable, and entity types can be used as keys and values. We experimented with and showed examples of using many of the different combinations of key and value types, illustrating how each changed the way the collection was mapped. We then outlined, in list form, the basic rules of using a Map type.

We finished off collections by looking at the corner cases of adding duplicates and null values to collections and outlined the cases when support might be reasonable. Some best practices and practical guidance to using collections followed.

The next chapter will discuss using entity managers and persistence contexts in more advanced ways than we did previously, delving into the practices and nuances of injecting and using them in Java EE and Java SE environments.

Download at WoweBook.Com

CHAPTER 5 ■ COLLECTION MAPPING

130

Download at WoweBook.Com

C H A P T E R 6

■ ■ ■

131

Entity Manager

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. JPA 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 JPA. 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. JPA 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 Java 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 JPA. 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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

132

Entity Managers Up to this point, we have demonstrated only basic entity manager operations in both the Java SE and Java EE environments. We have reached a point, however, where we can finally reveal the full range of entity manager configurations. JPA 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 we will see, the persistence context is just one part of the puzzle.

Container-Managed Entity Managers In the Java 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 JTA 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.

Transaction-Scoped All the entity manager examples that we have shown so far for the Java 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 Java 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.

Listing 6-1. The ProjectService Session Bean

@Stateless public class ProjectServiceBean implements 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); } // ... }

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

133

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 JTA transaction. All container-managed entity managers depend on JTA 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 JTA 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.

Listing 6-2. First Attempt at Department Manager Bean

@Stateful public class DepartmentManagerBean implements DepartmentManager { @PersistenceContext(unitName="EmployeeService") EntityManager em; int deptId; public void init(int deptId) { this.deptId = deptId; }

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

134

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() { } }

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.

Listing 6-3. Second Attempt at Department Manager Bean

@Stateful public class DepartmentManagerBean implements 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() { } }

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

135

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.

Listing 6-4. Using an Extended Entity Manager

@Stateful public class DepartmentManagerBean implements 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() { } }

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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

136

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 will 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.

The biggest limitation of the extended entity manager is that it requires a stateful session bean. Despite having been available in the EJB specification for many years, stateful session beans are still not widely used. Partly because of the poor quality of early vendor implementations, stateful session beans gained a reputation for poor performance and poor scalability. Even though modern application servers are very efficient in their management of stateful session beans, developer skepticism remains. Given that the HTTP session offers similar capabilities and is readily available without developing new beans, developers have traditionally preferred it to stateful session beans for conversational data.

More importantly, many Java EE applications do not require the kind of conversational state that stateful session beans provide. But that said, the extended persistence context is a significant feature custom-tailored to stateful session beans, and as best practices emerge for this type of persistence context they might see more use in the future.

Application-Managed Entity Managers In Chapter 2 we introduced JPA 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 we will see, they can be used in Java 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 Java 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.

Listing 6-5. Application-Managed Entity Managers in Java SE

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")

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

137

.getResultList(); for (Employee e : emps) { System.out.println(e.getId() + ", " + e.getName()); } em.close(); emf.close(); } }

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 will discuss persistence unit properties in Chapter 13.

The best way to create an application-managed entity manager in Java 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.

Listing 6-6. Application-Managed Entity Managers in Java EE

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(); } // ... } }

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

138

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, and that 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 Java EE, the container closes the factory automatically, so no extra steps are required.

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 Java 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 will 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 JPA.

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 JPA. 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 JTA transactions, which are the transactions of the Java EE server, supporting multiple participating resources, transaction lifecycle management, and distributed XA transactions.

Container-managed entity managers always use JTA transactions, while application-managed entity managers can use either type. Because JTA is 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 Java EE applications is JTA. As we will describe in the next section, propagating persistence contexts with JTA 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 will discuss this setting and how to apply it in Chapter 13.

JTA Transaction Management In order to talk about JTA 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

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

139

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 JTA 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.

Listing 6-7. AuditService Session Bean

@Stateless public class AuditServiceBean implements 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); } }

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

140

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.

Listing 6-8. Logging EmployeeService Transactions

@Stateless public class EmployeeServiceBean implements EmployeeService { @PersistenceContext(unitName="EmployeeService") EntityManager em; @EJB AuditService audit; public void createEmployee(Employee emp) { em.persist(emp); audit.logTransaction(emp.getId(), "created employee"); } // ... }

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 AuditServiceBean. 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 JTA 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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

141

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 EmployeeServiceBean we demonstrated in Listing 6-8, consider the same change made to a stateful session bean DepartmentManagerBean to audit when an employee is added to a department. Listing 6-9 shows this example.

Listing 6-9. Logging Department Changes

@Stateful public class DepartmentManagerBean implements 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()); } // ... }

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

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

142

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 DepartmentManagerBean. 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 JTA transaction. We also said that the extended persistence context would always try to make itself the active persistence context. This can quickly 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.

While extended persistence context propagation is useful if a stateful session bean with an extended persistence context is the first EJB 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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

143

Listing 6-10. Customizing Transaction Attributes to Avoid Collision

@Stateful @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public class DepartmentManagerBean implements 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()); } // ... }

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 section “Application-Managed Persistence Contexts.”

Persistence Context Inheritance

The restriction of only one stateful session bean with an extended persistence context being able to participate in a JTA 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 stateful session bean from the getManager() method in order to represent the manager of the department. The EmployeeManager stateful session bean is injected and then initialized during the invocation of the init() method.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

144

Listing 6-11. Creating and Returning a Stateful Session Bean

@Stateful public class DepartmentManagerBean implements 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; } // ... }

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 JTA 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 JTA 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 DepartmentManagerBean from Listing 6-11 that uses an application-managed entity manager instead of an extended entity manager.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

145

Listing 6-12. Using Application-Managed Entity Managers with JTA

@Stateful public class DepartmentManagerBean implements 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(); } }

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 JTA transaction. Without this call, the changes to 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 session 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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

146

Listing 6-13. Sharing an Application-Managed Entity Manager

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 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 JTA 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.

Download at WoweBook.Com

CHAPTER 6 ■ ENTITY MANAGER

147

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 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 JTA, 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-14 shows the complete EntityTransaction interface.

Listing 6-14. The EntityTransaction Interface

public interface EntityTransaction { public void begin(); public void commit(); public void rollback(); public void setRollbackOnly(); public boolean getRollbackOnly(); public boolean isActive(); }

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 will be thrown if the commit fails.

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-15 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.

Listing 6-15. Using the EntityTransaction Interface

public class ExpirePasswords { public static void main(String[] args) { int maxAge = Integer.parseInt(args[0]); String defaultPassword = args[1]; EntityManagerFactory emf = Persistence.createEntityManagerFactory("admin"); try { EntityManager em = emf.createEntityManager(); Calendar cal = Calendar.getInstance();

Download at WoweBook.Com

nema postavljenih komentara
ovo je samo pregled
3 prikazano na 150 str.