Up to this point, we configured and used the persistence layer to connect and retrieve information from the database but have not tried to store data into it. This is a pretty straightforward procedure that involves the Entity Manager component—the same one we used in Chapter 3, Java EE Basics – Persistence, Query, and Presentation,—to read data from MySQL, and also a transaction, which is something we haven't seen yet.
The concept is pretty widespread nowadays, so there's no need to have painstaking explanations here but just a quick refresher. We use transactions to coordinate efforts on disparate resources—which obviously must support it—so we have a consistent unit of work. The ACID concept (atomicity, consistency, isolation, and durability) states the primary attributes that must be observed when a transaction is used:
When using an application server such as WebLogic, we can access its transaction manager via the Java Transaction API (JTA); by doing this, we have access to the resources mapped by the container that supports transactions, for example a JMS queue, a data source, a local EJB, or even a remote EJB (hosted at another WebLogic Server).
If the code that must deal with a transaction happens to be inside an EJB, you can use container-managed transactions (CMT), meaning that you don't have to acquire and control the transaction manually—the container takes care of all boilerplate procedures involved in this process.
On the other hand, you can take control of everything by disabling the container's transaction control and deal with the transaction manager, and an actual transaction, through code; this mode is called bean-managed transaction (BMT).
You can declare which mode will be used by decorating the bean with the TransactionManagement
annotation and setting its value to TransactionManagementType.CONTAINER
or TransactionManagementType.BEAN
.
CMT is the default setting when no explicit annotations are found by the application server.
So, if container-managed transactions (CMT) is the way to go, you don't need to use @TransactionManagement
because it already is the default strategy.
For each method, you can use another annotation, TransactionAttribute
, to tell the container how a transaction should be set up when a request to the method is made, if this is the case. You must set its value with an entry from the enum TransactionAttributeType
that fits your requirement. Here's a list of possible values and a description of how each one works:
Value |
Description |
---|---|
|
If the caller already has a transaction context, the method participates in it. If not, a new transaction is created and finished upon completion of the method. This is the default value. |
|
If a transaction context is passed along by the caller, the method is executed using it. If not, an exception is raised. |
|
The caller should not pass a transaction context. If a transaction happens to be present, the container suspends it and creates a new one. At the end of the execution, the local transaction is finished and the original one is resumed. |
|
It has the same behavior as |
|
The method states that it will not accept or create a transaction. If the caller passes one, an exception is thrown. |
|
A new transaction context is always created. What is done inside this transaction is committed before passing the control back to the caller. If a context is present, the manager suspends it before passing the control to the method and resumes it after the method finishes. |
To demonstrate the CMT feature, we're going to create and save a Ticket
instance in the generate
method of ReservationCodeBean
, the one that generates the control number we will send as part of the SOAP web service call we're about to create:
ReservationCodeBean
from the Store project.@PersistenceContext(unitName = "StoreBO") private EntityManager em;
generate
method is sensitive information, so we're going to annotate it to create a new transaction each time it's called. This way, it doesn't participate in previously opened transactions, avoiding data tampering. Also, we're going to assign two parameters to the method's signature that is necessary to create the Ticket
instance:@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public String generate(int theaterRef, int exhibitionRef)
Ticket
with the necessary data:// Create an instance Ticket ticket = new Ticket(); // Create Mandatory reference (Ticket --> Theater) Theater theater = new Theater(); theater.setId(theaterRef); // Set ticket.setTheater(theater); ticket.setExhibitionRef(exhibitionRef); ticket.setControl(control);
// Save em.persist(ticket);
return
instruction.Ticket
(inside the store_db
database) is created.As said earlier, this mode leaves all of the responsibility of acquiring and releasing a transaction to the bean's code. There are some helpers that can be used to ease these procedures, but it is the developer's burden to code it all.
To reach the same outcome of the previous section, the code should look as follows:
TransactionManagement
and the value BEAN
:@Singleton @TransactionManagement(TransactionManagementType.BEAN) public class ReservationCodeBean { ...
@PersistenceContext private EntityManager em;
UserTransaction
component so we can demarcate our transaction:@Resource private UserTransaction ut;
Theater
and Ticket
instances remains unchanged. When the data is ready to be saved, we must start a transaction, save the objects, and then commit the transaction. The catch
block checks whether there is an active transaction that must be rolled back:// Save try { ut.begin(); em.persist(ticket); ut.commit(); } catch (Exception e) { try { if (Status.STATUS_ACTIVE == ut.getStatus()) { ut.rollback(); } } catch (Exception rbe) { rbe.printStackTrace(); } }
Not that much work, but you do have to put some extra effort when compared to CMT. The decision of using bean-managed transactions is usually taken when several business steps must be executed as a whole, but some of them must be committed even though the bigger transaction must be rolled back if an error occurs. Also, keep in mind that not participating in a global transaction can be both a strength and a weakness, depending on the business scenario you must implement.
We discussed the usage of transactions inside the context of EJBs, but you can also acquire and use transactions where CDI isn't available. WebLogic exposes a helper class to make this process easier:
import weblogic.transaction.TransactionHelper; … TransactionHelper th = TransactionHelper.getTransactionHelper(); UserTransaction anotherUT = th.getUserTransaction();
The other pieces of code—opening and closing a transaction and saving an object—are exactly the same.