Saturday, March 26, 2011

Entities and Transactions in JPA

All entities have the property of transactionability and their CRUD operations will take place within a transactional context. Transactions can be broadly classified into two types based on who actually owns (or manages) the transaction. They are JTA and Resource-local transaction.

In the case of a J2EE Application, the default transaction type is JTA (Java Transaction API), unless explicitly specified. A method can be simply annotated with @RequiresNew (or @Requires) in which case a new transaction will always started by the container and the transaction completes as soon as the method ends.

Whereas in a J2SE application, the transaction-type defaults to Resource-local, which means that the developer (or the application code) has to explicitly start and end a transaction. It means that the user has to explicitly start a transaction with the help of EntityManager object, and then have to commit it, if everything goes normal, else have to roll-back the transaction if some error occurs.

The following code shows this,

EntityTransaction userTransaction = entityManager.getTransaction();

// Do something here.

// If everthing goes well, make a commit here.
}catch(Exception exception){
// Exception has occurred, roll-back the transaction.

Operations on Entity Objects

The following are the legal operations that can be performed on Entity objects with the help of EntityManager API. As seen previously, EntityManager are objects that manage one or more entity objects with the help of an implicit persistence context.

Persisting Entity Objects:
Entity objects are like regular java objects until they become managed and made persistent by the EntityManager. The following piece of code makes an entity to become persistent and managed.

MobileEntity mobileObject = new MobileEntity();
mobileObject.set()... // Update the state values here.

The persist(entityObject) methods makes the entity persistent in the underlying database and managed within the persistence context of the EntityManager, whether the persistence context is a transaction-scoped or an extended persistence context depends upon how actually the EntityManager was configured.

[What happens when a managed entity is again forced to become managed by calling the persist() method. Or what happens when the persist() method is called couple of times on the same entity object.

Whenever the persist() method is called, the persistence engine will check for the existence of that object with the help of its unique identifier (which is represented in the form of primary key). If any duplicate object is found, then a run-time exception, EntityExistsException will be thrown.]

Querying for Entities:
Developers can either depend on the EntityManager for simple search or Query objects for providing powerful search conditions for locating and querying entity objects.

Using EntityManager object:
Following are the two different methods available in EntityManager interface for querying entity objects and there are some major differences between the two.

Using the EntityManager.find() method:
The find() method takes the class name of the Entity object and the primary key value for locating a single entity object. If the object of interest cannot be located by the EntityManager, then this method will simply return null. The following code illustrates this,

MobileEntity mobile = entityManager.find(MobileEntity.class, “ABC-123”);
If (mobile != null){ // mobile object may or may not be null.
// Process the object.

One good thing about the find() method is that, the returned entity object soon becomes managed automatically within the persistence context of the EntityManager.

Using the EntityManager.getReference() method:
This method, like the EntityManager.find() method, takes the name of the entity class and the primary key as their arguments. But the difference is, unlike the find() method which will return null if the entity object is not found, this method will throw an exception EntityNotFFoundException. Another difference between this method and the find() method is that, the entity that is fetched by this method may be lazily loaded. That is, the state of the state of entity (like model, manufacturer, imeiNo may be lazily loaded during the first time it is actually accessed).

MobileEntity mobile = entityManager.getReference(MobileEntity.class, “ABC-123”);
// mobile object may not contain the actual state values for model, manufacturer
// and imei number, the states may be loaded during the first access.

String model = mobile.getModel();
// The persistence engine may fetch the model value for the mobile here
// at this particular point of time.

Using the Query object: Discussed later.

Deleting Entities:
To remove (delete) an entity object from the database, make a call to EntityManager.remove(entityObject) method. The entity object that is passed to the remove() must be a managed entity, else the operation will fail.

Also, making this call may or may-not remove the entity immediately from the database. The algorithm that achieves the same is implementation specific. Some implementation may only mark the entity as a removed entity after this method call, and the actual deletion of the entity object in the database may happen when a flush() operation (which is discussed later) is made.

After this method call, the entity will become detached from the persistence context and it is no longer a managed one.

Updating Entities:
The EntityManager.merge(entityObject) method will make a detached entity to get associated with the current persistence context of the EntityManager. Consider the following lines of code.

// Transaction has begin.
MobileEntity mobile = entityManager.find(MobileEntity.class, “ABC-123”);
// Transaction ends.

mobile.set()…… // Updating the mobile object.


In the above piece of code, a mobile entity object is located with the EntityManager.find() method. This entity is now in a managed state. Assume that the transaction ends after some point of time and also the persistence context is a transaction-scoped persistence context. As soon as the transaction completes, the persistence context will go off, (since the persistence context is a transaction-scoped persistence context). So the entity object becomes detached from the persistence context. After this any modifications that are made to the mobile object won’t be knowledgeable to the EntityManager as the mobile object has already been detached from it.

Now, calling the merge() method, will make the mobile object becomes managed, and all the recent changes that are made to it will become visible to the current persistence context of the EntityManager.

Flushing and Refreshing:

The EntityManager.flush() will synchronize all the changes that are made to the persistent entities back to the underlying database, whereas the EntityManager.refresh() does the reverse. It will update the entity object with values taken from the database. Any new values that are set to the entity objects will be lost as a result of this method call.

For example, consider the following piece of code,

MobileEntity mobile = …..
mobile.set(); // Update the state values for the mobile object.
// Calling this flush method will synchronize the database with the values
// taken from the entity object.
consider this code,

MobileEntity mobile = …
mobile.set(); // The state values for the mobile object is updated.
// The refresh() method will refresh the entity object with the values taken from the database.
// All the updates that are done are lost.

No comments:

Post a Comment