Detached Entities and FetchType




Detached Entities and FetchType

In Chapter 5, we discussed how managed entity instances become detached from a persistence context when the persistence context ends. Since these entity instances are no longer managed by any persistence context, they may have uninitialized properties or relationships. If you are returning these detached entities to your clients and basically using them as data transfer objects between the client and server, you need to fully understand the effects of accessing any uninitialized relationships.

When an entity instance becomes detached, its state may not be fully initialized because some of its persistent properties or relationships may be marked as lazily loaded in the mapping metadata. Each relationship annotation has a fetch( ) attribute that specifies whether the relationship property is loaded when the entity is queried. If the fetch( ) attribute is set to FetchType.LAZY , then the relationship is not initialized until it is traversed in your code:

Customer customer = entityManager.find(Customer.class, id);
customer.getPhoneNumbers( ).size( );

Invoking the size( ) method of the phoneNumbers collection causes the relationship to be loaded from the database. It is important to note that this lazy initialization does not happen unless the entity bean is being managed by a persistence context. If the entity bean is detached, the specification is not clear on what actions the persistence provider should perform when accessing an unloaded relationship of a detached entity. Most persistence providers throw some kind of lazy instantiation exception when you call the accessor of the relationship or when you try to invoke an operation on the relationship of a detached entity:

Cruise detachedCruise = ...;
try
{
   int numReservations = detachedCruise.getReservations( ).size( );
}
catch (SomeVendorLazyInitializationException ex)
{
}

In this code, the application has received an instance of a detached Cruise entity and attempts to access the @OneToMany reservations relationship. Since the fetch( ) attribute of this relationship is LAZY , most vendor implementations will throw a vendor-specific exception. This lazy initialization problem can be overcome in two ways. The obvious way is just to navigate the needed relationships while the entity instance is still managed by a persistence context. The second way is to perform the fetch eagerly when you query the entity. In Chapter 9, you will see that the EJB QL query language has a FETCH JOIN operation that allows you to preinitialize selected relationships when you invoke a query.

How is the persistence provider able to throw an exception when accessing the relationship when the Cruise class is a plain Java class? Although not defined in the specification, the vendor has a few ways to implement this. One is through bytecode manipulation of the Cruise class. In Java EE, the application server is required to provide hooks for bytecode manipulation for persistence providers. In Java SE, the persistence provider may require an additional post-compilation step on your code base. Another way for vendors to implement this is to create a proxy class that inherits from Cruise and that reimplements all the accessor methods to add lazy initialization checking. For collection-based relationships, the persistence provider can just provide its own implementation of the collection and do the lazy check there. Whatever the implementation, make a note to discover what your persistence provider will do in the detached lazy initialization scenario so that your code can take appropriate measures to handle this exception.