This article attempts to explain how to use spring to integrate components (including the transaction relationship of components ). In J2EE applications Program Is not difficult to connect to a single storage. However, once enterprise-level components are required to be integrated, the situation becomes complicated. A component is generally supported by one or more storage data, so when we mention integrating one or more components, we think that atomic operations in multiple data storage need to be maintained across multiple components. The J2EE server provides a container for these components, which can manage these transactional atomic operations and isolate them across components. If we do not use the J2EE server, spring can help us. When integrating component services and their related transaction relationships, spring is based on the inversion of control.
Transaction
Assume that we already have an audit component in our enterprise component library, and the client can call its service method. Later, when we wanted to establish an order processing system, we found a design requirement: the orderlistmanager component service also needs to audit the component service. Orderlistmanager creates and manages orders. Therefore, all orderlistmanager services have their own transaction attributes. When the audit component is called within the orderlistmanager service, the transaction relation (context) of the orderlistmanager service is passed to the Audit Service. In the future, a new business service component may need to audit the component service, but the Audit Service will be called in a completely different transaction relationship. The actual result is that, although the functions of the Audit component remain unchanged, it can be used in combination with other business service functions, using mix-and-match) to provide different run time transaction behaviors.
Figure 1 shows two independent call Link flows. In Stream 1, if the client has a TX relationship, orderlistmanager either participates in it or starts a new Tx, depending on whether the client is in a Tx, and what TX attributes are provided for the orderlistmanager method. This explanation is correct when the orderlistmanager service calls the auditmanager method.
Figure 1: integrated component transactions |
The EJB architecture achieves this flexibility by allowing the component integrator to provide correct transaction attributes in a declarative manner (declaratively. We have not studied alternatives to declarative Transaction Management (called programmatic transaction control) because it involves changesCodeTo affect different running current events. Almost all J2EE application servers provide distributed transaction managers in accordance with the X/Open XA specification to adapt to the two-phase commit protocol. Now the question is, can we use the same functions outside the EJB server? Spring is an alternative solution. Let's take a look at how spring helps us solve the problem of transaction integration.
Use spring for Transaction Management
We will see a lightweight transaction architecture that can manage component-level transaction integration. Spring is a solution. Its advantage is that it is not embedded into the J2EE Container service as the JNDI data source. It is worth noting that it is not difficult to insert this lightweight transaction architecture into an existing J2EE container. It seems that it has done a good job in balancing the two.
On the other hand, spring's lightweight transaction architecture also uses the Aspect-oriented programming (AOP) framework. The Spring AOP framework component uses the spring Bean factory that activates AOP. By specifying the transaction feature in the component service layer (in a spring-specific configuration file applicationcontext. XML), various transactions are divided.
propagation_required propagation_required propagation_required propagation_required, -Com. example. exception. facadeexception propagation_required, readonly propagation_required, readonly |
Once the transaction attributes are specified at the service layer, they can be intercepted and interpreted by the specific implementation of the org. springframework. transaction. platformtransactionmanager interface. This interface is as follows:
Public interface platformtransactionmanager { Transactionstatus gettransaction (transactiondefinition definition ); Void commit (transactionstatus status ); Void rollback (transactionstatus status ); } |
Hibernate Transaction Manager
Since we have decided to use hibernate as An ORM tool, we must write a specific hibernate Transaction Manager implementation, and we will do this in the next step.
<Beans> <! -- Other code goes here... --> <Bean id = "transactionmanager1" class = "org. springframework. Orm. hibernate. hibernatetransactionmanager"> <Property name = "sessionfactory"> <Ref local = "sessionfactory1"/> </Property> </Bean> </Beans> |
DESIGN: manage transactions in multiple components
Now let's discuss what "integrated component transactions" mean. You may notice that the Tx Attributes provided to orderlistmanager (a service layer component in the domain) are different. Figure 2 shows the main objects that can be identified in the business domain object model (BDOM:
Figure 2: business domain object model (BDOM) |
For demonstration purposes, we listed some non-functional requirements of objects in the domain (NFR ):
· The business object must be stored in the database appfuse1.
· Audit should be recorded in an independent database appfuse2. For security, it must be placed behind the firewall.
· Business components should be reusable.
· All activities tried at the business service layer must be audited.
Considering the above requirements, we decided that the orderlistmanager service would act as a proxy for any audit log calls to the existing auditmanager component. This forms the detailed design shown in Figure 3:
Figure 3: Component Service Design |
Note that due to the constraints of NFR (non-functional requirements), we map the objects related to orderlistmanager (Order Management) to the appfuse1 database and audit) related objects are mapped to appfuse2. The orderlistmanager component then calls the auditmanager component for auditing purposes. We all think that the methods in the orderlistmanager component are transactional, because we use this service to create orders and entries. What is the service in the auditmanager component? We believe that the Service execution audit trail in the auditmanager component is so interested in maintaining the audit track (that is, tracking records) as much as possible and keeping the track of any possible business behavior in the system. This will lead to another requirement-even if the main business activity fails, we also need to create an audit entry. The auditmanager component also needs its own transactions because it also needs to interact with its own database. As follows:
<Beans> <! -- Other code goes here... --> <Bean id = "auditmanager" Class = "org. springframework. transaction. Interceptor. transactionproxyfactorybean "> <Property name = "transactionmanager"> <Ref local = "transactionmanager2"/> </Property> <Property name = "target"> <Ref local = "auditmanagertarget"/> </Property> <Property name = "transactionattributes"> <Props> <Prop key = "log"> Propagation_requires_new </Prop> </Props> </Property> </Bean> </Beans> |
Now we focus on two business services: createorderlist and addlineitem. Please note that we didn't adopt the best design strategy-you may have noticed that the addlineitem method throws a facadeexception exception, but the createorderlist does not. In the product environment, you may want every service method to handle exceptions.
public class orderlistmanagerimpl implements orderlistmanager { private auditmanager; Public long createorderlist (orderlist) { long orderid = orderlistdao. createorderlist (orderlist); auditmanager. log (New auditobject (Order + orderid, create); return orderid; } Public void addlineitem (long orderid, lineitem) throws Facadeexception { long lineitemid = orderlistdao. addlineitem (orderid, lineitem); auditmanager. log (New auditobject (line_item + lineitemid, create); int numberoflineitems = orderlistdao. querynumberoflineitems (orderid); If (numberoflineitems> 2) { log ("added lineitem" + lineitemid + "to order" + orderid + "But rolling back ***! "); throw new facadeexception (" make a new order for this line item "); }< br> else { log ("added lineitem" + lineitemid + "to order" + orderid + ". "); }< BR >} // other code... } |
For demonstration purpose, we have established an exception handling module and introduced another business rule: an order cannot contain more than two order items. We should now note the call to the auditmanager. Log () method in createorderlist and addlineitem. You should have noticed the transaction Attributes provided for the above method.
<Bean id = "orderlistmanager" class = "org. springframework. transaction. Interceptor. transactionproxyfactorybean"> <Property name = "transactionattributes"> <Props> <Prop key = "createorderlist"> Propagation_required </Prop> <Prop key = "addlineitem"> Propagation_required,-Com. Example. Exception. facadeexception </Prop> </Props> </Property> </Bean><Bean id = "auditmanager" class = "org. Springframework. transaction. interceptor. Transactionproxyfactorybean "> <Property name = "transactionattributes"> <Props> <Prop key = "log"> Propagation_requires_new </Prop> </Props> </Property> </Bean> |
Propagation_required is equivalent to tx_required in EJB, and propagation_requires_new is equivalent to tx_requires_new in EJB. If you want the service method to run in a transaction all the time, you can use propagation_required. When we use propagation_required, if a Tx is already running, add the bean method to that Tx. Otherwise, the spring lightweight TX manager will restart you. If we want to start a new transaction when the component service is called, we can use the propagation_requires_new attribute.
We also explained that if the addlineitem method generates a facadeexception type exception, it should roll back the transaction. This is another granularity level, through which we may slightly control how TX terminates (that is, how to terminate in the case of exceptions ). The prefix-symbol indicates that the Tx is rolled back, And the + symbol indicates that the Tx is submitted.
The next question is, why do we assign propagation_requires_new to the log method? This is driven by our needs: no matter what happens to the main service method, we must record the attempt track for creating and adding order items in the system in the audit. This means that even if we encounter any internal exceptions in the createorderlist and addlineitem implementations, we have to record the audit track. This is feasible only when we start a new Tx and call the LOG method in the new TX relationship. This is why the propagation_requires_new TX attribute is assigned to log: If we call
Auditmanager. Log (New auditobject (line_item + lineitemid, create ));
Auditmanager. log () occurs in the new TX context, if auditmanager. log () itself is successful (that is, there is no exception), it will be submitted.
Create a sample Environment
In order to establish the sample environment, I followed the steps of the reference spring live:
1. download and install the following components. During the operation, pay attention to the accurate version number. Otherwise, the version mismatch may occur.
· JDK versions 5_0_01 and later
· Apache Tomcat 5.5.7
· Apache ant 1.6.2
· Equinox 1.2
2. Set the following environment variables in the system:
· Java _ home
· Catalina _ home
· Ant _ home
3. Add the following content to the PATH environment variable or use the full path to execute the script:
· Java _ home \ bin
· Catalina _ home \ bin
· Ant _ home \ bin
4. To set tomcat, open the $ catalina_home/CONF/tomcat-users.xml file in the text editor and check whether the following lines exist. If it does not exist, add:
<Role rolename = "manager"/> <User Username = "admin" Password = "admin" roles = "manager"/> |
5. create a web application based on struts, spring, and hibernate. We use equinox as the initial application framework-it has a pre-defined folder structure and all the required. JAR file and ant creation script. Decompress Equinox to a folder, which creates an equinox folder. Switch the current directory to the Equinox folder and enter the ant_home \ bin \ ant New-dapp. Name = myusers command. It creates a folder named myusers at the same level of equinox. Its content is as follows:
Figure 4: equinox myusers application folder Template |
6. Delete all. xml files in myusers \ WEB \ WEB-INF folder.
7. Copy the Equinox \ extras \ struts \ WEB \ WEB-INF \ Lib \ struts *. jar file to the myusers \ WEB \ WEB-INF \ Lib folder, so that the sample application also supports struts.
8.decompress myusersextra.zip in the sample code to a directory. Switch the current directory to the newly created myusersextra folder. Copy all the content in the myusersextra folder and paste (or overwrite) It To The myusers folder.
9. Open the command line prompt and switch the current directory to the myusers folder. Run catalina_home \ bin \ Startup. It is very important to start Tomcat from the myusers folder. Otherwise, the database will be created outside the myusers folder, causing errors when executing some transactions defined in build. xml.
10. Open the second command prompt and switch the current directory to myusers. Run ant_home \ bin \ ant install. This process will create an application and deploy it to Tomcat. During the operation, we may note that a DB directory is created in myusers to store the database appfuse1 and appfuse2.
11. Open a browser and verify that the myusers application is deployed at http: // localhost: 8080/myusers.
12. to reinstall the application, run ant_home \ bin \ ant remove and then run catalina_home \ bin \ shutdown to disable tomcat. Next, delete any myusers folder in the catalina_home \ webapps folder. Run catalina_home \ bin \ Startup to restart Tomcat and run ant_home \ bin \ ant install to install the application.
Running example
To run the test example, A JUnit test class orderlistmanagertest is provided in myusers \ test \ com \ example \ service. To execute this command, run the following command on the command line of the application we created:
Catalina_home \ bin \ ant test-dtestcase = orderlistmanager |
The test case is divided into two main parts: The first part creates an order containing two order items, and then receives the two order necklaces. It runs successfully as follows:
Orderlist orderlist1 = new orderlist (); Long orderid1 = orderlistmanager. createorderlist (orderlist1 ); Log ("created orderlist with id'" + orderid1 + "'..."); Orderlistmanager. addlineitem (orderid1, lineitem1 ); Orderlistmanager. addlineitem (orderid1, lineitem2 ); |
A similar operation is performed in the next part, but this time we try to add three order items to the order, and an exception will occur:
Orderlist orderlist2 = new orderlist (); Long orderid2 = orderlistmanager. createorderlist (orderlist2 ); Log ("created orderlist with id'" + orderid2 + "'..."); Orderlistmanager. addlineitem (orderid2, lineitem3 ); Orderlistmanager. addlineitem (orderid2, lineitem4 ); // We know that an exception will occur here Try { Orderlistmanager. addlineitem (orderid2, lineitem5 ); } Catch (facadeexception ){ Log ("error:" + facadeexception. getmessage ()); } |
The output information printed on the console is as follows:
Figure 5: Console output of the Client |
We have created Order1 and added order items with order item IDs of 1 and 2. Then we created order2 and tried to add three order items to it. Adding the first two order items (order item IDs are 3 and 4 respectively) is successful, but Figure 5 shows that when we try to add the third order item to order2 (ID is 5) business Methods encountered exceptions. Therefore, the Business Method Tx is rolled back, and the order item with the order item ID 5 is not saved to the database. This can be confirmed in Figure 6 and Figure 7. You can see the two figures by running the following commands on the console:
Catalina_home \ bin \ ant browse1
Figure 6: orders in the appfuse1 Database |
Figure 7: Order items created in the appfuse1 Database |
The next step is the most important. The example shows that order and order items are stored in the appfuse1 database, and audit objects are stored in the appfuse2 database. In fact, the service methods in orderlistmanager interact with multiple databases. Run the following command to open the appfuse2 database to view the audit track, as shown in Figure 8:
Catalina_home \ bin \ ant browse2
Figure 8: Audit track created in the appfuse2 database, which contains failed TX entries |
Pay special attention to the last line in Figure 8. The resource data list shows "it corresponds to lineitem5 ". However, if we look back at figure 7, we will find that there is no order item corresponding to lineitem5. Is there an error? In fact, there is no error, and this extra line in Figure 8 is all explained in this article. We are now discussing what happened.
We know that addlineitem () has propagation_required, and the log () method has propagation_requires_new. In addition, addlineitem () internally calls the log () method. Therefore, when we try to add the third order item to order2, an exception is thrown (according to the business rules). It will roll back the creation and link operations of this order item. However, because log () is also called in addlineitem (), and because log () has the property of propagation_requires_new Tx, rollback of addlineitem () does not roll back log (), because log () occurs in the new Tx.
Now we have made some modifications to the Tx attribute of log. Instead of using propagation_requires_new, we change it to propagation_supports. The propagation_supports attribute allows the service method to run in the client TX (if the client has a TX context), otherwise the method will run without Tx. You may need to reinstall the application so that the existing data in the database can be cleared. If you want to reinstall it, see Step 1 in the previous section.
If we run it again, we will experience a little different. This time, we encountered an exception when trying to add the third order item to order 2. It will roll back the transaction (trying to add the third order item. Then this method calls the log () method. However, because the Tx attribute of the log () method is propagation_supports, log () will be called in the same TX context as the addlineitem () method. Because addlineitem () is rolled back and log () is rolled back, the audit records of TX are not rolled back. Therefore, no audit track entries in Figure 9 correspond to the failed TX!
Figure 9: Audit track in the appfuse2 database, no entries corresponding to the failed TX |
The only modification that causes such different transaction behaviors is that we changed the Tx attribute in spring configuration, as shown below:
<Bean id = "auditmanager" Class = "org. springframework. transaction. Interceptor. transactionproxyfactorybean "> <Property name = "transactionattributes"> <Props> <! -- Prop key = "log"> Propagation_requires_new </Prop --> <Prop key = "log"> Propagation_supports </Prop></Props> </Property> </Bean> |
This is the effect of declarative transaction management. We have discussed it since the beginning of EJB. However, we know that we need high-end application servers to host EJB components. Now we know that even if we don't have an EJB server and use spring, we can see similar results.
Summary
This article provides a bright path for the combination of spring and hibernate, two strong players in the J2EE world. By improving the capabilities of the two, we have an alternative technology for persistent (CMP), container management (CMR), and declarative Transaction Management for container management. Even if spring is not designed to replace EJB, it provides features such as declarative transaction management for non-format Java objects, which allows you to save EJB in many projects.
Finding an alternative to EJB is not the goal of this article, but we try to find the most feasible technical solution to solve the problem at hand. Therefore, we need to further study the lightweight combination of spring and hibernate, which is also the topic for readers to explore in the future.