Implementing Transactions in Code

Client applications connect to databases and manage transactions by using the oracle.jbo.Transaction interface. Some useful methods of the Transaction interface with regard to Application Modules are:

A client can use an Application Module provided by a middle-tier component, or it can use the default Application Module provided by the Business Components for Java framework. When connected to a database, an Application Module defines a transaction boundary. When an Application Module commits changes to the database, it makes the updated data available to other Application Modules. Figure 1 shows two Application Modules: AppMod 1 and AppMod 2. In AppMod 1, DeptEmpView and EmpView provide access to the DEPT and EMP tables by using Entity Objects. AppMod 2 also uses EmpView. However, when data is changed in one Application Module, the changes are not available to the other Application Module until the transaction is committed. For example, if EmpView in AppMod 1 updates EMP but does not commit the changes, DeptEmpView can access the new data, but EmpView in AppMod 2 cannot.

Figure 1: Transaction Boundaries Between Two Application Modules

If a middle-tier component does not include a custom Application Module, a client application must use the generic Application Module provided by the Business Components for Java framework to get the transaction for connecting to the database and starting a transaction. The following code example shows how an Application Module provides a context for transactions. The code assumes that an Application Module represented by the variable appMod has been declared and initialized elsewhere, and the transaction is started using the connect() method. It also assumes that a method named updateAttr has been implemented to update a row of a View Object vo with the value newAttrVal. If updateAttr succeeds (returns true), the code commits the transaction; otherwise, it rolls the transaction back.

// Assume that appMod has been declared and initialized elsewhere.
try {
  if (updateAttr(vo, newAttrVal)) {
    // Commit changes to the database, making
    // updated data available to other Application Modules.
    appMod.getTransaction().commit();
    System.out.println("\n Transaction committed. \n");
  }
  else {
    appMod.getTransaction().rollback();
    System.out.println("\n Transaction rolled back. \n");
  }
} catch (Exception e) {
  e.printStackTrace();
}

Application Modules can be nested. That is, an Application Module can (logically) contain one or more other Application Modules as well as View Objects. When Application Modules are nested, the outer-most (top-level) Application Module provides the transaction context for the others. Figure 2 shows two examples. In the first example, amTwo contains amThree, but it does not provide its transaction context because amOne is the outer-most Application Module. In the second example, the generic Application Module is outer-most, so it provides the transaction context for amFour.

Figure 2: Transaction Contexts and Nested Application Modules

The top-level Application Module defines a transaction context for groups of related queries and supports combining data changes from independent forms into the same transaction. By sharing an Application Module, forms can participate in the same transaction, and they can pass View Objects from the shared Application Module.

Data changes, such as inserts and deletes, are immediately detected by other Application Modules in the same transaction. Likewise, changes to attributes are detected by View Objects immediately.

An Entity Object represents a database object. It caches data and provides mapping to an underlying database object. The transaction manages the cache and synchronizes data with one database. Entity Objects are not directly exposed to the client tier. Instead, clients access an Entity Object's data through a View Object in an Application Module. All View Objects within the transaction share the same Entity Object caches. View Objects and Entity Objects communicate via events and/or Java calls (not by using remote interface proxies).

A View Object, expressed as a query on top of underlying Entity Objects, shapes the result set for presentation. Because data is cached at the Entity Object level and all View Object references within the same transaction share the Entity Objects, changes made through one View Object are immediately available through other View Objects in the same transaction.

The following code example shows the interaction between Application Modules, View Objects, and the database as values are changed, posted, and committed.

package amdemo;
import oracle.jbo.*;
import java.util.Hashtable;
import javax.naming.*;
public class TestAm {
public static void main(String[] args) {
final String amName1 = "am1.AppMod1";
final String amName2 = "am2.AppMod2";
final String voName1 = "am1.DeptView1";
final String voName2 = "am1.DeptView2";
final String voName3 = "am2.DeptView3";
final String voName4 = "am2.DeptView4";
final String connStr = "jdbc:oracle:thin:scott/tiger@jtora815:1521:ORCL";
    // Set environment for local deployment.
Hashtable env = new Hashtable(2);
env.put(Context.INITIAL_CONTEXT_FACTORY, JboContext.JBO_CONTEXT_FACTORY);
env.put(JboContext.DEPLOY_PLATFORM, JboContext.PLATFORM_LOCAL);
    ApplicationModule appMod1 = null;
ApplicationModule appMod2 = null;
    try {
javax.naming.Context ic = new InitialContext(env);
      ApplicationModuleHome home1 =
(ApplicationModuleHome)ic.lookup(amName1);
appMod1 = home1.create();
appMod1.getTransaction().connect(connStr);
      ApplicationModuleHome home2 =
(ApplicationModuleHome)ic.lookup(amName2);
appMod2 = home2.create();
appMod2.getTransaction().connect(connStr);
    } catch(Exception e){
e.printStackTrace();
}
    ViewObject vo1 = appMod1.createViewObject("vo1", voName1);
ViewObject vo2 = appMod1.createViewObject("vo2", voName2);
ViewObject vo3 = appMod2.createViewObject("vo3", voName3);
//ViewObject vo4 = appMod2.createViewObject("vo4", voName4);
    Row r1 = vo1.first();
r1.setAttribute("Loc", "asdf");
System.out.println("vo1 before AppMod1 post " + r1.getAttribute("Loc"));
Row r2 = vo2.first();
System.out.println("vo2 before AppMod1 post " + r2.getAttribute("Loc"));
vo3.executeQuery();
Row r3 = vo3.first();
System.out.println("vo3 before AppMod1 post " + r3.getAttribute("Loc"));
//Row r4 = vo4.first();
//System.out.println("vo4 before AppMod1 post " + r4.getAttribute("Loc"));
appMod1.getTransaction().postChanges();
System.out.println("vo1 after AppMod1 post " + r1.getAttribute("Loc"));
r2 = vo2.first();
System.out.println("vo2 after AppMod1 post " + r2.getAttribute("Loc"));
r3 = vo3.first();
System.out.println("vo3 after AppMod1 post " + r3.getAttribute("Loc"));
//r4 = vo4.first();
//System.out.println("vo4 after AppMod1 post " + r4.getAttribute("Loc"));
  try {
appMod1.getTransaction().commit();
System.out.println("Commit succeeded.");
} catch (oracle.jbo.JboException e) {
System.out.println("Commit failed. " + e);
}
    System.out.println("vo1 after AppMod1 commit " + r1.getAttribute("Loc"));
r2 = vo2.first();
System.out.println("vo2 after AppMod1 commit " + r2.getAttribute("Loc"));
vo3.executeQuery();
r3 = vo3.first();
System.out.println("vo3 after AppMod1 commit " + r3.getAttribute("Loc"));
//r4 = vo4.first();
//System.out.println("vo4 after AppMod1 commit " + r4.getAttribute("Loc"));
    // Keep the console window open so you can see what happened.
System.out.println("\n Press Enter to close this window.");
try { System.in.read(); } catch (Exception e) {}
}
}