Oracle8i Enterprise JavaBeans Developer's Guide and Reference Release 3 (8.1.7) Part Number A83725-01 |
|
For JTA, client-side demarcated transactions are explicitly demarcated programmatically through the UserTransaction
object, which must be bound with the bindut
command into the namespace. With client-side transaction demarcation, the client controls the transaction. The client starts a global transaction by invoking the UserTransaction begin
method; it ends the transaction by invoking either the commit
or rollback
methods.
In addition, the client must always set up an environment including a Hashtable
with authentication information and namespace location URL. It must also register an OracleDriver
when retrieving the transaction objects from the namespace.
Figure 7-3 shows a client invoking a server object--which, in this example, is a container-managed transactional bean. The client starts a global transaction, then invokes the bean. Since the bean is a container-managed transactional bean and is specified with the transactional attribute of "Supports", the transactional context is propagated to include the server object.
The following must occur for the client to demarcate the transaction:
Hashtable
environment with the namespace address and authentication information.
OracleDriver
.
UserTransaction
object from the namespace within the client logic. When you retrieve the UserTransaction
object from any client, the URL must consist of "jdbc_access://
" prefix before the JNDI name.
UserTransaction.begin()
.
UserTransaction.commit()
or UserTransaction.rollback()
.
Example 7-1 shows a client that invokes a server bean within the transaction.
Before starting the client, you must first bind the UserTransaction
object in the namespace.
You bind the UserTransaction
object in the namespace through the bindut
command of the sess_sh
tool. To bind a UserTransaction
object to the name "/test/myUT
" in the namespace located on nsHost
, execute the following:
sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER & bindut /test/myUT
Verify that the user bound with the UserTransaction
has FORCE ANY TRANSACTION granted to the user that bound this object. This privilege enables the user to commit this transaction. In this example, you would execute the following:
GRANT FORCE ANY TRANSACTION TO SCOTT
After binding the UserTransaction
object, your client code can retrieve the UserTransaction
object and start a global transaction. Since the client is retrieving the UserTransaction
object from a remote site, the lookup requires authentication information, location of the namespace, the OracleDriver
registration, and the "jdbc_access://
" prefix.
import employee.Employee; import employee.EmployeeHome; import employee.EmployeeInfo; import javax.naming.Context; import javax.naming.InitialContext; import java.util.Hashtable; import javax.transaction.*; public class Client { public static void main (String[] args) throws Exception { //Set up the service URL to where the UserTransaction object //is bound. Since from the client, the connection to the database //where the namespace is located can be communicated with over either //a Thin or OCI8 JDBC driver. This example uses a Thin JDBC driver.String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";
//User and password are case sensitive. String user = "SCOTT"; String password = "TIGER";//1.(a) Authenticate to the database.
// create InitialContext and initialize for authenticating client Hashtable env = new Hashtable (); env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put (Context.SECURITY_PRINCIPAL, user); env.put (Context.SECURITY_CREDENTIALS, password); env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);//1.(b) Specify the location of the namespace where the transaction objects
// are bound.
env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
Context ic = new InitialContext (env);//2. Register a JDBC OracleDriver. Requirement for opening JDBC connection
// to retrieve UserTransaction object from namespaceDriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
//3. Retrieve the UserTransaction object from JNDI namespace
UserTransaction ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");
//4. Start the transaction
ut.begin();
//5. Retrieve the EJB
// get an handle to the employee_home object EmployeeHome employee_home = (EmployeeHome)ic.lookup ("sess_iiop://myhost:1521:orcl/test/employee"); // get an handle to the remote bean Employee employee = employee_home.create ();//6. Perform bean business logic.
// get an info of an employee EmployeeInfo info = employee.getEmployee ("SCOTT"); System.out.println ("Beginning salary = " + info.salary); // do work on the info-object info.salary += (info.salary * 10) / 100; // call update on the server-side employee.updateEmployee (info);//7. End the transaction
//Commit the updated value ut.commit(); } }Example 7-2 Container-Managed Transactional Bean with Supports Attribute
In the EJB deployment descriptor, the container-managed transactional session bean is specified with the Supports attribute. These transactional specifications are defined in the <transaction-type>
and <container-transaction>
elements.
<?xml version="1.0"?> <!DOCTYPE ejb-jar PUBLIC "-//Oracle Corporation//DTD Enterprise JavaBeans 1.1 //EN" "ejb-jar.dtd"> <ejb-jar> <enterprise-beans> <session> <ejb-name>test/myEmployee</ejb-name> <home>employee.EmployeeHome</home> <remote>employee.Employee</remote> <ejb-class>employeeServer.EmployeeBean</ejb-class> <session-type>Stateful</session-type><transaction-type>Container</transaction-type>
</session> </enterprise-beans> <assembly-descriptor> <container-transaction> <method> <ejb-name>EmployeeBean</ejb-name> <method-name>*</method-name> </method><trans-attribute>Supports</trans-attribute>
</container-transaction> </assembly-descriptor> </ejb-jar>
Since the session bean allows the container to manage the transaction and the bean is specified as supporting existing transactions, the transaction context is propagated to the bean when the client invokes it. The bean itself contains no transactional logic within it. It only contains the bean implementation code, as follows:
Note: The database that this bean resides on is automatically enlisted. Thus, any SQLJ statement is executed against the database and its changes are included in the global transaction. See "Local Database Enlistment" for full details on the automatic enlistment of a local database. |
package employeeServer; import employee.*; import javax.ejb.SessionBean; import javax.ejb.CreateException; import javax.ejb.SessionContext; import java.rmi.RemoteException; import java.sql.SQLException; public class EmployeeBean implements SessionBean { // Methods of the Employee interface public EmployeeInfo getEmployee (String name)
throws RemoteException, SQLException { int empno = 0; double salary = 0.0; #sql { select empno, sal into :empno, :salary from emp where ename = :name }; return new EmployeeInfo (name, empno, salary); } public void updateEmployee (EmployeeInfo employee) throws RemoteException, SQLException { #sql { update emp set ename = :(employee.name), sal = :(employee.salary) where empno = :(employee.number) }; return; } // Methods of the SessionBean public void ejbCreate () throws RemoteException, CreateException {} public void ejbRemove () {} public void setSessionContext (SessionContext ctx) {} public void ejbActivate () {} public void ejbPassivate () {} }
The previous example showed how a transaction context was propagated to server objects from a client within the JTA global transaction. When you execute the server object, the transaction is propagated over the IIOP transport layer. In addition to invoking IIOP server objects, you may wish to update databases over JDBC connections. This section shows how you enlist databases using a JDBC connection in tandem with the IIOP server object propagation.
To include a remote database within the transaction from a client, you must use a DataSource
object, which has been bound in the namespace as a JTA DataSource
. Then, invoke the getConnection
method of the DataSource
object after the transaction has started, and the database is included in the global transaction. See "Enlisting Resources" for more information.
The following must occur in the client runtime to demarcate the transaction:
Hashtable
environment with the namespace address and authentication information.
OracleDriver
.
UserTransaction
object from the namespace within the client logic. When you retrieve the UserTransaction
object from the client, the URL must consist of "jdbc_access://
" prefix before the JNDI name.
UserTransaction.begin()
.
UserTransaction.commit()
or UserTransaction.rollback()
.
Example 7-3 shows a client that invokes a bean and enlists a single database within the transaction.
Before starting the client, you must first bind the UserTransaction
and DataSource
objects in the namespace. See "Bind UserTransaction Object in the Namespace" for directions on the binding the UserTransaction
object.
Use the bindds
command of the sess_sh
tool to bind an DataSource
object in the namespace. The full command is detailed in the Oracle8i Java Tools Reference.
To bind a DataSource
object for a single-phase commit transaction with the empHost
database to the name "/test/empDatabase
" in the namespace located on nsHost
, execute the following:
sess_sh -service jdbc:oracle:thin:@nsHost:5521:ORCL -user SCOTT -password TIGER & bindds /test/empDatabase -url jdbc:oracle:thin:@empHost:5521:ORCL -dstype jta
After binding the DataSource
object in the namespace, the server can enlist the database within a global transaction.
Note: If using more than one database, you will need to setup for a two-phase commit. See "Configuring Two-Phase Commit Engine" for more information. |
The following example follows the steps listed in "JTA Client-Side Demarcation Including Databases".
import employee.Employee; import employee.EmployeeHome; import employee.EmployeeInfo; import javax.naming.Context; import javax.naming.InitialContext; import java.util.Hashtable; import javax.transaction.*; public class Client { public static void main (String[] args) throws Exception { //Set up the service URL to where the UserTransaction object //is bound. Since from the client, the connection to the database //where the namespace is located can be communicated with over either //a Thin or OCI8 JDBC driver. This example uses a Thin JDBC driver.String namespaceURL = "jdbc:oracle:thin:@nsHost:1521:ORCL";
//User and password are case sensitive. String user = "SCOTT"; String password = "TIGER";//1.(a) Authenticate to the database.
// create InitialContext and initialize for authenticating client Hashtable env = new Hashtable (); env.put (Context.URL_PKG_PREFIXES, "oracle.aurora.jndi"); env.put (Context.SECURITY_PRINCIPAL, user); env.put (Context.SECURITY_CREDENTIALS, password); env.put (Context.SECURITY_AUTHENTICATION, ServiceCtx.NON_SSL_LOGIN);//1.(b) Specify the location of the namespace where the transaction objects
// are bound.
env.put(jdbc_accessURLContextFactory.CONNECTION_URL_PROP, namespaceURL);
Context ic = new InitialContext (env);//2. Register a JDBC OracleDriver. Required for retrieving UserTransaction
// and the DataSource objects from namespace over JDBC connection.DriverManager.registerDriver (new oracle.jdbc.driver.OracleDriver());
//3. Retrieve the UserTransaction object from JNDI namespace
UserTransaction ut;
ut = (UserTransaction)ic.lookup ("jdbc_access://test/myUT");
//4. Start the transaction
ut.begin();
//5.(a) Retrieve the DataSource
(that was previously bound with bindds in // the namespace. After retrieving the DataSource... // get a connection to a database. You need to provide authentication info // for a remote database lookup, similar to what you would do from a client. // In addition, if this was a two-phase commit transaction, you must provide // the username and password.DataSource ds = (DataSource)ic.lookup ("jdbc_access://test/empDB");
// 5.(b) Get connection to the database
through DataSource.getConnection // in this case, the database requires the same username and password as // set in the environment.Connection conn = ds.getConnection ();
//6. Retrieve the EJB
// get an handle to the employee_home object EmployeeHome employee_home = (EmployeeHome)ic.lookup (serviceURL + objectName); // get an handle to the remote bean Employee employee = employee_home.create ();//7. (a) Perform bean business logic.
// get an info of an employee EmployeeInfo info = employee.getEmployee ("SCOTT"); System.out.println ("Beginning salary = " + info.salary); // do work on the info-object info.salary += (info.salary * 10) / 100; // call update on the server-side employee.updateEmployee (info);//7. (b) Execute SQL
statements against the enlisted database. Statement stmt = conn.createStatement (); int cnt = stmt.executeUpdate ("insert into my_tab values (39304)");//8. Close the database connection.
conn.close ();
//9. End the transaction
//Commit the updated value ut.commit(); } }
The deployment descriptor and bean implementation code is the same as described in Example 7-2.
Whether your bean instance uses bean-managed or container-managed transactions, the databases that the bean accesses must be enlisted to be included within the global transaction. This is discussed more in "Enlisting Resources" and "Bind DataSource Object in the Namespace".
Once bound, you can enlist the database after the transaction begins.
Because your bean has been deployed into an Oracle8i database, the container enlists this database automatically within the transaction. This database is known as the local database. Thus, if you execute any SQL against this database, the results are committed when the transaction is committed.
As discussed in the Oracle8i SQLJ Developer's Guide and Reference, an implicit local connection is supplied to the database that the object is running in. Any statements executed within the SQLJ statement is executed against the local database. However, in order for the statement results to be part of the transaction, you must execute the SQLJ statement within an open global transaction. That is, the SQLJ statement must be executed after the |
|
You can create a local connection by executing the methods specified in Table 7-6. |
Retrieval Method | Description |
---|---|
|
See Example 7-6 |
|
See Example 7-5 |
|
See Example 7-4 |
Always execute these methods and subsequent SQL statements after the global transaction has started.
The following example is a container-managed transactional bean with RequiresNew attribute, so the global transaction is initialized when the bean is first invoked. The local connection is retrieved through the DataSource.getConnection
method, which enlists the database in the transaction. SQL statements are executed against the local database. These statements are committed when the global transaction is committed by the container when the bean exits.
public EmpRecord query (int empNumber) throws SQLException, RemoteException {//Retrieving the UserTransaction and DataSource using in-session activation
Context ic = new InitialContext ( ); //Retrieve the DataSource using in-session activation DataSource ds = (DataSource) ic.lookup("/test/myDB"); //Retrieve the local connection object to the local database Connection conn = ds.getConnection ();//prepare and execute a sql statement against the local database. PreparedStatement ps = conn.prepareStatement ("select ename, sal from emp where empno = ?"); try { ps.setInt (1, empNumber); ResultSet rset = ps.executeQuery (); if (!rset.next ()) throw new RemoteException ("no employee with ID " + empNumber); return new EmpRecord (rset.getString (1), empNumber, rset.getFloat (2)); } finally { ps.close(); } //close the database connection conn.close(); }
Example 7-5 DriverManager.getConnection Method Example
The following example is the same as Example 7-4, except for the database retrieval method. Substitute the following to retrieve the connection:
//Retrieve the local connection object to the local database Connection conn =
DriverManager.getConnection ("jdbc:oracle:kprb:");Example 7-6 OracleDriver().defaultConnection Method Example
The following example is the same as Example 7-4, except for the database retrieval method. Substitute the following to retrieve the connection:
//Retrieve the local connection object to the local databaseConnection conn =
new oracle.jdbc.driver.OracleDriver().defaultConnection ();
If you access a remote Oracle8i database from the server that should be included in the transaction, you must open the connection to the database after the global transaction starts.
The following example enlists a database in the global transaction within a bean-managed transactional bean.
The following example is a container-managed transactional bean, so it does not retrieve the UserTransaction
. However, it does retrieve a DataSource
to start a JDBC 2.0 connection.
package employeeServer; import employee.*; import javax.ejb.SessionBean; import javax.ejb.CreateException; import javax.ejb.SessionContext; import java.rmi.RemoteException; import java.sql.SQLException; import javax.transaction.*; public class EmployeeBean implements SessionBean { SessionContext ctx; // Methods of the Employee interface public EmployeeInfo getEmployee (String name) throws RemoteException, SQLException { // get a connection to the local database. If this was a two-phase commit // transaction, you would provide the username and password
// for the 2pc engineDataSource ds = (DataSource)ic.lookup ("/test/myDS");
// get connection to the local database through DataSource.getConnectionConnection conn = ds.getConnection ();
//perform your SQL against the database. //prepare and execute a sql statement.
//retrieve the employee's selected benefits PreparedStatement ps = conn.prepareStatement ("update emp set ename = :(employee.name),
sal = :(employee.salary) where empno = :(employee.number)"); try { ps.setInt (1, empNumber); ResultSet rset = ps.executeQuery (); if (!rset.next ()) throw new RemoteException ("no employee with ID " + empNumber); return new EmpRecord (rset.getString (1), empNumber, rset.getFloat (2)); } finally { ps.close(); } //close the connection conn.close(); return new EmployeeInfo (name, empno, salary); } // Methods of the SessionBean public void ejbCreate () throws RemoteException, CreateException {} public void ejbRemove() {} public void setSessionContext (SessionContext ctx) { this.ctx = ctx; } public void ejbActivate () {} public void ejbPassivate () {} }
|
Copyright © 1996-2000, Oracle Corporation. All Rights Reserved. |
|