JDBC
JDBC
JDBC
Server and ®
WebLogic
Express ®
Programming WebLogic
JDBC
Version 10.0
Revised: March 30, 2007
Copyright
Copyright © 1995-2007 BEA Systems, Inc. All Rights Reserved.
7. Troubleshooting JDBC
Problems with Oracle on UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-1
Thread-related Problems on UNIX . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-1
Closing JDBC Objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-2
Abandoning JDBC Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-3
Using Microsoft SQL with Nested Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-3
Exceeding the Nesting Level . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-4
Using Triggers and EJBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7-5
This section describes the contents and organization of this guide—Programming WebLogic
JDBC.
z Chapter 2, “Using WebLogic JDBC in an Application,” which explains how to use a JDBC
connection in your application.
z Chapter 3, “Performance Tuning Your JDBC Application,” which describes how to design
JDBC connection usage in your applications for the best performance.
z Chapter 4, “Using WebLogic Wrapper Drivers,” which describes how to use some
alternative drivers for getting a JDBC connection from a data source.
z Chapter 5, “Using Third-Party Drivers with WebLogic Server,” which describes special
programming considerations for third-party drivers in your applications.
z Chapter 6, “Using RowSets with WebLogic Server,” which describes how to use rowsets in
your applications.
z Chapter 7, “Troubleshooting JDBC,” which describes some common JDBC problems and
solutions.
Related Documentation
This document contains JDBC-specific programming information.
For comprehensive guidelines for developing, deploying, and monitoring WebLogic Server
applications, see the following documents:
You use the WebLogic Server Administration Console to enable, configure, and monitor features
of WebLogic Server, including JDBC data sources and multi data sources. You can do the same
tasks programmatically using the JMX API and the WebLogic Scripting Tool (WLST). After
configuring JDBC connectivity components, you can use them in your applications.
The following sections describe how to use the JDBC connectivity in your applications.
try {
ctx = new InitialContext(ht);
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup ("myDataSource");
conn = ds.getConnection();
stmt = conn.createStatement();
stmt.execute("select * from someTable");
rs = stmt.getResultSet();
...
conn.close();
conn=null;
}
catch (Exception e) {
// a failure occurred
log message;
}
finally {
try {
ctx.close();
} catch (Exception e) {
log message; }
try {
if (rs != null) rs.close();
} catch (Exception e) {
log message; }
try {
if (stmt != null) stmt.close();
} catch (Exception e) {
log message; }
try {
if (conn != null) conn.close();
} catch (Exception e) {
log message; }
}
(Substitute the correct hostname and port number for your WebLogic Server.)
Note: The code above uses one of several available procedures for obtaining a JNDI context.
For more information on JNDI, see Programming WebLogic JNDI at
http://e-docs.bea.com/wls/docs100/jndi/index.html.
The following sections explain how to get the best performance from JDBC applications:
important table or index can be read once from disk and remain available to all clients without
having to access the disk again.
This returns the number of rows the original query would have returned, assuming no change in
relevant data. The actual count may change when the query is issued if other DBMS activity has
occurred that alters the relevant data.
Be aware, however, that this is a resource-intensive operation. Depending on the original query,
the DBMS may perform nearly as much work to count the rows as it will to send them.
Make your application queries as specific as possible about what data it actually wants. For
example, tailor your application to select into temporary tables, returning only the count, and then
sending a refined second query to return only a subset of the rows in the temporary table.
Learning to select only the data you really want at the client is crucial. Some applications ported
from ISAM (a pre-relational database architecture) will unnecessarily send a query selecting all
the rows in a table when only the first few rows are required. Some applications use a 'sort by'
clause to get the rows they want to come back first. Database queries like this cause unnecessary
degradation of performance.
Proper use of SQL can avoid these performance problems. For example, if you only want data
about the top three earners on the payroll, the proper way to make this query is with a correlated
subquery. Table 3-1 shows the entire table returned by the SQL statement
select * from payroll
Joe 10
Mike 20
Sam 30
Tom 40
Jan 50
Ann 60
Sue 70
Hal 80
May 80
A correlated subquery
select p.name, p.salary from payroll p
Sue 70
Hal 80
May 80
This query returns only three rows, with the name and salary of the top three earners. It scans
through the payroll table, and for every row, it goes through the whole payroll table again in an
inner loop to see how many salaries are higher than the current row of the outer scan. This may
look complicated, but DBMSs are designed to use SQL efficiently for this type of operation.
UPDATE TABLE1...
DELETE TABLE3
COMMIT
This approach results in better performance than using separate statements and commits. Even
with conditional logic and temporary tables in the batch, it is preferable because the DBMS
obtains all the locks necessary on the various rows and tables, and uses and releases them in one
step. Using separate statements and commits results in many more client-to-DBMS transmissions
and holds the locks in the DBMS for much longer. These locks will block out other clients from
accessing this data, and, depending on whether different updates can alter tables in different
orders, may cause deadlocks.
Warning: If any individual statement in the preceding transaction fails, due, for instance, to
violating a unique key constraint, you should put in conditional SQL logic to detect statement
failure and to roll back the transaction rather than commit. If, in the preceding example, the insert
failed, most DBMSs return an error message about the failed insert, but behave as if you got the
message between the second and third statement, and decided to commit anyway! Microsoft SQL
Server offers a connection option enabled by executing the SQL set xact_abort on, which
automatically rolls back the transaction if any statement fails.
design your application and data to support lots of parallel processes working on easily
distinguished subsets of the work, your application will be much faster. If there are multiple steps
to processing, try to design your application so that subsequent steps can start working on the
portion of data that any prior process has finished, instead of having to wait until the prior process
is complete. This may not always be possible, but you can dramatically improve performance by
designing your program with this in mind.
BEA recommends that you use DataSource objects to get database connections in new
applications. DataSource objects (WebLogic data sources and multi data sources), along with the
JNDI tree, provide access to pooled connections in a data source for database connectivity. The
WebLogic wrapper drivers are deprecated. For existing or legacy applications that use the JDBC
1.x API, you can use the WebLogic wrapper drivers to get database connectivity.
The following sections describe how to use WebLogic wrapper drivers with WebLogic Server:
z Using a JNDI lookup. This is the preferred and most direct procedure.
z Passing the DataSource name to the RMI driver with the Driver.connect()method. In
this case, WebLogic Server performs the JNDI look up on behalf of the client.
ht.put(Context.PROVIDER_URL,
"t3://hostname:port");
try {
ctx = new InitialContext(ht);
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup ("myDataSource");
java.sql.Connection conn = ds.getConnection();
log message; }
}
(Where hostname is the name of the machine running your WebLogic Server and port is the
port number where that machine is listening for connection requests.)
In this example a Hashtable object is used to pass the parameters required for the JNDI lookup.
There are other ways to perform a JNDI lookup. For more information, see Programming
WebLogic JNDI at http://e-docs.bea.com/wls/docs100/jndi/index.html.
Notice that the JNDI lookup is wrapped in a try/catch block in order to catch a failed look up
and also that the context is closed in a finally block.
(Where hostname is the name of the machine running your WebLogic Server and port is the
port number where that machine is listening for connection requests.)
You can also define the following properties which will be used to set the JNDI user information:
z weblogic.user—specifies a username
1. If you have not already done so, in the Change Center of the Administration Console, click
Lock & Edit.
2. In the Domain Structure tree, expand Services > JDBC, then select Data Sources.
3. On the Summary of Data Sources page, click the data source name.
b. In Row Prefetch Size, type the number of rows you want to cache for each
ResultSet.next() call.
5. Click Save.
6. To activate these changes, in the Change Center of the Administration Console, click Activate
Changes.
See the JDBC Data Source: Configuration: General page in the Administration Console Online
Help.
Important Limitations for Row Caching with the WebLogic RMI Driver
Keep the following limitations in mind if you intend to implement row caching with the RMI
driver:
z WebLogic Server only performs row caching if the result set type is both
TYPE_FORWARD_ONLY and CONCUR_READ_ONLY.
z Certain data types in a result set may disable caching for that result set. These include the
following:
– LONGVARCHAR/LONGVARBINARY
– NULL
– BLOB/CLOB
– ARRAY
– REF
– STRUCT
– JAVA_OBJECT
z Certain ResultSet methods are not supported if row caching is enabled and active for that
result set. Most pertain to streaming data, scrollable result sets or data types not supported
for row caching. These include the following:
– getAsciiStream()
– getUnicodeStream()
– getBinaryStream()
– getCharacterStream()
– isBeforeLast()
– isAfterLast()
– isFirst()
– isLast()
– getRow()
– getObject (Map)
– getRef()
– getBlob()/getClob()
– getArray()
– getDate()
– getTime()
– getTimestamp()
1. When the RMI call returns, the global transaction is suspended automatically by the server
instance.
2. The JDBC driver invalidates the pending ResultSet object to release the system resources.
4. A "Fetch Out Of Sequency" exception is thrown if that data has not been prefetched. Since
the number of rows prefetched is vendor specific, you may or may not encounter this issue,
especially when working with one or two rows.
If you encounter this exception, make sure to populate the RowSet on the server side and then
serialize it back to the client.
Although Java clients may not register the JTS driver themselves, they may participate in
transactions via Remote Method Invocation (RMI). You can begin a transaction in a thread on a
client and then have the client call a remote RMI object. The database operations executed by the
remote object become part of the transaction that was begun on the client. When the remote object
is returned back to the calling client, you can then commit or roll back the transaction. The
database operations executed by the remote objects must all use the same data source to be part
of the same transaction.
For the JTS driver and your application to participate in a global transaction, the application must
call conn = myDriver.connect("jdbc:weblogic:jts", props); within a global
transaction. After the transaction completes (gets committed or rolled back), WebLogic Server
puts the connection back in the data source. If you want to use a connection for another global
transaction, the application must call conn = myDriver.connect("jdbc:weblogic:jts",
props); again within a new global transaction.
2. Establish the transaction by using the UserTransaction class. You can look up this class on
the JNDI tree. The UserTransaction class controls the transaction on the current execute
thread. Note that this class does not represent the transaction itself. The actual context for the
transaction is associated with the current execute thread.
Context ctx = null;
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
UserTransaction tx = (UserTransaction)
ctx.lookup("javax.transaction.UserTransaction");
6. Execute your database operations. These operations may be made by any service that uses a
database connection, including EJB, JMS, and standard JDBC statements. These operations
must use the JTS driver to access the same data source as the transaction begun in step 3 in
order to participate in that transaction.
If the additional database operations using the JTS driver use a different data source than
the one specified in step 5, an exception will be thrown when you try to commit or roll
back the transaction.
7. Close your connection objects. Note that closing the connections does not commit the
transaction nor return the connection to the pool:
conn.close();
8. Complete the transaction by either committing the transaction or rolling it back. In the case
of a commit, the JTS driver commits all the transactions on all connection objects in the
current thread and returns the connection to the pool.
tx.commit();
// or:
tx.rollback();
The following sections describe how to set up and use third-party JDBC drivers:
z “Using Oracle Extensions with the Oracle Thin Driver” on page 5-12
z “Support for Vendor Extensions Between Versions of WebLogic Server Clients and
Servers” on page 5-28
try {
ctx = new InitialContext(ht);
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup ("myDataSource");
conn = ds.getConnection();
stmt = conn.createStatement();
stmt.execute("select * from someTable");
rs = stmt.getResultSet();
...
conn.close();
conn=null;
}
catch (Exception e) {
// a failure occurred
log message;
}
finally {
try {
ctx.close();
} catch (Exception e) {
log message; }
try {
if (rs != null) rs.close();
} catch (Exception e) {
log message; }
try {
if (stmt != null) stmt.close();
} catch (Exception e) {
log message; }
try {
if (conn != null) conn.close();
} catch (Exception e) {
log message; }
}
(Where hostname is the name of the machine running your WebLogic Server and port is the
port number where that machine is listening for connection requests.)
In this example a Hashtable object is used to pass the parameters required for the JNDI lookup.
There are other ways to perform a JNDI lookup. For more information, see Programming
WebLogic JNDI at http://e-docs.bea.com/wls/docs100/jndi/index.html.
Notice that the JNDI lookup is wrapped in a try/catch block in order to catch a failed look up
and also that the context is closed in a finally block.
Opening a Connection
To get a physical database connection, you first get a connection from a connection pool as
described in “Using a JNDI Lookup to Obtain the Connection” on page 5-2, then do one of the
following:
try {
ctx = new InitialContext(ht);
// Look up the data source on the JNDI tree and request
// a connection.
javax.sql.DataSource ds
= (javax.sql.DataSource) ctx.lookup ("myDataSource");
java.sql.Connection vendorConn =
((WLConnection)conn).getVendorConnection();
// do not close vendorConn
Closing a Connection
When you are finished with your JDBC work, you should close the logical connection to get it
back into the pool. When you are done with the physical connection:
z Do not close the physical connection. Set the physical connection to null.
You determine how a connection closes by setting the value of the Remove Infected
Connections Enabled property in the administration console. See the JDBC Data Source:
Configuration: Connection Pool page in the Administration Console Help or see
“JDBCConnectionPoolParamsBean” in the WebLogic Server MBean Reference for more details
about these options
Note: The Remove Infected Connections Enabled property applies only to applications
that explicitly call getVendorConnection.
catch (Exception e)
{
// Handle the exception.
}
finally
{
// For safety, check whether the logical (pooled) connection
// was closed.
// Always close the logical (pooled) connection as the
// first step in the finally block.
if (conn != null) try {conn.close();} catch (Exception ignore){}
}
}
z The physical connection is replaced with a new database connection in the connection
pool, which uses resources on both the application server and the database server.
z The statement cache for the original connection is closed and a new cache is opened for the
new connection. Therefore, the performance gains from using the statement cache are lost.
connection is closed. You must make sure that the connection is suitable for reuse by other
applications before it is returned to the connection pool.
z When you use a physical connection, you lose all of the connection management benefits
that WebLogic Server offer, such as error handling and statement caching.
z You should use the physical connection only for the vendor-specific methods or classes
that require it. Do not use the physical connection for generic JDBC, such as creating
statements or transactional calls.
z CLOB
z BLOB
z InputStream
z OutputStream
WebLogic Server handles de-serialization for these object types so they can be used in client-side
code.
Note: There are interoperability limitations when using different versions of WebLogic Server
clients and servers. See “Support for Vendor Extensions Between Versions of WebLogic
Server Clients and Servers” on page 5-28.
To use the extension methods exposed in the JDBC driver, you must include these steps in your
application code:
z Import the driver interfaces from the JDBC driver used to create connections in the data
source.
Get a Connection
Establish the database connection using JNDI, DataSource and data source objects. For
information, see “Using a JNDI Lookup to Obtain the Connection” on page 5-2.
// Get a valid DataSource object for a data source.
// Here we assume that getDataSource() takes
// care of those details.
javax.sql.DataSource ds = getDataSource(args);
int default_prefetch =
((oracle.jdbc.OracleConnection)conn).getDefaultRowPrefetch();
// This replaces the deprecated process of casting the connection
// to a weblogic.jdbc.vendor.oracle.OracleConnection. For example:
// ((weblogic.jdbc.vendor.oracle.OracleConnection)conn).
// getDefaultRowPrefetch();
((oracle.jdbc.OracleStatement)stmt).setRowPrefetch(20);
while(rs.next()) {
java.math.BigDecimal empno = rs.getBigDecimal(1);
String ename = rs.getString(2);
System.out.println(empno + "\t" + ename);
}
rs.close();
stmt.close();
conn.close();
conn = null;
}
z You can use Oracle extensions for ARRAYs, REFs, and STRUCTs in server-side
applications that use the same JVM as the server only. You cannot use Oracle extensions
for ARRAYs, REFs, and STRUCTs in remote client applications.
z You cannot create ARRAYs, REFs, and STRUCTs in your applications. You can only
retrieve existing ARRAY, REF, and STRUCT objects from a database. To create these
objects in your applications, you must use a non-standard Oracle descriptor object, which
is not supported in WebLogic Server.
z There are interoperability limitations when using different versions of WebLogic Server
clients and servers. See “Support for Vendor Extensions Between Versions of WebLogic
Server Clients and Servers” on page 5-28.
z BLOBs and CLOBs—See “Programming with BLOBs and CLOBs” on page 5-25.
If you selected the option to install server examples with WebLogic Server, see the JDBC
examples for more code examples, typically at
WL_HOME\samples\server\src\examples\jdbc, where WL_HOME is the folder where you
installed WebLogic Server.
5. Use the standard Java methods (when used as a java.sql.Array) or Oracle extension
methods (when cast as a weblogic.jdbc.vendor.oracle.OracleArray) to work with the
data.
The following sections provide more details for these actions.
Note: You can use ARRAYs in server-side applications only. You cannot use ARRAYs in
remote client applications.
Getting an ARRAY
You can use the getArray() methods for a callable statement or a result set to get a Java array.
You can then use the array as a java.sql.array to use standard java.sql.array methods, or
you can cast the array as a weblogic.jdbc.vendor.oracle.OracleArray to use the Oracle
extension methods for an array.
The following example shows how to get a java.sql.array from a result set that contains an
ARRAY. In the example, the query returns a result set that contains an object column—an
ARRAY of test scores for a student.
try {
conn = getConnection(url);
stmt = conn.createStatement();
String sql = "select * from students";
//Get the result set
rs = stmt.executeQuery(sql);
while(rs.next()) {
BigDecimal id = rs.getBigDecimal("student_id");
String name = rs.getString("name");
log("ArraysDAO.getStudents() -- Id = "+id.toString()+", Student =
"+name);
//Get the array from the result set
Array scoreArray = rs.getArray("test_scores");
String[] scores = (String[])scoreArray.getArray();
for (int i = 0; i < scores.length; i++) {
log(" Test"+(i+1)+" = "+scores[i]);
}
}
1. Create an array in the database using PL/SQL, if the array you want to update does not already
exist in the database.
4. Update the array in the database using the setArray() method for a prepared statement or a
callable statement. For example:
String sqlUpdate = "UPDATE SCOTT." + tableName + " SET col1 = ?";
conn = ds.getConnection();
pstmt = conn.prepareStatement(sqlUpdate);
pstmt.setArray(1, array);
pstmt.executeUpdate();
1. Import the required classes. (See “Import Packages to Access Oracle Extensions” on
page 5-14.)
4. Cast the STRUCT as a STRUCT, either java.sql.Struct (to use standard methods) or
weblogic.jdbc.vendor.oracle.OracleStruct (to use standard and Oracle extension
methods).
5. Use the standard or Oracle extension methods to work with the data.
The following sections provide more details for steps 3 through 5.
Getting a STRUCT
To get a database object as a STRUCT, you can use a query to create a result set and then use the
getObject method to get the STRUCT from the result set. You then cast the STRUCT as a
java.sql.Struct so you can use the standard Java methods. For example:
conn = ds.getConnection();
stmt = conn.createStatement();
struct = (java.sql.Struct)(rs.getObject(2));
WebLogic Server supports all of the JDBC API methods for STRUCTs:
z getAttributes()
z getAttributes(java.util.Dictionary map)
z getSQLTypeName()
Oracle supports the standard methods as well as the Oracle extensions. Therefore, when you cast
a STRUCT as a weblogic.jdbc.vendor.oracle.OracleStruct, you can use both the
standard and extension methods.
z getDescriptor()
z getOracleAttributes()
z getAutoBuffering()
z setAutoBuffering(boolean)
stmt = conn.createStatement();
struct = (java.sql.Struct)(rs.getObject(2));
In the preceding example, the third column in the people table uses an object data type. The
example shows how to assign the results from the getObject method to a Java object that
contains an array of values, and then use individual values in the array as necessary.
You can also use the getAttributes(java.util.Dictionary map) method to get the
attributes from a STRUCT. When you use this method, you must provide a hash table to map the
data types in the Oracle object to Java language data types. For example:
java.util.Hashtable map = new java.util.Hashtable();
map.put("NUMBER", Class.forName("java.lang.Integer"));
map.put("VARCHAR", Class.forName("java.lang.String"));
You can also use the Oracle extension method getOracleAttributes() to get the attributes for
a STRUCT. You must first cast the STRUCT as a
weblogic.jdbc.vendor.oracle.OracleStruct. This method returns a datum array of
oracle.sql.Datum objects. For example:
oracle.sql.Datum[] attrs =
((weblogic.jdbc.vendor.oracle.OracleStruct)struct).getOracleAttribut
es();
stmt = conn.createStatement();
ps.executeUpdate();
stmt = conn.createStatement();
stmt.execute(cmd);
stmt.execute(cmd);
stmt.execute(cmd);
Note: You cannot create STRUCTs in your applications. You can only retrieve existing objects
from a database and cast them as STRUCTs. To create STRUCT objects in your
applications, you must use a non-standard Oracle STRUCT descriptor object, which is
not supported in WebLogic Server.
((weblogic.jdbc.vendor.oracle.OracleStruct)struct).setAutoBuffering(true);
You can also use the getAutoBuffering() method to determine the automatic buffering mode.
z REFs are supported for use with Oracle only. To use REFs in your applications,
you must use the Oracle Thin Driver to communicate with the database, typically
through a data source.
z You can use REFs in server-side applications only.
To use REFs in WebLogic Server applications, follow these steps:
1. Import the required classes. (See “Import Packages to Access Oracle Extensions” on
page 5-14.)
4. Cast the result as a STRUCT or as a Java object. You can then manipulate data using STRUCT
methods or methods for the Java object.
You can also create and update a REF in the database.
The following sections describe these steps 3 and 4 in greater detail.
Getting a REF
To get a REF in an application, you can use a query to create a result set and then use the getRef
method to get the REF from the result set. You then cast the REF as a java.sql.Ref so you can
use the built-in Java method. For example:
conn = ds.getConnection();
stmt = conn.createStatement();
rs.next();
Note that the WHERE clause in the preceding example uses dot notation to specify the attribute
in the referenced object.
After you cast the REF as a java.sql.Ref, you can use the Java API method
getBaseTypeName, the only JDBC 2.0 standard method for REFs.
When you get a REF, you actually get a pointer to a value in an object table. To get or manipulate
REF values, you must use the Oracle extensions, which are only available when you cast the
sql.java.Ref as a weblogic.jdbc.vendor.oracle.OracleRef.
Getting a Value
Oracle provides two versions of the getValue() method—one that takes no parameters and one
that requires a hash table for mapping return types. When you use either version of the
getValue() method to get the value of an attribute in a REF, the method returns a either a
STRUCT or a Java object.
The example below shows how to use the getValue() method without parameters. In this
example, the REF is cast as an oracle.sql.STRUCT. You can then use the STRUCT methods to
manipulate the value, as illustrated with the getAttributes() method.
oracle.sql.STRUCT student1 =
(oracle.sql.STRUCT)((weblogic.jdbc.vendor.oracle.OracleRef)ref).getV
alue ();
You can also use the getValue(dictionary) method to get the value for a REF. You must
provide a hash table to map data types in each attribute of the REF to Java language data types.
For example:
java.util.Hashtable map = new java.util.Hashtable();
map.put("VARCHAR", Class.forName("java.lang.String"));
map.put("NUMBER", Class.forName("java.lang.Integer"));
z Change the value in the underlying table with the setValue(object) method.
z Change the location to which the REF points with a prepared statement or a callable
statement.
s1.setName("Terry Green");
s1.setAge(20);
((weblogic.jdbc.vendor.oracle.OracleRef)ref).setValue(s1);
When you update the value for a REF with the setValue(object) method, you actually update
the value in the table to which the REF points.
To update the location to which a REF points using a prepared statement, you can follow these
basic steps:
1. Get a REF that points to the new location. You use this REF to replace the value of another
REF.
2. Create a string for the SQL command to replace the location of an existing REF with the value
of the new REF.
conn = ds.getConnection();
stmt = conn.createStatement();
rs.next();
pstmt = conn.prepareStatement(sqlUpdate);
pstmt.setRef(1, ref);
pstmt.executeUpdate();
To use a callable statement to update the location to which a REF points, you prepare the stored
procedure, set any IN parameters and register any OUT parameters, and then execute the
statement. The stored procedure updates the REF value, which is actually a location. For
example:
conn = ds.getConnection();
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT ref (s) FROM t1 s where s.ob1=5");
rs.next();
cstmt = conn.prepareCall(sql);
cstmt.setRef(1, ref1);
// Execute
cstmt.execute();
The preceding example creates an object type (ob), a table (t1) of that object type, a table (t2)
with a REF column that can point to instances of ob objects, and inserts a REF into the REF
column. The REF points to a row in t1 where the value in the first column is 5.
myStatement = myConnect.createStatement();
rs = myStatement.executeQuery(selectBlob);
while (rs.next()) {
myRegularBlob = rs.getBlob("blobCol");
os = ((OracleThinBlob)myRegularBlob).getBinaryOutputStream();
...
Once you cast to the Oracle.ThinBlob interface, you can access the BEA supported methods.
remove the excess characters left after the update. See the Oracle documentation for more
information about the dbms_lob.trim procedure.
1. Create a JDBC data source in your WebLogic Server configuration that uses either the Oracle
Thin driver or the Oracle OCI driver. See “Using WebLogic JDBC in an Application” on
page 2-1 or “Create JDBC data sources” in the Administration Console Online Help.
Note: If you are using an XA-enabled version of the JDBC driver, you must set
KeepXAConnTillTxComplete=true. See “JDBC Data Source: Configuration:
Connection Pool” in the Administration Console Online Help.
Note: This code uses an underlying physical connection from a pooled (logical) connection.
See “Getting a Physical Connection from a Data Source” on page 5-4 for usage
guidelines.
Note: Standard JDBC interfaces are supported regardless of the client or server version.
weblogic.jdbc.vendor.oracle. oracle.jdbc.OracleConnection
OracleConnection
weblogic.jdbc.vendor.oracle. oracle.jdbc.OracleStatement
OracleStatement
weblogic.jdbc.vendor.oracle. oracle.jdbc.OraclePreparedStatement
OraclePreparedStatement
weblogic.jdbc.vendor.oracle. oracle.jdbc.OracleResultSet
OracleResultSet
The interfaces listed in Table 5-2 are still valid because Oracle does not provide interfaces to
access these extension methods.
weblogic.jdbc.vendor.oracle.OracleRef
weblogic.jdbc.vendor.oracle.OracleStruct
weblogic.jdbc.vendor.oracle.OracleThinClob
weblogic.jdbc.vendor.oracle.OracleThinBlob
The following tables describe the Oracle interfaces and supported methods you use with the
Oracle Thin Driver (or another driver that supports these methods) to extend the standard JDBC
(java.sql.*) interfaces.
String getUserName()
throws java.sql.SQLException;
boolean getBigEndian()
throws java.sql.SQLException;
boolean getIncludeSynonyms()
throws java.sql.SQLException;
boolean getRemarksReporting()
throws java.sql.SQLException;
boolean getReportRemarks()
throws java.sql.SQLException;
boolean isCompatibleTo816()
throws java.sql.SQLException;
(Deprecated)
byte[] getFDO(boolean b)
throws java.sql.SQLException;
int getDefaultRowPrefetch()
throws java.sql.SQLException;
int getStmtCacheSize()
throws java.sql.SQLException;
java.util.Properties getDBAccessProperties()
throws java.sql.SQLException;
short getStructAttrCsId()
throws java.sql.SQLException;
short getVersionNumber()
throws java.sql.SQLException;
void removeAllDescriptor()
throws java.sql.SQLException;
void removeDescriptor(String s)
throws java.sql.SQLException;
void setClientIdentifier(String s)
throws java.sql.SQLException;
void setDefaultAutoRefetch(boolean b)
throws java.sql.SQLException;
void setDefaultExecuteBatch(int i)
throws java.sql.SQLException;
void setDefaultRowPrefetch(int i)
throws java.sql.SQLException;
void setFDO(byte[] b)
throws java.sql.SQLException;
void setIncludeSynonyms(boolean b)
throws java.sql.SQLException;
void setStmtCacheSize(int i)
throws java.sql.SQLException;
void setUsingXAFlag(boolean b)
throws java.sql.SQLException;
void setXAErrorFlag(boolean b)
throws java.sql.SQLException;
void shutdown(int i)
throws java.sql.SQLException;
boolean getAutoRefetch()
throws java.sql.SQLException;
byte getSqlKind()
throws java.sql.SQLException;
int creationState()
throws java.sql.SQLException;
int getAutoRollback()
throws java.sql.SQLException;
(Deprecated)
int getRowPrefetch()
throws java.sql.SQLException;
int getWaitOption()
throws java.sql.SQLException;
(Deprecated)
int sendBatch()
throws java.sql.SQLException;
void describe()
throws java.sql.SQLException;
void setAutoRefetch(boolean b)
throws java.sql.SQLException;
void setAutoRollback(int i)
throws java.sql.SQLException;
(Deprecated)
void setRowPrefetch(int i)
throws java.sql.SQLException;
void setWaitOption(int i)
throws java.sql.SQLException;
(Deprecated)
void setAutoRefetch(boolean b)
throws java.sql.SQLException;
java.sql.ResultSet getCursor(int n)
throws java.sql.SQLException;
java.sql.ResultSet getCURSOR(String s)
throws java.sql.SQLException;
void registerOutParameter
(int i, int j, int k, int l)
throws java.sql.SQLException;
java.sql.ResultSet getCursor(int i)
throws java.sql.SQLException;
java.io.InputStream getAsciiStream(int i)
throws java.sql.SQLException;
java.io.InputStream getBinaryStream(int i)
throws java.sql.SQLException;
java.io.InputStream getUnicodeStream(int i)
throws java.sql.SQLException;
void setExecuteBatch(int i)
throws java.sql.SQLException;
About RowSets
WebLogic Server includes an implementation of Java RowSets according to the specifications
indicated in JSR-114. See the Sun Web site (http://java.sun.com/products/jdbc/download.html)
for details about the specification. The WebLogic rowset implementation also includes
extensions to the RowSets specification. These extensions make RowSets more useful in your
applications.
A rowset is an extension of a Java ResultSet. Like a ResultSet, a rowset is a Java object that holds
tabular data. However, a rowset adds significant flexibility to ResultSet features and reduces or
eliminates some ResultSet limitations.
Types of RowSets
The WebLogic Server implementation of rowsets includes the following rowset types and
utilities:
Standard RowSet Types:
z CachedRowSets
z FilteredRowSets
z WebRowSets
z JoinRowSets
z JDBCRowSets
z WLCachedRowSets
z SharedRowSets
z SortedRowSets
1. Create and configure the rowset — define the query, database connection, and other
properties.
2. Populate the rowset with data — specify query parameters and execute the query.
z If you have pending changes, you cannot re-populate, filter, or sort the rowset. WebLogic
Server prevents these operations on the rowset when the rowset data has changed but the
changes have not been synchronized with the database to prevent the accidental loss of
data changes.
z There is no implicit movement of the cursor! You must explicitly move the cursor from
row to row.
z Rowset lifecycle stage is an internal process. There are no public APIs to access it. You
cannot set the lifecycle stage. When you call acceptChanges() or restoreOriginal(),
WebLogic Server rests the lifecycle stage of the rowset so you can begin again.
Note: When using a rowset in a client-side application, the exact same JDBC driver classes
must be in the CLASSPATH on both the server and the client. If the driver classes do not
match, you may see java.rmi.UnmarshalException exceptions.
See the comments in Listing 6-1 for an illustration of the lifecycle stages for a rowset from when
it is created to when data changes are synchronized with the database.
CachedRowSets
The following sections describe using standard CachedRowSets with WebLogic Server:
Characteristics
A CachedRowSet is a disconnected ResultSet object. Data in a CachedRowSet is stored in
memory. CachedRowSets from the WebLogic Server implementation have the following
characteristics:
z Are serializable, so they can be passed to various application components, including thin
clients and wireless devices.
z Include transaction handling to enable rowset reuse. See “Reusing a WebLogic RowSet
After Completing a Transaction” on page 6-17.
z Use an optimistic concurrency control for synchronizing data changes in the rowset with
the database.
z Data Contention
Data Contention
CachedRowSets are most suitable for use with data that is not likely to be updated by another
process between when the rowset is populated and when data changes in the rowset are
synchronized with the database. Database changes during that period will cause data contention.
See “Handling SyncProviderExceptions with a SyncResolver” on page 6-28 for more
information about detecting and handling data contention.
Code Example
Listing 6-1 shows the basic workflow of a CachedRowSet. It includes comments that describe
each major operation and its corresponding rowset lifecycle stage. Following the code example
is a more detailed explanation of each of the major sections of the example.
import javax.sql.rowset.CachedRowSet;
import weblogic.jdbc.rowset.RowSetFactory;
try {
//MANIPULATING lifecycle stage - navigate to a row
//(manually moving the cursor)
rs.first();
//UPDATING lifecycle stage - call an update() method
rs.updateString(4, "Francis");
//MANIPULATING lifecycle stage - finish update
rs.updateRow();
//Note that the database is not updated yet.
}
rs.acceptChanges();
rs.close();
Creating a CachedRowSet
Rowsets are created from a factory interface. To create a rowset with WebLogic Server, follow
these main steps:
1. Create a RowSetFactory instance, which serves as a factory to create rowset objects for use
in your application. You can specify database connection properties in the RowSetFactory so
that you can create RowSets with the same database connectivity using fewer lines of code.
RowSetFactory rsfact = RowSetFactory.newInstance();
CachedRowSet rs = rsfact.newCachedRowSet();
z Manually get a database connection—In your application, you can get a database
connection before the rowset needs it, and then pass the connection object as a parameter
in the execute() and acceptChanges() methods. You must also close the connection as
necessary.
//Lookup DataSource and get a connection
ctx = new InitialContext(ht);
javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup ("myDS");
conn = ds.getConnection();
//Pass the connection to the rowset
rs.execute(conn);
For more information about JDBC data sources, see “Getting a Database Connection from
a DataSource Object” on page 2-1.
z Load the JDBC driver for a direct connection—When you load the JDBC driver and set the
appropriate properties, the rowset creates a database connection when you call execute()
and acceptChanges(). The rowset closes the connection immediately after it uses it. The
rowset does not keep the connection between the execute() and acceptChanges()
method calls.
Class.forName("com.pointbase.jdbc.jdbcUniversalDriver");
rs.setUrl("jdbc:pointbase:server://localhost/demo");
rs.setUsername("examples");
rs.setPassword("examples");
rs.execute();
Populating a CachedRowSet
Populating a rowset is the act of filling the rowset with rows of data. The source of the data is
most commonly a relational database. To populate a rowset with data from a database, you can
use either of the following methods:
z Set an SQL command with the setCommand() method, then execute the command with
the execute() method:
rs.setCommand("SELECT ID, FIRST_NAME, MIDDLE_NAME, LAST_NAME,
PHONE, EMAIL FROM PHYSICIAN");
rs.execute();
acceptChanges(). For details, see “Synchronizing RowSet Changes with the Database” on
page 6-13 later in this section.
When working with a rowset, WebLogic Server internally sets the lifecycle stage of the rowset
after each operation on the rowset, and then limits further operations you can perform on the
rowset based on its current lifecycle stage. After you begin modifying a row with update methods,
you must complete the operation with updateRow() or insertRow() before you can work with
data in any other rows, including moving the cursor to another row. See “Programming with
RowSets” on page 6-3 for a complete discussion of rowset lifecycle stages and operations
allowed for each stage.
To update a row, you move the cursor to the row you want to update, call updateXXX methods
on individual columns within the row, then call updateRow() to complete the operation. For
example:
rs.first();
rs.updateString(4, "Francis");
rs.updateRow();
Note: If you are updating same-named columns from more than one table, you must use the
column index number to refer to the column in the update statement.
Note that you must explicitly move the cursor after inserting a row. There is no implicit
movement of the cursor.
When you call acceptChanges(), the rowset connects to the database using the database
connection information already used by the rowset (see “Database Connection Options” on
page 6-9) or using a connection object passed with the acceptChanges(connection) method.
You can call acceptChanges() after making changes to one row or several rows. Calling
acceptChanges() after making all changes to the rowset is more efficient because the rowset
connects to the database only once.
When using rowsets with WebLogic Server, WebLogic Server internally uses a
weblogic.jdbc.rowset.WLSyncProvider object to read from and write to the database. The
WLSyncProvider uses an optimistic concurrency algorithm for making changes to the database,
which means that the design assumes data in the database will not be changed by another process
during the time between when a rowset is populated to when rowset data changes are propagated
to the database. Before writing changes to the database, the WLSyncProvider compares the data
in the database against the original values in the rowset (values read into the rowset when the
rowset was created or at the last synchronization). If any values in the database have changed,
WebLogic Server throws a javax.sql.rowset.spi.SyncProviderException and does not
write any changes to the database. You can catch the exception in your application and determine
how to proceed. For more information, see “Handling SyncProviderExceptions with a
SyncResolver” on page 6-28.
The WLCachedRowSet interface, an extension to the standard CachedRowSet interface, provides
options for selecting an optimistic concurrency policy. See “Optimistic Concurrency Policies” on
page 6-37 for more information.
After propagating changes to the database, WebLogic Server changes the lifecycle stage of the
rowset to Designing or Populating, depending on your application environment. In the Designing
stage, you must repopulate the rowset before you can use it again; in the Populating stage, you
can use the rowset with its current data. See “Reusing a WebLogic RowSet After Completing a
Transaction” on page 6-17 for more details.
If you do not plan to use the rowset again, you should close it with the close() method. For
example:
rs.close();
See the documentation for the javax.sql.rowset.CachedRowSet interface for more details.
z “Setting Table and Primary Key Information Using the MetaData Interface” on page 6-15
executeAndGuessTableName and
executeAndGuessTableNameAndPrimaryKeys
When populating a rowset with an SQL query, you typically use the execute() method to run
the query and read the data. The WLCachedRowSet implementation provides the
executeAndGuessTableName and executeAndGuessTableNameAndPrimaryKeys methods
that extend the execute method to also determine the associated table metadata.
The executeAndGuessTableName method parses the associated SQL and sets the table name
for all columns as the first word following the SQL keyword FROM.
The executeAndGuessTableNameAndPrimaryKeys method parses the SQL command to read
the table name. It then uses the java.sql.DatabaseMetaData to determine the table's primary
keys.
Note: These methods rely on support in the DBMS or JDBC driver. They do not work with all
DBMSs or all JDBC drivers.
or
metaData.setTableName("e_id", "employees");
metaData.setTableName("e_name", "employees");
You can also use the WLRowSetMetaData interface to identify primary key columns.
metaData.setPrimaryKeyColumn("e_id", true);
with a join from multiple tables, but the rowset should only update one table. Any column that is
not from the write table is marked as read-only.
For instance, a rowset might include a join of orders and customers. The write table could be set
to orders. If deleteRow were called, it would delete the order row, but not delete the customer
row.
Note: JSR-114 provides the CachedRowSet.setTableName that provides the same
functionality as the WebLogic CachedRowSetMetaData.setWriteTableName
method. Calling either method marks those columns that do NOT belong to the write
table as read-only. WebLogic also provides the
CachedRowSetMetaData.setTableName method which is used to map which table a
column belongs to. When setting the write table using setTableName, be careful to
implement the method using the appropriate API for your application.
z Call rowset.refresh to update the rowset with data from the database.
Calling connection.commit
In this situation, the connection object is not created by the rowset and initiates a local transaction
by calling connection.commit. If the transaction fails or if the connection calls
connection.rollback, the data is rolled back from the database, but is not rolled back in the
rowset. Before proceeding, you must do one of the following:
z Call rowset.refresh to update the rowset with data from the database.
Calling acceptChanges
In this situation, the rowset creates its own connection object and uses it to update the data in
rowset by calling acceptChanges. In the case of failure or if the rowset calls
connection.rollback, the data is be rolled back from the rowset and also from the database.
WebLogic Server needs to make sure that any transaction in which the rowset participates has
completed before allowing you to make further changes to the data.
If you use a rowset in a local transaction and if autocommit=true is set on the connection object
before rowset data changes are synchronized with the database, you can reuse the rowset with its
current data after synchronizing the data because the autocommit setting forces the local
transaction to complete immediately. WebLogic Server can be sure that the local transaction is
complete before any further changes are made to the rowset.
WebLogic Server cannot automatically be sure that all transactions are complete if you use a
rowset in either of the following scenarios:
z In a global transaction
FilteredRowSets
The following sections describe using standard FilteredRowSets with WebLogic Server:
FilteredRowSet Characteristics
A FilteredRowSet enables you to work with a subset of cached rows and change the subset of
rows while disconnected from the database. A filtered rowset is simply a cached rowset in which
only certain rows are available for viewing, navigating, and manipulating. FilteredRowSets have
the following characteristics:
import javax.sql.rowset.FilteredRowSet;
import weblogic.jdbc.rowset.RowSetFactory;
import weblogic.jdbc.rowset.SQLPredicate;
//S E T F I L T E R
//use SQLPredicate class to create a SQLPredicate object,
//then pass the object in the setFilter method to filter the RowSet.
//C H A N G I N G F I L T E R
//R E M O V I N G F I L T E R
rs.setFilter(null);
while (rs.next ())
{
System.out.println ("ID: " +rs.getInt (1));
System.out.println ("FIRST_NAME: " +rs.getString (2));
System.out.println ("MIDDLE_NAME: " +rs.getString (3));
System.out.println ("LAST_NAME: " +rs.getString (4));
System.out.println ("PHONE: " +rs.getString (5));
System.out.println ("EMAIL: " +rs.getString (6));
System.out.println (" ");
}
rs.close();
}
}
Creating a FilteredRowSet
Rowsets are created from a factory interface. To create a FilteredRowSet with WebLogic Server,
follow these main steps:
1. Create a RowSetFactory instance, which serves as a factory to create rowset objects for use
in your application. For example:
RowSetFactory rsfact = RowSetFactory.newInstance();
Populating a FilteredRowSet
Data population options for a FilteredRowSet are the same as those for a CachedRowSet. See
“Populating a CachedRowSet” on page 6-10.
2. Create an instance of the class (a filter) to specify the filtering criteria that you want to use.
For example, you may want to see only rows with values in the ID column between 100 and
199.
package examples.jdbc.rowsets;
import javax.sql.rowset.Predicate;
import javax.sql.rowset.CachedRowSet;
import javax.sql.RowSet;
import java.sql.SQLException;
this.criteria = criteria;
}
See “SQLPredicate, a SQL-Style RowSet Filter” on page 6-36 for more information.
WebRowSets
A WebRowSet is a cached rowset that can read and write a rowset in XML format. WebRowSets
have the following characteristics:
z The XML code used to populate the rowset or written from the rowset conforms to the
standard WebRowSet XML Schema definition available at
http://java.sun.com/xml/ns/jdbc/webrowset.xsd.
For more information, see the Sun Web site at http://java.sun.com/products/jdbc/download.html
and the Javadoc for the javax.sql.rowset.WebRowSet interface.
Note: WebLogic Server supports two schemas for rowsets: one for the standard WebRowSet
and one for the WLCachedRowSet, which was implemented before JSR-114 was
finalized.
z If you are using only WebLogic Server rowsets, you can use either schema. The
proprietary schema offers the following benefits:
– Has more element types.
– Is used by rowsets in BEA Workshop for WebLogic Platform.
z To interact with other rowset implementations, you must use the standard schema.
JoinRowSets
A JoinRowSet is a number of disconnected RowSet objects joined together in a single rowset by
a SQL JOIN. JoinRowSets have the following characteristics:
z Each rowset added to the JoinRowSet must have a "match" column specified in the
addRowSet method used to add the rowset to the JoinRowSet. For example:
addRowSet(javax.sql.RowSet[] rowset,java.lang.String[] columnName);
z You can set the join type using setJoinType method. The following join types are
supported:
CROSS_JOIN
FULL_JOIN
INNER_JOIN
LEFT_OUTER_JOIN
RIGHT_OUTER_JOIN
z JoinRowSets are for read-only use. JoinRowSets cannot be used to update data in the
database.
z Match columns in a JoinRowSet are limited to four data types: Number, Boolean, Date,
and String. Table 6-1 provides more details about data types allowed for a match column
in a JoinRowSet.
Number Number
String
Boolean Boolean
String
Date Date
String
String String
Number
Boolean
Date
For more information about JoinRowSets, see the Javadoc for the
javax.sql.rowset.Joinable and JoinRowSet interfaces.
JDBCRowSets
A JDBCRowSet is a wrapper around a ResultSet object that enables you to use the result set as a
JavaBeans component. Note that a JDBCRowSet is a connected rowset. All other rowset types
are disconnected rowsets.
For more information, see the Javadoc for the javax.sql.rowset.JdbcRowSet interface.
2. Get the SyncResolver object from the exception. See “Getting a SyncResolver Object” on
page 6-33.
3. Page through conflicts using nextConflict() or any other navigation method. “Navigating
in a SyncResolver Object” on page 6-33.
4. Determine the correct value, then set it with setResolvedValue(), which sets the value in
the rowset. See “Setting the Resolved Value for a RowSet Data Synchronization Conflict” on
page 6-34.
Table 6-2 Conflict Types When Synchronizing RowSet Changes in the Database
RowSet Data Database Data Notes
Change Type Change Type
Update Update Values in the same row in the rowset and database have changed.
The syncresolver status is
SyncResolver.UPDATE_ROW_CONFLICT.
Your application may need to supply logic to resolve the conflict
or may need to present the new data to the user.
Update Delete Values in the row in the rowset have been updated, but the row has
been deleted in the database. The syncresolver status is
SyncResolver.UPDATE_ROW_CONFLICT.
Your application may need to supply logic to decide whether to
leave the row as deleted (as it is in the database) or to restore the
row and persist changes from the rowset.
• To leave the row as deleted, revert the changes to the row in
the rowset.
• To restore the row with changes, insert a new row with the
desired values.
Note that if the row is deleted in the database, there is no conflict
value. When you call getConflictValue(), WebLogic
Server throws a
weblogic.jdbc.rowset.RowNotFoundException.
Table 6-2 Conflict Types When Synchronizing RowSet Changes in the Database
RowSet Data Database Data Notes
Change Type Change Type
Delete Update The row has been deleted in the rowset, but the row has been
updated in the database. The syncresolver status is
SyncResolver.DELETE_ROW_CONFLICT.
Your application may need to supply logic to decide whether to
delete the row (as it is in the rowset) or to keep the row and persist
changes currently in the database.
Note that in this scenario, all values in the row will be conflicted
values. To keep the row with the current values in the database,
call setResolvedValue to set the resolved value for each
column in the row to the current value in the database. To proceed
with the delete, call syncprovider.deleteRow().
Delete Delete The row has been deleted in the rowset and has been deleted in the
database by another process.The syncresolver status is
SyncResolver.DELETE_ROW_CONFLICT.
To resolve the SyncProviderException, you must revert the delete
operation on the row in the rowset.
Note that there will be no conflict value (not null, either) for any
column in the row. When you call getConflictValue(),
WebLogic Server throws a
weblogic.jdbc.rowset.RowNotFoundException.
Insert Insert If a row is inserted in the rowset and a row is inserted in the
database, a primary key conflict may occur, in which case an SQL
exception will be thrown. You cannot directly handle this conflict
type using a SyncResolver because a SyncProviderException is
not thrown.
try {
rs.acceptChanges();
} catch (SyncProviderException spex) {
SyncResolver syncresolver = spex.getSyncResolver();
while (syncresolver.nextConflict()) {
int status = syncresolver.getStatus();
int rownum = syncresolver.getRow();
rs.absolute(rownum);
//check for null in each column
//write out the conflict
//set resolved value to value in the db for this example
//handle exception for deleted row in the database
try {
Object idConflictValue = syncresolver.getConflictValue("ID");
if (idConflictValue != null) {
System.out.println("ID value in db: " + idConflictValue);
System.out.println("ID value in rowset: " + rs.getInt("ID"));
syncresolver.setResolvedValue("ID", idConflictValue);
System.out.println("Set resolved value to " + idConflictValue);
}
else {
System.out.println("ID: NULL - no conflict");
}
} catch (RowNotFoundException e) {
System.out.println("An exception was thrown when requesting a ");
System.out.println("value for ID. This row was ");
System.out.println("deleted in the database.");
}
. . .
try {
rs.acceptChanges();
} catch (Exception ignore2) {
}
z Sets the value to persist in the database. That is, it sets the current value in the rowset.
When changes are synchronized, the new value will be persisted to the database.
z Changes the original value for the rowset data to the current value in the database. The
original value was the value since the last synchronization. After calling
setResolvedValue(), the original value becomes the current value in the database.
z Changes the WHERE clause in the synchronization call so that updates are made to
appropriate rows in the database.
Synchronizing Changes
After resolving conflicting values in the SyncResolver, you must synchronize your changes with
the database. To do that, you call rowset.acceptChanges(). again. The acceptChanges()
call closes the SyncResolver object and releases locks on the database after the synchronization
completes.
WLCachedRowSets
A WLCachedRowSet is an extension of CachedRowSets, FilteredRowSets, WebRowSets, and
SortedRowSets. JoinRowSets have the following characteristics:
z WLCachedRowSets include convenience methods that help make using rowsets easier and
also include methods for setting optimistic concurrency options and data synchronization
options.
For more information, see the Javadoc for the weblogic.jdbc.rowset.WLCachedRowSet
interface.
SharedRowSets
Rowsets can be used by a single thread. They cannot be shared by multiple threads. A
SharedRowSet extends CachedRowSets so that additional CachedRowSets can be created for use
in other threads based on the data in an original CachedRowSet. SharedRowSets have the
following characteristics:
z Each SharedRowSet is a shallow copy of the original rowset (with references to data in the
original rowset instead of a copy of the data) with its own context (cursor, filter, sorter,
pending changes, and sync provider).
z When data changes from any of the SharedRowSets are synchronized with the database,
the base CachedRowSet is updated as well.
SortedRowSets
A SortedRowSet extends CachedRowSets so that rows in a CachedRowSet can be sorted based
on the Comparator object provided by the application. SortedRowSets have the following
characteristics:
z Sorting is set in a way similar to way filtering is set for a FilteredRowSet, except that
sorting is based on a java.util.Comparator object instead of a
javax.sql.rowset.Predicate object:
a. The application creates a Comparator object with the desired sorting behavior.
z Sorting is done in memory rather than depending on the database management system for
sort processing. Using SortedRowSets can increase application performance by reducing
the number of database round-trips.
z weblogic.jdbc.rowset.SortedRowSet interface
z weblogic.jdbc.rowset.SQLComparator class
SQLPredicate Grammar
The SQLPredicate class borrows its grammar from the JMS selector grammar, which is very
similar to the grammar for an SQL select WHERE clause.
Some important notes:
z When referencing a column, you must use the column name; you cannot use column index
number.
z The grammar supports the use of operators and mathematical operations, for example:
(colA + ColB) >=100.
z In constructing the WHERE clause, you can use simple datatypes only, including:
– String
– Int
– Boolean
– Float
Code Example
//S E T F I L T E R
//use SQLPredicate class to create a SQLPredicate object,
//then pass the object in the setFilter method to filter the RowSet.
For more information, see the Javadoc for the weblogic.jdbc.rowset.SQLPredicate class.
The UPDATE and DELETE statements issued by the rowset include WHERE clauses that are
used to verify the data in the database against what was read when the rowset was populated. If
the rowset detects that the underlying data in the database has changed, it issues an
OptimisticConflictException. The application can catch this exception and determine how
to proceed. Typically, applications will refresh the updated data and present it to the user again.
The WLCachedRowSet implementation offers several optimistic concurrency policies that
determine what SQL the rowset issues to verify the underlying database data:
z VERIFY_READ_COLUMNS
z VERIFY_MODIFIED_COLUMNS
z VERIFY_SELECTED_COLUMNS
z VERIFY_NONE
z VERIFY_AUTO_VERSION_COLUMNS
z VERIFY_VERSION_COLUMNS
To illustrate the differences between these policies, we will use an example that uses the
following:
VERIFY_READ_COLUMNS
The default rowset optimistic concurrency control policy is VERIFY_READ_COLUMNS.
When the rowset issues an UPDATE or DELETE, it includes all columns that were read from the
database in the WHERE clause. This verifies that the value in all columns that were initially read
into the rowset have not changed.
VERIFY_MODIFIED_COLUMNS
The VERIFY_MODIFIED_COLUMNS policy only includes the primary key columns and the
updated columns in the WHERE clause. It is useful if your application only cares if its updated
columns are consistent. It does allow your update to commit if columns that have not been
updated have changed since the data has been read.
In our example update, the rowset issues:
UPDATE employees SET e_salary = 20000
WHERE e_id = 1 AND e_salary=10000
The e_id column is included since it is a primary key column. The e_salary column is a
modified column so it is included as well. The e_name column was only read so it is not verified.
VERIFY_SELECTED_COLUMNS
The VERIFY_SELECTED_COLUMNS includes the primary key columns and columns you
specify in the WHERE clause.
WLRowSetMetaData metaData = (WLRowSetMetaData) rowSet.getMetaData();
metaData.setOptimisticPolicy(WLRowSetMetaData.VERIFY_SELECTED_COLUMNS);
// Only verify the e_salary column
metaData.setVerifySelectedColumn("e_salary", true);
metaData.acceptChanges();
The e_id column is included since it is a primary key column. The e_salary column is a
selected column so it is included as well.
VERIFY_NONE
The VERIFY_NONE policy only includes the primary key columns in the WHERE clause. It
does not provide any additional verification on the database data.
VERIFY_AUTO_VERSION_COLUMNS
The VERIFY_AUTO_VERSION_COLUMNS includes the primary key columns as well as a
separate version column that you specify in the WHERE clause. The rowset will also
automatically increment the version column as part of the update. This version column must be
an integer type. The database schema must be updated to include a separate version column
(e_version). Assume for our example this column currently has a value of 1.
metaData.setOptimisticPolicy(WLRowSetMetaData.
VERIFY_AUTO_VERSION_COLUMNS);
metaData.setAutoVersionColumn("e_version", true);
metaData.acceptChanges();
The e_version column is automatically incremented in the SET clause. The WHERE clause
verified the primary key column and the version column.
VERIFY_VERSION_COLUMNS
The VERIFY_VERSION_COLUMNS has the rowset check the primary key columns as well as
a separate version column. The rowset does not increment the version column as part of the
update. The database schema must be updated to include a separate version column (e_version).
Assume for our example this column currently has a value of 1.
metaData.setOptimisticPolicy(WLRowSetMetaData.VERIFY_VERSION_COLUMNS);
metaData.setVersionColumn("e_version", true);
metaData.acceptChanges();
The WHERE clause verifies the primary key column and the version column. The rowset does
not increment the version column so this must be handled by the database. Some databases
provide automatic version columns that increment when the row is updated. It is also possible to
use a database trigger to handle this type of update.
Performance Options
Consider the following performance options when using RowSets.
JDBC Batching
The rowset implementation includes support for JDBC batch operations. Instead of sending each
SQL statement individually to the JDBC driver, a batch sends a collection of statements in one
bulk operation to the JDBC driver. Batching is disabled by default, but it generally improves
performance when large numbers of updates occur in a single transaction. It is worthwhile to
benchmark with this option enabled and disabled for your application and database.
The WLCachedRowSet interface contains the methods setBatchInserts(boolean),
setBatchDeletes(boolean), and setBatchUpdates(boolean) to control batching of
INSERT, DELETE, and UPDATE statements.
Note: The setBatchInserts, setBatchDeletes, or setBatchUpdates methods must be
called before the acceptChanges method is called.
Group Deletes
When multiple rows are deleted, the rowset would normally issue a DELETE statement for each
deleted row. When group deletes are enabled, the rowset issues a single DELETE statement with
a WHERE clause that includes the deleted rows.
For instance, if we were deleting 3 employees from our table, the rowset would normally issue:
DELETE FROM employees WHERE e_id = 3 AND e_version = 1;
DELETE FROM employees WHERE e_id = 4 AND e_version = 3;
DELETE FROM employees WHERE e_id = 5 AND e_version = 10;
Troubleshooting JDBC
The following sections describe some common issues when developing JDBC applications:
You can determine what type of threads you are using by checking the environment variable
called THREADS_TYPE. If this variable is not set, you can check the shell script in your Java
installation bin directory.
Some of the problems are related to the implementation of threads in the JVM for each operating
system. Not all JVMs handle operating-system specific threading issues equally well. Here are
some hints to avoid thread-related problems:
z If you are using HP UNIX, upgrade to version 11.x, because there are compatibility issues
with the JVM in earlier versions, such as HP UX 10.20.
z On HP UNIX, the new JDK does not append the green-threads library to the SHLIB_PATH.
The current JDK can not find the shared library (.sl) unless the library is in the path
defined by SHLIB_PATH. To check the current value of SHLIB_PATH, at the command line
type:
$ echo $SHLIB_PATH
Use the set or setenv command (depending on your shell) to append the WebLogic
shared library to the path defined by the symbol SHLIB_PATH. For the shared library to be
recognized in a location that is not part of your SHLIB_PATH, you will need to contact your
system administrator.
try {
Driver d =
(Driver)Class.forName("weblogic.jdbc.oci.Driver").newInstance();
catch (Exception e) {
finally {
try {rs.close();}
catch (Exception rse) {}
try {stmt.close();}
catch (Exception sse) {}
try {conn.close();
catch (Exception cse) {}
The first line in this example creates a result set that is lost and can be garbage collected
immediately.
For information on supported data bases and data base drivers, see Supported Configurations.
For example:
CREATE TABLE EmployeeEJBTable (name varchar(50) not null,salary int, card
varchar(50), primary key (name))
2. Verify the current value the SQL server by running the following script:
exec sp_configure 'nested triggers'
For example:
CREATE TABLE EmployeeEJBTable (name varchar(50) not null,salary int, card
varchar(50), primary key (name))
The EJB code assumes that the record is not found and throws an appropriate error message.