|
|
|
|
Java Programming with Oracle SQLJBy Jason PriceAugust 2001 0-596-00087-1, Order Number: 0871 398 pages, $39.95 |
Chapter 8
Contexts and MultithreadingThere are two important objects used in SQLJ that affect the execution of database operations: connection contexts and execution contexts. Connection contexts are used to connect to a database. All embedded SQL statements within a SQLJ program run in a connection context. Connection contexts make it possible to create multiple connections to a database or to connect to more than one database at a time. An execution context is used to hold the number of rows affected by a SQL operation, along with any warnings generated by the database. Execution contexts are used to control certain aspects of how a SQL statement is executed. For example, you can use an execution context to control the timeout period after which a SQL operation is abandoned.
A multithreaded program is one that is able to carry out several tasks in parallel using Java threads. As you will see in this chapter, execution contexts are very important when writing a multithreaded SQLJ program.
Connection Contexts
A connection context represents a connection to a particular database schema. So far, all the SQLJ programs you have seen have used one database connection, established using a call to the
Oracle.connect( )method. For example, the program FundamentalExample1.sqlj described in Chapter 3 used the following call to connect to thefundamental_userschema:Oracle.connect("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");A call to the
Oracle.connect( )method creates an object of thesqlj.runtime.ref.DefaultContextclass, which may then be used to access the database. ThisDefaultContextobject is known as the default connection context, and it is the database connection that the rest of the program uses by default when performing SQL operations. You can create additional connection contexts in order to make multiple database connections.Multiple Database Connections
Sometimes, a single database connection may not be enough for your program to accomplish its task. For example, you might need to connect to two or more schemas in the database at the same time to retrieve the data you need. It is possible to create multiple database connections by explicitly creating additional objects of the
DefaultContextclass.The
oracle.sqlj.runtime.Oracleclass contains a method namedgetConnection( )that creates and returns aDefaultContextobject, which may then be stored in aDefaultContextobject that you explicitly create. The syntax for thegetConnection( )method is the same as for theconnect( )method. For example, the following statement creates aDefaultContextobject namedconn_context1that connects to thefundamental_userschema:DefaultContext conn_context1 = Oracle.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");The statement in this example calls the
getConnection( )method to create and return aDefaultContextobject. That object is then assigned toconn_context1. Multiple connections may be made to the same schema using multipleDefaultContextobjects. For example, the following statement creates another connection context namedconn_context2, which also accesses thefundamental_userschema:DefaultContext conn_context2 = Oracle.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");An important point to note when working with multiple connection contexts is that each connection context establishes its own database session. As a result, each connection context has its own transaction in the database.
Specifying the connection context to use
Now that you have seen how to create connection contexts, you must be asking yourself, "How do I tell SQLJ which connection context to use?" There are two ways you can do this. The first is to include the name of the connection context in the SQLJ executable statement. This is done using the following syntax:
#sql [connection_context_name] { SQL_statement };TIP: The brackets around the connection context name in this syntax do not indicate an optional syntax element. They are actually part of the statement.
The syntax elements are as follows:
- connection_context_name
- The name of the connection context to use for the embedded SQL statement.
- SQL_statement
- The embedded SQL statement.
If a connection context name is not specified, then the default connection context is used to perform the SQL statement. The following example uses the connection context
conn_context1created earlier to update a row in thecustomerstable:#sql [conn_context1] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};The second way to indicate which connection context to use is to call the
DefaultContext.setDefaultContext( )method. This method accepts aDefaultContextobject, and any SQL statements that follow it will use this connection context by default. Of course, if a connection context is explicitly specified in an executable statement, then that connection context will be used: an explicitly specified execution context always overrides the default. The following example shows thesetDefaultContext( )method being used to set the default connection context for two different SQLJ statements:DefaultContext.setDefaultContext(conn_context1);#sql { SQL_statement }; // uses conn_context1DefaultContext.setDefaultContext(conn_context2);#sql { SQL_statement }; // uses conn_context2In addition to setting a default context, you can also retrieve it. A call to the
getDefaultContext( )method returns the default connection context. For example:DefaultContext default_context = DefaultContext.getDefaultContext( );Understanding transactions and multiple connection contexts
I have already mentioned that each connection context establishes its own database session and transaction. This is useful because it means you can have multiple transactions against the same schema. The code in this section illustrates a simple example of this powerful feature. The following example updates a row in the
customerstable using the connection contextconn_context1. If a transaction were not already underway, this statement would cause a new transaction to be started.#sql [conn_context1] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};Until this database transaction is completed by
conn_context1(using a commit or rollback, for example), the row is locked. If another statement uses a different connection context to attempt to modify the same row, it must wait untilconn_context1ends the transaction and the lock on the row is freed.[1] This locking behavior is not unique to connection contexts. If you connect to thefundamental_userschema using two instances of SQL*Plus to start two database sessions and then try to update the same row in a table from both sessions, one of the SQL*Plus sessions will wait until the other session ends its transaction.In the following example,
conn_context1performs a rollback of the update, andconn_context2is then able to modify the same row in thecustomerstable:#sql [conn_context1] { ROLLBACK };#sql [conn_context2] {UPDATEcustomersSETfirst_name = 'Fred',last_name = 'Smith'WHEREid = 1};Making connections to multiple databases
You aren't limited to accessing one database in a SQLJ program. The
Oracle.getConnection( )method may be used to create connection contexts that access schemas in more than one database. For example, the following statement creates a connection context that accesses a schema namedremote_userin the database identified by the Oracle SIDorcl, which is running on a computer namedremotehost:DefaultContext remote_conn_context = Oracle.getConnection("jdbc:oracle:thin:@remotehost:1521:orcl","remote_user","remote_password");The connection context
remote_conn_contextmay now be used in executable statements, just like any other connection context. In this way, your SQLJ programs can access as many databases as necessary.Closing connection contexts
Once a connection context is no longer needed, it is good programming practice to either perform a commit or a rollback, and close the connection context using the
close( )method. Actually, when you close a connection context, an implicit commit is performed, but you should still perform an explicit commit instead of relying on this default behavior. If you don't close a connection context, an implicit rollback occurs when your SQLJ program ends, and any changes you made to the database in the transaction for that context will be lost.The following statement closes the connection context
conn_context1:conn_context1.close( );The default connection context created by a call to the
Oracle.connect( )method may also be closed:Oracle.close( );Example Program: ContextExample1.sqlj
This section contains a complete example program named ContextExample1.sqlj (Example 8-1) that illustrates the use of connection contexts to make multiple connections to a database. The program ContextExample1.sqlj performs the following major steps:
- Makes two database connections: one using a call to the
connect( )method to create a default connection context, and the other using a call togetConnection( )to create a connection context namedconn_context.
- Adds a row to the
customerstable usingconn_context.
- Updates customer #1 using
conn_context.
- Displays all the rows in the
customerstable using the default connection context. This is done by calling the program'sdisplayCustomers( )method. The changes made to thecustomerstable in Steps 2 and 3 are not displayed in the output fromdisplayCustomers( )because those changes were made usingconn_context. Remember,conn_contextrepresents a separate connection, which has a database transaction separate from the default connection context used by thedisplayCustomers( )method.
- Switches the default connection context to
conn_contextby making a call to the program'ssetDefaultContext( )method.
- Displays all the rows in the
customerstable again via another call todisplayCustomers( ). The changes are now visible because the default connection context has been switched toconn_context, anddisplayCustomers( )uses the default context.
- Rolls back the changes.
- Closes both the
conn_contextconnection context and the default connection context.
Example 8-1: ContextExample1.sqlj /*The program ContextExample1.sqlj illustrates how to useDefaultContext connection context objects to make multipleconnections to a database.*/import java.sql.*;import oracle.sqlj.runtime.Oracle;import sqlj.runtime.ref.DefaultContext;public class ContextExample1 {// declare the iterator class#sql private static iterator CustomerIteratorClass (int id, String first_name, String last_name);public static void main(String [] args) {try {// set the default connection context using the Oracle.connect( )// methodOracle.connect("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");// connect to database using a second connection context// named conn_contextDefaultContext conn_context = Oracle.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");// add a row to the customers table using conn_contextSystem.out.println("Adding customer Jason Price using " +conn_context.");#sql [conn_context] {INSERT INTOcustomers (id, first_name, last_name)VALUES(6, 'Jason', 'Price')};// update the first row in the customers table using conn_contextSystem.out.println("Updating customer 1 name to John Doe using " +"conn_context.");#sql [conn_context] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};// display all rows in the customers table using the default// connection context, the new row and the update are// not visible to the default context because the row was added// using conn_context (which has a separate database transaction// associated with it)displayCustomers( );// switch the default context to conn_contextSystem.out.println("Switching default connection context " +"to conn_context.");DefaultContext.setDefaultContext(conn_context);ay all the rows in the customers table again using// the default connection context, the changes are now visible// because the default context has been switched to conn_contextdisplayCustomers( );// rollback the changes#sql { ROLLBACK };// close the conn_context connection contextconn_context.close( );// close the default connection contextOracle.close( );} catch ( SQLException e ) {System.err.println("SQLException " + e);System.exit(1);}} // end of main( )private static void displayCustomers( )throws SQLException {// declare a named iterator objectCustomerIteratorClass customer_iterator;// use the default context when populating the iterator#sql customer_iterator = {SELECTid, first_name, last_nameFROMcustomersORDER BYid};System.out.println("List of customers using default connection " +"context.");// access the contents of the iteratorwhile (customer_iterator.next( )) {// display the customerSystem.out.println("Customer:");System.out.println("id = " + customer_iterator.id( ));System.out.println("first_name = " + customer_iterator.first_name( ));System.out.println("last_name = " + customer_iterator.last_name( ));} // end of while loop// close the iteratorcustomer_iterator.close( );} // end of displayCustomers( )}The output from the program ContextExample1.sqlj is as follows:
Adding customer Jason Price using conn_context.Updating customer 1 name to John Doe using conn_context.List of customers using default connection context.Customer:id = 1first_name = Johnlast_name = SmithCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = HeysonSwitching default connection context to conn_context.List of customers using default connection context.Customer:id = 1first_name = Johnlast_name = DoeCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = HeysonCustomer:id = 6first_name = Jasonlast_name = PriceExecution Contexts
A SQLJ executable statement contains an embedded SQL statement, which is said to run within an execution context. Each SQLJ executable statement uses an execution context that is either implicitly or explicitly associated with it. An execution context is an object of the class
sqlj.runtime.ExecutionContext. TheExecutionContextclass provides a number of useful methods that you can use to control how the SQL statement is run. You can also use these methods to get information on the results of the previously executed SQL statement, and to enable batch processing (see Chapter 10 for details on batch processing).Execution contexts are also used in multithreaded SQLJ applications, as you will see later in this chapter. An execution context should not be confused with a connection context: a connection context is used to specify a connection to a database; an execution context is used to run a SQL statement. I know all this may sound confusing, but it will make more sense once you look at some examples.
The Default Execution Context
A default execution context is created for every connection context. When you create a default connection context via a call to the
Oracle.connect( )method, a default execution context is also created. Therefore, if you create five connection contexts, you will also get five execution contexts.No special effort on your part is required to use a connection's default execution context when executing a SQL statement. For example, the following statement updates customer #1 using the default execution context associated with the default connection context:
#sql {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};There is nothing special about this example: it simply uses the default execution context associated with the default connection context. The following example is another variation on this same theme, and uses the default execution context associated with the connection context
conn_context, created earlier in this chapter, to perform the same update:#sql [conn_context] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};You can retrieve the default execution context for a connection by using the
getExecutionContext( )method of the connection context object. The following statement declares an execution context namedexec_context, and retrieves the default execution context from the default connection context using thegetDefaultContext( )andgetExecutionContext( )methods:ExecutionContext exec_context =DefaultContext.getDefaultContext.getExecutionContext( );The following example does much the same thing, but uses a connection that you've created. It declares an execution context named
exec_context2, and retrieves the default execution context fromconn_contextusing thegetExecutionContext( )method:ExecutionContext exec_context2 = conn_context.getExecutionContext( );Once you have your execution context, you can use the execution context methods. These are described in the next section.
Execution Context Methods
The
ExecutionContextclass provides a number of useful methods that may be used in your SQLJ programs by importing thesqlj.runtime.ExecutionContextclass. The more useful methods in theExecutionContextclass are as follows:
getWarnings( )
getUpdateCount( )
setQueryTimeout( )
getQueryTimeout( )
setMaxRows( )
getMaxRows( )
The following sections describe each of these methods in detail.
getWarnings( )
The
getWarnings( )method returns ajava.sql.SQLWarningobject that contains the first warning from the most recently executed SQLJ statement within the execution context. The following statement declares aSQLWarningobject namedsql_warningand copies the results of a call to thegetWarnings( )method into it:SQLWarning sql_warning = exec_context.getWarnings( );The
SQLWarningclass contains the methodgetSQLState( ), which returns a string containing the text of the SQL warning. The following statement makes use of this method to display the SQL warning string for thesql_warningobject just created:System.out.println(sql_warning.getSQLState( ));getUpdateCount( )
The
getUpdateCount( )method returns anintvalue indicating the number of rows modified by the last SQLJ statement executed within the specified execution context. For example, the following statement displays the number of rows modified by the last SQLJ statement executed in theexec_contextexecution context:System.out.println(exec_context.getUpdateCount( ));setQueryTimeout( )
The
setQueryTimeout( )method changes the amount of time a query will wait before a timeout occurs;setQueryTimeout( )accepts anintvalue that represents the timeout duration in seconds. The default is 0, which means that there is no timeout. The following statement sets the query timeout to 30 seconds for the execution context namedexec_context:exec_context.setQueryTimeout(30);getQueryTimeout( )
The
getQueryTimeout( )method returns anintvalue that represents the amount of time in seconds a query will wait before a timeout occurs. The following statement displays the query timeout for the execution context namedexec_context:System.out.println(exec_context.getQueryTimeout( ));setMaxRows( )
The
setMaxRows( )method changes the maximum number of rows that may be returned by an iterator. The default is 0, which means that there is no maximum number of rows set. The following statement sets the maximum number of rows to 30 for the execution context namedexec_context:exec_context.setMaxRows(30);getMaxRows( )
The
getMaxRows( )method returns anintvalue that represents the maximum number of rows that may be returned by an iterator. The following statement displays the maximum number of rows that theexec_contextexecution context allows to be stored in an iterator:System.out.println(exec_context.getMaxRows( ));Creating a New Execution Context
Execution contexts may be created using the
ExecutionContextclass. As you will soon see, creating execution contexts is very important when writing multithreaded programs. The following statement creates an execution context namedexec_context:ExecutionContext exec_context = new ExecutionContext( );When you create an execution context, it isn't necessarily tied to a particular connection context: you can use it with any connection context. The only execution context tied to a particular connection context is the default execution context for that connection context. The next section shows you how to specify which execution context to use in a SQLJ statement.
Specifying an Execution Context
You can specify connection contexts and execution contexts together in an embedded SQL operation. The following syntax illustrates how to do this:
#sql [connection_context_name, execution_context_name] { SQL_statement };The syntax elements are as follows:
- connection_context_name
- The name of the connection context to use when executing the statement
- execution_context_name
- The name of the execution context in which to execute the statement
- SQL_statement
- The embedded SQL statement that you want to execute[2]
TIP: You can specify both a connection context and an execution context. If you do, the connection context must come first.
Both the connection context name and the execution context name are optional. If you specify a connection context without specifying an execution context, then the default execution context for that connection context is used to perform the SQL statement. Similarly, if you specify an execution context without specifying a connection context, then the default connection context is used. If you specify neither a connection context nor an execution context, then the default connection and execution contexts are used.
TIP: SQLJ statements that use the same connection context share the same database transaction even if they use different execution contexts.
In the following example, only an execution context is specified. The execution context
exec_contextis used together with the default connection context to update customer #1:#sql [exec_context] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};In this next example, both a connection context and an execution context are specified. The connection context
conn_contextis used along with the execution contextexec_contextto update customer #2:#sql [conn_context, exec_context] {UPDATEcustomersSETfirst_name = 'Jean',last_name = 'Smith'WHEREid = 2};Because this example uses a connection context different from that used in the first example, it also uses a different database transaction. The following example uses the same connection context as the previous example (
conn_context), but a different execution context:#sql [conn_context, exec_context2] {UPDATEcustomersSETfirst_name = 'Fred',last_name = 'Doe'WHERE id = 2};Because this example uses the same connection context as the previous one, it also shares the same database transaction. Therefore, the customer name for customer #2 will be changed from "Jean Smith" to "Fred Doe", overwriting the previous update.
Example Program: ContextExample2.sqlj
This section contains a complete program that illustrates the transactional behavior of execution contexts. The program ContextExample2.sqlj (Example 8-2) performs the following major steps:
- Creates a connection context, named
conn_context, that connects to thefundamental_userschema.
- Creates two execution contexts, named
exec_contextandexec_context2.
- Displays all rows in the
customerstable using the program'sdisplayCustomers( )method.
- Updates the customer name to "John Doe" for customer #1 using
conn_contextandexec_context.
- Updates the customer name to "Jean Smith" for customer #1 using
conn_contextandexec_context2. This overwrites the previous update, illustrating that SQLJ statements that use the same connection context share the same database transaction.
- Displays all the rows in the
customerstable, showing the change made to customer #1.
- Rolls back the changes made to the
customerstable.
Example 8-2: ContextExample2.sqlj /*The program ContextExample2.sqlj illustrates how to createand use execution context objects.*/import java.sql.*;import oracle.sqlj.runtime.Oracle;import sqlj.runtime.ref.DefaultContext;import sqlj.runtime.ExecutionContext;public class ContextExample2 {// declare the iterator class#sql private static iterator CustomerIteratorClass (int id, String first_name, String last_name);public static void main(String [] args) {try {// connect to database using a connection context// named conn_contextDefaultContext conn_context = Oracle.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");// create an execution context named exec_contextExecutionContext exec_context = new ExecutionContext( );// create a second execution context named exec_context2ExecutionContext exec_context2 = new ExecutionContext( );// display all customers using conn_context and exec_contextSystem.out.println("List of customers using connection context " +"conn_context and execution context exec_context.");displayCustomers(conn_context, exec_context);// update customer using conn_context and exec_contextSystem.out.println("Updating customer 1 name to John Doe using " +"conn_context and exec_context.");#sql [conn_context, exec_context] {UPDATEcustomersSETfirst_name = 'John',last_name = 'Doe'WHEREid = 1};// update customer using conn_context and exec_context2,System.out.println("Updating customer 1 name to Jean Smith using " +"conn_context and exec_context2.");#sql [conn_context, exec_context2] {UPDATEcustomersSETfirst_name = 'Jean',last_name = 'Smith'WHEREid = 1};// display all customers using the conn_context and exec_contextSystem.out.println("List of customers using conn_context " +"and exec_context.");displayCustomers(conn_context, exec_context);// perform an explicit ROLLBACK using conn_context and exec_context#sql [conn_context, exec_context] { ROLLBACK };// close the connection contextconn_context.close( );} catch ( SQLException e ) {System.err.println("SQLException " + e);System.exit(1);}} // end of main( )private static void displayCustomers(DefaultContext conn_context,ExecutionContext exec_context) throws SQLException {// instantiate a named iterator objectCustomerIteratorClass customer_iterator;#sql [conn_context, exec_context] customer_iterator = {SELECTid, first_name, last_nameFROMcustomersORDER BYid};// access the contents of the iteratorwhile (customer_iterator.next( )) {System.out.println("Customer:");System.out.println("id = " + customer_iterator.id( ));System.out.println("first_name = " + customer_iterator.first_name( ));System.out.println("last_name = " + customer_iterator.last_name( ));} // end of while loop// close the iteratorcustomer_iterator.close( );} // end of displayCustomers( )}The output from ContextExample2.sqlj is as follows:
List of customers using connection context conn_context and execution context exec_context.Customer:id = 1first_name = Johnlast_name = SmithCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = HeysonUpdating customer 1 name to John Doe using conn_context and exec_context.Updating customer 1 name to Jean Smith using conn_context and xec_context2.List of customers using conn_context and exec_context.Customer:id = 1first_name = Jeanlast_name = SmithCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = HeysonMultithreaded SQL J Programs
A Java program can perform a number of actions in parallel, with each action being performed by a separate process. Such a process is also known as a thread. For example, a multithreaded program might perform database access using one thread while another thread handles the display. In the case of a SQLJ program, you might want to perform several SQL operations in parallel using a separate thread for each one. Multithreading is a very powerful feature of the Java language. The downside to writing a multithreaded program is the additional complexity of dealing with the thread model.
If a SQLJ program were to use multiple threads, then the program would run faster, right? Not always: it depends on where the program runs. If the program is deployed in the Oracle JServer JVM (described in Chapter 6), then the multithreaded program may not run faster than a comparable program that is not multithreaded. This is because the JServer JVM executes each thread serially through a single operating system thread and schedules the execution of each thread using a round-robin algorithm. However, this doesn't mean that the JServer JVM is slow! The JServer JVM has other ways to increase the performance of Java programs. If a SQLJ program is not going to be run using the JServer JVM, then the program may benefit from the use of threads.
Designing a Multithreaded SQL J Program
How would you structure a SQLJ program to use threads? The basic problem that must be solved is that when a program has two or more threads that attempt to modify the same row, the threads may conflict with one another. One thread may begin to make modifications to a row, only to have those modifications overwritten by a second thread that is modifying the same row. This is known as a race condition, and might occur when the threads use the same execution context. There are two solutions to this problem:
- Have each thread use a different execution context
- Have each thread use a different connection context
Both these solutions basically result in each thread using a different execution context, solving the race condition problem. If you use the same connection context for the threads, you should explicitly specify different execution contexts for each thread. Each connection context has its own default execution context, so if you use different connection contexts for each thread, then by default they will use different execution contexts.
Example Program: MultithreadedExample1.sqlj
This section contains a complete program that illustrates how to write a multithreaded SQLJ program. The program MultithreadedExample1.sqlj (Example 8-3) uses two threads, each of which uses a separate execution context to update the same row in the
customerstable. Because this program requires a little more explanation than the others in this chapter, I have provided a full description of the program after the program listing.
Example 8-3: MultithreadedExample1.sqlj /*The program MultithreadedExample1.sqlj illustrates the use ofmultithreading to update customer names.*/import java.sql.*;import oracle.sqlj.runtime.Oracle;import sqlj.runtime.ExecutionContext;import java.util.Random;public class MultithreadedExample1 extends Thread {// declare the iterator class#sql private static iterator CustomerIteratorClass (int id, String first_name, String last_name);int customer_id;String first_name;String last_name;MultithreadedExample1(int customer_id,String first_name,String last_name) {this.customer_id = customer_id;this.first_name = first_name;this.last_name = last_name;}public static void main(String [] args) {try {Oracle.connect("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");// display all customersdisplayCustomers( );MultithreadedExample1 thread1 =new MultithreadedExample1(1, "John", "Doe");MultithreadedExample1 thread2 =new MultithreadedExample1(1, "Jean", "Smith");// start the threads using the start( ) methodthread1.start( );thread2.start( );// wait for each thread to complete using the join( ) methodthread1.join( );thread2.join( );// display all customersdisplayCustomers( );#sql { ROLLBACK };Oracle.close( );} catch ( SQLException e ) {System.err.println("SQLException " + e);System.exit(1);} catch ( Exception e ) {System.err.println("Exception " + e);System.exit(1);}} // end of main( )public void run( ) {try {System.out.println("Updating customer " + customer_id +" name to " + first_name + " " + last_name + ".");// create new execution contextExecutionContext exec_context = new ExecutionContext( );#sql [exec_context] {UPDATEcustomersSETfirst_name = :first_name,last_name = :last_nameWHEREid = :customer_id};} catch ( SQLException e ) {System.err.println("SQLException " + e);System.exit(1);} catch ( Exception e ) {System.err.println("Exception " + e);System.exit(1);}} // end of run( )private static void displayCustomers( )throws SQLException {// declare a named iterator objectCustomerIteratorClass customer_iterator;#sql customer_iterator = {SELECTid, first_name, last_nameFROMcustomersORDER BYid};the contents of the iteratorwhile (customer_iterator.next( )) {System.out.println("Customer:");System.out.println("id = " + customer_iterator.id( ));System.out.println("first_name = " + customer_iterator.first_name( ));System.out.println("last_name = " + customer_iterator.last_name( ));} // end of while loop// close the iteratorcustomer_iterator.close( );} // end of displayCustomers( )}The following sections describe some of the more important aspects of this example program.
The MultithreadedExample1 class
The
MultithreadedExample1class extends theThreadclass; this indicates that the program is multithreaded. TheMultithreadedExample1class contains the following three variables that are used to hold information from thecustomertable:int customer_id;String first_name;String last_name;The constructor for the class
MultithreadedExample1accepts three parameters. These are used to initialize the three variables when a new object of that class is created:MultithreadedExample1(int customer_id,String first_name,String last_name) {this.customer_id = customer_id;this.first_name = first_name;this.last_name = last_name;}The main( ) method
Using a call to
Oracle.connect( ), themain( )method creates a default connection context that connects to thefundamental_userschema:Oracle.connect("jdbc:oracle:thin:@localhost:1521:orcl","fundamental_user","fundamental_password");All the rows in the
customerstable are then selected and displayed via a call to thedisplayCustomers( )method:displayCustomers( );Next, two threads are created. These are named
thread1andthread2:MultithreadedExample1 thread1 =new MultithreadedExample1(1, "John", "Doe");MultithreadedExample1 thread2 =new MultithreadedExample1(1, "Jean", "Smith");The thread named
thread1sets thecustomer_id,first_name, andlast_nameobject variables to 1, "John", and "Doe" respectively. The thread namedthread2sets thecustomer_id,first_name, andlast_nameobject variables to 1, "Jean", and "Smith" respectively. Each thread is an instance of theMultithreadedExample1class, and the class's constructor is used to initialize the object variables when the two thread objects are created. The two threads are then started by calling each thread'sstart( )method:thread1.start( );thread2.start( );You don't have to write the
start( )method: it comes with theThreadclass. You do, however, have to write arun( )method in your class, and thestart( )method calls thatrun( )method. Therun( )method updates thefirst_nameandlast_namecolumns of the row in thecustomerstable for the customer whose ID number is specified in thecustomer_idobject variable. I will describe more details of therun( )method shortly. After starting the two threads, the program waits until both threads are completed. This is done by calling thejoin( )method for the two threads:thread1.join( );thread2.join( );The next statement in the
main( )method is not executed until both threads are completed. At that point, all the rows in thecustomerstable are displayed again via another call to thedisplayCustomers( )method:displayCustomers( );Finally, the
main( )method rolls back the changes made to thecustomerstable by therun( )methods for the two threads and disconnects from the database:#sql { ROLLBACK };Oracle.close( );The run( ) method
The
run( )method begins by creating an execution context namedexec_context:ExecutionContext exec_context = new ExecutionContext( );In this way, each thread has its own execution context, thereby preventing any race condition that might otherwise occur. The execution context uses the database transaction established by the default connection context at the start of the
main( )method. Both execution contexts created by the two threads share this database transaction.The execution context
exec_contextis then used to update thefirst_nameandlast_namecolumns for the row with anidequal to the value stored in thecustomer_idvariable. The values for thefirst_nameandlast_namecolumns come from the variablesfirst_nameandlast_name:#sql [exec_context] {UPDATEcustomersSETfirst_name = :first_name,last_name = :last_nameWHEREid = :customer_id};Program output
The output from MultithreadedExample1.sqlj is as follows:
Customer:id = 1first_name = Johnlast_name = SmithCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = HeysonUpdating customer 1 name to John Doe.Updating customer 1 name to Jean Smith.Customer:id = 1first_name = Jeanlast_name = SmithCustomer:id = 2first_name = Cynthialast_name = StevensCustomer:id = 3first_name = Stevelast_name = SeymourCustomer:id = 4first_name = Gaillast_name = WilliamsCustomer:id = 5first_name = Doreenlast_name = Heyson
1. Actually, this is a simplification. In the next chapter, I discuss the default transactional behavior and how to change it.
2. In the case of a FETCH statement, the connection context and execution context originally used to populate the iterator is used.
Back to: Java Programming with Oracle SQLJ
© 2001, O'Reilly & Associates, Inc.
webmaster@oreilly.com