A Ride at the OK (or Cancel) CorralBy Steve Muench
Configure nested transactions using Oracle ADF task flows.
The September/October 2008 Frameworks column (“Task and You Shall Receive”) showed you how to use Oracle Application Development Framework (Oracle ADF) task flows to encapsulate logical units of work, how one task flow can call another, and how to configure a bounded task flow to automatically coordinate commit and rollback. This column explores Oracle ADF’s support for nested transactions, which enable you to easily build composite task flows in which the user can cancel work performed in a called flow without losing other pending changes. The example in this column also illustrates how the same transactional task flow can be used either on its own or as a nested part of a containing flow.
To begin, download the starter workspace and ensure that you’re using the studio edition of the Oracle JDeveloper 126.96.36.199.0 (production) release, available as a free download on the Oracle Technology Network (OTN). Start by extracting the contents of the o29frame.zip file and opening the FrameworksMarApr2009.jws workspace in Oracle JDeveloper. The Model project in the workspace defines Emp and Dept entity objects, EmpView and DeptView view objects, and EmpModule and DeptModule application modules. The ViewController project contains three bounded task flows that enable you to manage employee data and create new departments. Note that you can safely ignore the contents of the FrameworkExtensions project for now—in next issue’s column, you’ll learn in more detail the role it plays. Before proceeding, adjust the properties of the scott connection in the Application Resources zone of the Application Navigator until you can successfully test a connection to a SCOTT schema. If you need to create the tables, use the provided CreateDeptEmpTables.sql.
Exploring the Example
Start by familiarizing yourself with the example, a simple application that enables an end user to perform two high-level tasks: managing employee information and creating new departments. Because the application’s end-user functionality is modeled as Oracle ADF task flows, it is easy to visually explore the application’s structure to quickly understand the basic flow. The unbounded adfc-config task flow typically contains the application “home page,” so when exploring an Oracle ADF application you didn’t build yourself, it is a good place to start. In the Application Navigator, in the ViewController project Web Content folder, expand the Page Flows folder and double-click the adfc-config task flow to open it in the visual editor. You can see from the diagram that the Home view activity enables the user to call the create-department task flow or the manage-employees task flow. Next, double-click the manage-employees task flow call activity in the diagram to open the related task flow in the editor. This bounded task flow’s default activity is an EmployeeList view activity, which enables the user to search for an existing employee to modify or to create a new one. Both actions are handled by the modify-employee task flow call activity.
Double-click the modify-employee task flow call activity to open it. You may recognize it as an employee-flavored version of the parameterized task flow you built in the September/October 2008 Frameworks column: it enables the user to find an existing employee to edit or to create a new employee. Note that its EditEmployee view activity enables the user to call the create-department task flow to add a new department, if necessary, as part of the employee modification task. Finally, double-click the create-department task flow call activity. Its default method call activity uses a built-in Create operation to create a new department row before forwarding control to the AddDepartment view activity.
Understanding Transaction Options
Before you start working with the declarative transaction coordination features in this Oracle ADF release, you need to understand the declarative settings that control its behavior.
If your bounded task flow modifies data or wants to control when to commit or roll back data modified by any called task flows, it will require a transaction. You indicate this by setting the task flow’s transaction property to requires-transaction . At runtime, when Oracle ADF starts a bounded task flow that requires a transaction, one of two things occurs. If no transaction is currently active, Oracle ADF will begin a new transaction and remember the task flow that was responsible for starting the transaction. In contrast, if a current transaction is already open, the new task flow will participate in that existing transaction. Given this rule of thumb, any task flow you build that requires a transaction will either coordinate one or be coordinated by a calling task flow’s transaction, depending on how the task flow is used. Sometimes a task flow requires a transaction but also requires parameters from a calling task flow in order to work properly. In such a case, set the task flow’s transaction property to requires- existing-transaction instead. This says a task flow can participate in an existing transaction but not start a new transaction itself. To indicate that a task flow should never be used as part of an existing transaction, set its transaction property to new-transaction .
When a bounded task flow that requires a transaction begins, it can create a savepoint in the pending work if it ends up participating in an existing transaction. This enables the nested task flow to roll back to this savepoint—if it exists—with a return activity configured to perform a rollback. (This savepoint is similar in concept to a database-level savepoint, but it is implemented in the Oracle ADF middle-tier layer instead.) To control this savepoint behavior, use the no-save-point property of the task flow and the restore-save-point property of the return activity. Oracle ADF automatically determines at runtime whether to perform a full rollback or just a rollback to a savepoint, depending on whether the task flow in question started the transaction or is participating in an existing transaction.
Putting Theory into Practice
Put these ideas into practice by configuring the create-department task flow that’s open in the editor to require a transaction and to support savepoints. First, click the diagram surface anywhere to select the whole task flow. Next, in the Property Inspector, expand the Behavior section and set the transaction property to requires-transaction . Note that the no-save-point property defaults to false, which (unwinding the double-negative) means that a savepoint will be created if necessary. Given that this is what you want, leave that default setting.
Observe that a red error icon appears on the Cancel and Save return activities in the create-department task flow to indicate that they require additional configuration to be valid, now that the task flow requires a transaction. First, select the Save return activity, and in the Behavior section of the Property Inspector, set the End Transaction property to commit . This setting will cause the return activity to commit the transaction if the create-department task flow started it; it will perform no action when this flow participates in a calling task flow’s transaction. Next select the Cancel return activity and, in the Behavior section of the Property Inspector, set its End Transaction property to rollback and its restore-save-point property to true . These property settings cause the return activity to roll back the transaction if the create-department task flow started it; otherwise, the return activity will roll back to the savepoint created at the start of the nested task flow when this flow participates in a calling task flow’s transaction. Now perform similar steps for the manage-employees task flow. Select its editor tab and click the diagram surface. In the Property Inspector, set the transaction property to new-transaction . This will ensure that the manage-employees task flow can be called only when no existing transaction is in effect. Select the Save return activity, and set its End Transaction property to commit . Select the Cancel return activity and set its End Transaction property to rollback and its restore-save-point property to true.
Finally, configure the modify-employee activity. Select its editor tab and click the diagram surface. Because this task flow has required parameters, it makes sense that it be called only from another task flow. So in the Property Inspector, set its transaction property to requires- existing-transaction . (If the transaction list becomes outlined in red, set the data-control-scope property to shared —even though that is the default—to work around the incorrectly reported error.) Select the Cancel return activity, and set its restore-save-point property to true . Finally, save all your changes and take the application for a spin.
Exercising Your OKs and Cancels
To run the application, right-click the ViewController project in the Application Navigator and choose Run Project (or Run ViewController.jpr ). Because the ViewController project is configured with the adfc-config unbounded task flow as its default run target and the ADF Task Flow settings of that configuration select the Home view as the default starting activity, the home page automatically appears in your default browser. Click the Create a New Department link to start the create-department task flow. Enter 99 in the Deptno field, TestName for Dname , and TestLoc for Loc , and then click OK . In this case, the create-department task flow began the transaction, so clicking OK commits the data to the database.
Next, back on the home page, click the Manage Employees link to start the manage-employees task flow. When the Manage Employees page appears, enter the letter s in the quick search field for Ename and click the Search icon to the right of the field. This search finds employees SMITH and SCOTT. Select the row containing SCOTT, and click the Edit button in the table header to start the modify-employee task flow (with the mode parameter set to Edit). On the Edit Employee page, change Job to MANAGER and Sal to 3500 , and click OK . Because the modify-employee task flow participates in the transaction started by the calling manage-employees task flow, clicking the OK button does not yet save the changes to the database. Back on the Manage Employees page, note that the table is configured to show modified rows in italics and modified attributes in bold.
Next you will start and cancel a new change to observe that you don’t lose the pending edit you’ve already performed to employee SCOTT in the current transaction. Click Add a New Employee to start the modify-employee task flow (with the mode parameter set to Create). On the Create Employee page, enter 1234 for the required Empno field and TestEmp for Ename . Click the Deptno list, and note that it includes the TestName department number 99 we created (and committed) above. Assuming that the new employee TestEmp is the first employee in a new department 77 that doesn’t yet exist, click Add New Department to call the create-department task flow. Enter 77 for Deptno , NewDept for Dname , and NewLoc for Loc , and then click OK . In contrast to when we called create-department directly from the home page in the unbounded adfc-config task flow, here we called create-department in the context of an open transaction that was started by the manage-employees task flow and in which the calling modify-employee task flow is also participating. Therefore, the newly created department 77 you’ve entered is not immediately saved to the database; it is simply a pending change in the current transaction. Back on the Create Employee page, click the Deptno list again and select NewDept 77 , which appears automatically on the list. Click Cancel to return to the Manage Employees page.
Now you will try to apply and then cancel additional changes on top of the other changes you’ve made. On the Manage Employees page, select SCOTT and click Edit again. Select the Deptno list, and note that NewDept 77 is no longer on the list, because all the changes made by the nested task flow call were cancelled (by Oracle ADF’s rolling back to the savepoint created when the modify-employee task flow started). Set SCOTT’s Deptno value to OPERATIONS 40 , change Job to SALESMAN and Sal to 4000 , and then click Cancel . Note that the previous pending changes to the row containing SCOTT are left intact ( Job = MANAGER , Sal = 3500 ), which demonstrates that this rollback to a savepoint is not simply restoring the current database values. When you’re done experimenting, click OK on the Manage Employees page to save all your changes or Cancel to abandon them all.
Scoping Out One Final Detail
You now have seen firsthand how simple it is to configure nested transactions to handle typical OK and Cancel scenarios and have witnessed how reusable, transaction-aware task flows such as create-department can either coordinate a transaction or have their transactions coordinated by a calling task flow. One small but important detail that may have slipped by unnoticed is that the create-department task flow referenced a different data control (DeptModuleDataControl) from the previous two task flows, which both referenced EmpModuleDataControl. Figure 1 illustrates what happened at runtime as you went through the example above.
A called bounded task flow’s data-control-scope property determines whether or not it shares the same frame of reference for data controls as the calling flow. All three bounded task flows in this column’s example use the default value of shared for this property, which is usually the setting you want. In practice, it means that if a calling task flow such as manage-employees and a called task flow such as modify-employee both reference a data control named EmpModuleDataControl, both of these references will resolve to the same data control instance. When the first data control in a frame of reference is used, Oracle ADF will check out an instance of its application module from the respective pool. However, when a transactional task flow such as create-department references a second (or subsequent) data control such as DeptModuleDataControl in the same frame of reference, as shown in Figure 1, Oracle ADF will automatically nest an instance of the new application module as a member of the first application module referenced. This enables the newly referenced application module to participate in the same transaction as the original application module referenced in the transaction.
If you want a called task flow to always work in its own isolated transaction, set its data-control-scope property to isolated . Figure 2 illustrates how such a setting on the create-department task flow would change the runtime behavior of the application. Because create-department would have an isolated frame of reference for its data controls, DeptModuleDataControl would be the first one referenced in that separate, isolated frame. Oracle ADF would check out an instance of the DeptModule application module (from the pool) that would use a second database connection and be a completely distinct unit of work. None of the pending changes from modify-employee would be visible to create-department , and none of the work performed by create-department would affect the state of pending changes in modify-employee .
Hopefully this column has helped you understand how to best leverage Oracle Application Development Framework’s nested transactions and transactional task flows and has also given you some tips that can help you make more-informed decisions about how to modularize your application’s functionality. For more information, see section 17.2, “Managing Transactions,” in the Oracle Fusion Middleware Fusion Developer’s Guide for Oracle ADF 11g.
Steve Muench is a consulting product manager for Oracle JDeveloper and an Oracle ACE. Since 1990 he has developed and supported Oracle tools and XML technologies and continues to evangelize them. Muench coauthored Oracle ADF Developer’s Guide for Forms/4GL Developers (Oracle, 2006), wrote Building Oracle XML Applications (O’Reilly Media, 2000), and shares tips and tricks on OTN and in his “Dive into ADF” blog.