JDBC
JDBC
Copyright
Copyright 1995 - 2006 BEA Systems, Inc. All Rights Reserved.
Contents
iii
2. Use Built-in DBMS Set-based Processing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-3 3. Make Your Queries Smart . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-3 4. Make Transactions Single-batch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-5 5. Never Have a DBMS Transaction Span User Input. . . . . . . . . . . . . . . . . . . . . . . . 3-5 6. Use In-place Updates . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-6 7. Keep Operational Data Sets Small. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-6 8. Use Pipelining and Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3-6
iv
Opening a Connection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-4 Closing a Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-5 Limitations for Using a Physical Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-7 Using Vendor Extensions to JDBC Interfaces. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-8 Sample Code for Accessing Vendor Extensions to JDBC Interfaces. . . . . . . . . . . . . 5-9 Import Packages to Access Vendor Extensions. . . . . . . . . . . . . . . . . . . . . . . . . . 5-9 Get a Connection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-9 Cast the Connection as a Vendor Connection . . . . . . . . . . . . . . . . . . . . . . . . . . 5-10 Use Vendor Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-10 Using Oracle Extensions with the Oracle Thin Driver . . . . . . . . . . . . . . . . . . . . . . . . . . 5-11 Limitations When Using Oracle JDBC Extensions . . . . . . . . . . . . . . . . . . . . . . . . . 5-12 Sample Code for Accessing Oracle Extensions to JDBC Interfaces . . . . . . . . . . . . 5-12 Programming with ARRAYs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-13 Import Packages to Access Oracle Extensions . . . . . . . . . . . . . . . . . . . . . . . . . 5-13 Establish the Connection. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-13 Getting an ARRAY . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-14 Updating ARRAYs in the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-14 Using Oracle Array Extension Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15 Programming with STRUCTs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-15 Getting a STRUCT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-16 Using OracleStruct Extension Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-17 Getting STRUCT Attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-17 Using STRUCTs to Update Objects in the Database . . . . . . . . . . . . . . . . . . . . 5-18 Creating Objects in the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-19 Automatic Buffering for STRUCT Attributes. . . . . . . . . . . . . . . . . . . . . . . . . . 5-19 Programming with REFs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-20 Getting a REF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-20 Using OracleRef Extension Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-21
Programming WebLogic JDBC v
Getting a Value . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-21 Updating REF Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-22 Creating a REF in the Database . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-24 Programming with BLOBs and CLOBs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-24 Query to Select BLOB Locator from the DBMS . . . . . . . . . . . . . . . . . . . . . . . 5-25 Declare the WebLogic Server java.sql Objects. . . . . . . . . . . . . . . . . . . . . . . . . 5-25 Begin SQL Exception Block. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-25 Updating a CLOB Value Using a Prepared Statement . . . . . . . . . . . . . . . . . . . 5-26 Programming with Oracle Virtual Private Databases . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-26 Oracle VPD with WebLogic Server. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5-27 Support for Vendor Extensions Between Versions of WebLogic Server Clients and Servers 5-28 Tables of Oracle Extension Interfaces and Supported Methods . . . . . . . . . . . . . . . . . . . 5-28
vi
Working with Data in a CachedRowSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-11 Synchronizing RowSet Changes with the Database. . . . . . . . . . . . . . . . . . . . . . . . . 6-13 RowSet MetaData Settings for Database Updates. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-14 WebLogic RowSet Extensions for Working with MetaData . . . . . . . . . . . . . . . . . . . . . 6-14 executeAndGuessTableName and executeAndGuessTableNameAndPrimaryKeys 6-14 Setting Table and Primary Key Information Using the MetaData Interface . . . . . . 6-15 Setting the Write Table . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-15 RowSets and Transactions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-16 Integrating with JTA Global Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-16 Behavior of Rowsets Using Global Transactions . . . . . . . . . . . . . . . . . . . . . . . 6-16 Using Local Transactions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-16 Behavior of Rowsets Using Local Transactions . . . . . . . . . . . . . . . . . . . . . . . . 6-17 Reusing a WebLogic RowSet After Completing a Transaction . . . . . . . . . . . . . . . . 6-17 FilteredRowSets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-18 FilteredRowSet Characteristics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-18 Special Programming Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-19 FilteredRowSet Code Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-20 Importing Classes and Interfaces for FilteredRowSets . . . . . . . . . . . . . . . . . . . . . . 6-22 Creating a FilteredRowSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-22 Setting FilteredRowSet Properties . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Database Connection Options for a FilteredRowSet . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Populating a FilteredRowSet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Setting FilteredRowSet MetaData . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Setting the Filter for a FilteredRowSet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-23 Working with Data in a FilteredRowSet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-26 WebRowSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-26 Special Programming Considerations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-26 JoinRowSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-27
Programming WebLogic JDBC vii
JDBCRowSets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-28 Handling SyncProviderExceptions with a SyncResolver . . . . . . . . . . . . . . . . . . . . . . . . 6-28 RowSet Data Synchronization Conflict Types. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-30 SyncResolver Code Example. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-31 Getting a SyncResolver Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-32 Navigating in a SyncResolver Object . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-33 Setting the Resolved Value for a RowSet Data Synchronization Conflict . . . . . . . 6-34 Synchronizing Changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-34 WLCachedRowSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-34 SharedRowSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-35 SortedRowSets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-35 SQLPredicate, a SQL-Style RowSet Filter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-36 What is SQLPredicate?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-36 SQLPredicate Grammar. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-36 Code Example . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-37 Optimistic Concurrency Policies. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-37 VERIFY_READ_COLUMNS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-38 VERIFY_MODIFIED_COLUMNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-38 VERIFY_SELECTED_COLUMNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-39 VERIFY_NONE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-39 VERIFY_AUTO_VERSION_COLUMNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-39 VERIFY_VERSION_COLUMNS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-40 Optimistic Concurrency Control Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-40 Choosing an Optimistic Policy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-41 Performance Options. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-41 JDBC Batching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-41 Oracle Batching Limitations. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-42 Group Deletes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6-42
viii Programming WebLogic JDBC
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
ix
CHAPTER
This section describes the contents and organization of this guideProgramming WebLogic JDBC. Document Scope and Audience on page 1-1 Guide to this Document on page 1-2 Related Documentation on page 1-2 JDBC Samples and Tutorials on page 1-2 New and Changed JDBC Features in This Release on page 1-3
1-1
I n tro d uc ti on a n d R o a d ma p
Related Documentation
This document contains JDBC-specific programming information. For comprehensive guidelines for developing, deploying, and monitoring WebLogic Server applications, see the following documents: Configuring and Managing WebLogic JDBC is a guide to JDBC configuration and management for WebLogic Server. Developing WebLogic Server Applications is a guide to developing WebLogic Server applications. Deploying WebLogic Server Applications is the primary source of information about deploying WebLogic Server applications in development and production environments.
N e w a n d C ha n g ed JD B C F e at u res i n T h i s Re l e a se
1-3
I n tro d uc ti on a n d R o a d ma p
1-4
CHAPTER
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. Getting a Database Connection from a DataSource Object on page 2-1 Pooled Connection Limitation on page 2-4 Getting a Connection from an Application-Scoped Data Source on page 2-5 For more information about configuring JDBC data sources and multi data sources, see Configuring and Managing WebLogic JDBC.
2-1
U si n g W e b Lo g i c J D B C i n a n Ap p l i c a t i o n
2-2
G e tti ng a D a ta b as e C on n e cti on fr om a D a ta So u rc e O b j e c t
//Close JDBC objects as soon as possible stmt.close(); stmt=null; 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/docs90/jndi/index.html.
U si n g W e b Lo g i c J D B C i n a n Ap p l i c a t i o n
connection fails because the connection test on the reserved connection failed. This typically happens when the database server is unavailable.
ConnectionUnavailableSQLExceptiongenerated when an application request to get a connection fails because there are currently no connections available in the pool to be allocated. This is a transient failure, and is generated if all connections in the pool are currently in use. It can also be thrown when connections are unavailable because they are being tested. PoolDisabledSQLExceptiongenerated when an application request to get a connection fails because the JDBC Data Source has been administratively disabled. PoolLimitSQLExceptiongenerated when an application request to get a connection fails due to a configured threshold of the data source, such as HighestNumWaiters, ConnectionReserveTimeoutSeconds, and so forth. PoolPermissionsSQLExceptiongenerated when an application request to get a connection fails a (security) authentication or authorization check.
2-4
G e ttin g a C o nn e ct ion f ro m a n A pp l i c a ti o n -S co p ed D a ta S o ur ce
2-5
U si n g W e b Lo g i c J D B C i n a n Ap p l i c a t i o n
2-6
CHAPTER
The following sections explain how to get the best performance from JDBC applications: WebLogic Performance-Enhancing Features on page 3-1 Designing Your Application for Best Performance on page 3-2
3-1
P er f or ma n ce T u nin g Y ou r J D B C A pp l i c a t i o n
3-2
De s i g ni ng Y o ur Ap p l i c a ti o n fo r B e st Pe rf or ma n ce
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.
3-3
P er f or ma n ce T u nin g Y ou r J D B C A pp l i c a t i o n
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
Salary
10 20 30 40 50 60 70 80 80
A correlated subquery
select p.name, p.salary from payroll p where 3 >= (select count(*) from payroll pp where pp.salary >= p.salary);
returns a much smaller result, shown in Table 3-2. Table 3-2 Results from Subquery Name
Sue Hal May
Salary
70 80 80
3-4
De s i g ni ng Y o ur Ap p l i c a ti o n fo r B e st Pe rf or ma n ce
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.
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.
3-5
P er f or ma n ce T u nin g Y ou r J D B C A pp l i c a t i o n
If you require user input to form or complete a transaction, use optimistic locking. Briefly, optimistic locking employs timestamps and triggers in queries and updates. Queries select data with timestamp values and prepare a transaction based on that data, without locking the data in a transaction. When an update transaction is finally defined by the user input, it is sent as a single submission that includes timestamped safeguards to make sure the data is the same as originally fetched. A successful transaction automatically updates the relevant timestamps for changed data. If an interceding update from another client has altered data on which the current transaction is based, the timestamps change, and the current transaction is rejected. Most of the time, no relevant data has been changed so transactions usually succeed. When a transaction fails, the application can refetch the updated data to present to the user to reform the transaction if desired.
CHAPTER
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: Using the WebLogic RMI Driver (Deprecated) on page 4-1 Using the WebLogic JTS Driver (Deprecated) on page 4-8 Using the WebLogic Pool Driver (Deprecated) on page 4-10
4-1
U si n g We b Lo g i c Wra p p er D ri v er s
4-2
Us in g th e We bL o gi c R MI D riv er (D e p rec a te d )
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(); // You can now use the conn object to create // // a Statement object to execute SQL statements and process result sets:
Statement stmt = conn.createStatement(); stmt.execute("select * from someTable"); ResultSet rs = stmt.getResultSet(); // Do not forget to close the statement and connection objects // when you are finished: } 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) {
4-3
U si n g We b Lo g i c Wra p p er D ri v er s
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/docs90/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:
weblogic.userspecifies a username weblogic.credentialspecifies the password for the weblogic.user.
4-4
Us in g th e We bL o gi c R MI D riv er (D e p rec a te d )
Size, respectively. You set data source attributes via the Administration Console. To enable row caching and to set the row prefetch size attribute for a data source, follow these steps: 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. 4. Select the Configuration: General tab and then do the following:. a. Select the Row Prefetch Enabled check box. 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.
4-5
U si n g We b Lo g i c Wra p p er D ri v er s
WebLogic Server only performs row caching if the result set type is both TYPE_FORWARD_ONLY and CONCUR_READ_ONLY. 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 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()
4-6
Us in g th e We bL o gi c R MI D riv er (D e p rec a te d )
getTime()
getTimestamp()
4-7
U si n g We b Lo g i c Wra p p er D ri v er s
This explanation demonstrates creating and using a JTS transaction from a server-side application and uses a data source named myDataSource. 1. Import the following classes:
import import import import import javax.transaction.UserTransaction; java.sql.*; javax.naming.*; java.util.*; weblogic.jndi.*;
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"); // Parameters for the WebLogic Server. // Substitute the correct hostname, port number // user name, and password for your environment: env.put(Context.PROVIDER_URL, "t3://localhost:7001"); env.put(Context.SECURITY_PRINCIPAL, Fred); env.put(Context.SECURITY_CREDENTIALS, secret); ctx = new InitialContext(env); UserTransaction tx = (UserTransaction) ctx.lookup("javax.transaction.UserTransaction");
4-9
U si n g We b Lo g i c Wra p p er D ri v er s
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();
4-10
CHAPTER
The following sections describe how to set up and use third-party JDBC drivers: Getting a Connection with Your Third-Party Driver on page 5-1 Using Vendor Extensions to JDBC Interfaces on page 5-8 Using Oracle Extensions with the Oracle Thin Driver on page 5-11 Programming with Oracle Virtual Private Databases on page 5-26 Support for Vendor Extensions Between Versions of WebLogic Server Clients and Servers on page 5-28 Tables of Oracle Extension Interfaces and Supported Methods on page 5-28
5-1
stmt = conn.createStatement(); stmt.execute("select * from someTable"); rs = stmt.getResultSet(); ... //Close JDBC objects as soon as possible stmt.close(); stmt=null; conn.close(); conn=null; } catch (Exception e) { // a failure occurred
5-2
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/docs90/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.
non-standard JDBC-related classes that require direct access of the physical connection (the actual vendor JDBC connection). To directly access a physical connection in a connection pool, you must cast the connection using getVendorConnection at
http://e-docs.bea.com/wls/docs90/javadocs/weblogic/jdbc/extensions/WLConne ction.html.
The following sections provide information on getting a physical connection: Opening a Connection on page 5-4 Closing a Connection on page 5-5 Getting a Physical Connection from a Data Source on page 5-3
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: Implicitly pass the physical connection (using getVendorConnection) within a method that requires the physical connection. Cast the connection as a WLConnection and call getVendorConnection. Always limit direct access of physical database connections to vendor-specific calls. For all other situations, use the generic JDBC connection provided by WebLogic Server. Sample code to open a connection for vendor-specific calls is provided in Listing 5-1. Listing 5-1 Code Sample to Open a Connection for Vendor-specific Calls
//Import this additional class and any vendor packages //you may need. import weblogic.jdbc.extensions.WLConnection . . . myJdbcMethod() {
5-4
// Connections from a connection pool should always be // method-level variables, never class or instance methods. Connection conn = null; 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"); // Always get a pooled connection in a try block where it is // used completely and is closed if necessary in the finally // block. conn = ds.getConnection(); // You can now cast the conn object to a WLConnection // interface and then get the underlying physical connection. java.sql.Connection vendorConn = ((WLConnection)conn).getVendorConnection(); // do not close vendorConn // You could also cast the vendorConn object to a vendor // interface, such as: // oracle.jdbc.OracleConnection vendorConn = (OracleConnection) // ((WLConnection)conn).getVendorConnection() // If you have a vendor-specific method that requires the // physical connection, it is best not to obtain or retain // the physical connection, but simply pass it implicitly // where needed, eg: //vendor.special.methodNeedingConnection(((WLConnection)conn)).getVendorCo nnection());
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: Close any objects you have obtained from the connection.
Programming WebLogic JDBC 5-5
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. Listing 5-2 Sample Code to Close a Connection for Vendor-specific Calls
// As soon as you are finished with vendor-specific calls, // nullify the reference to the connection. // Do not keep it or close it. // Never use the vendor connection for generic JDBC. // Use the logical (pooled) connection for standard JDBC. vendorConn = null; ... do all the JDBC needed for the whole method... // close the logical (pooled) connection to return it to // the connection pool, and nullify the reference. conn.close(); conn = null; } 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){} } }
5-6
5-7
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.
U s i n g Ve n do r Ex t e ns i o n s t o J D B C I n t e r f a c es
Cast the connection object as the vendors connection interface. Use the vendor extensions as described in the vendors documentation. The following sections provide details in code examples. For information about specific extension methods for a particular JDBC driver, refer to the documentation from the JDBC driver vendor.
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); // get a java.sql.Connection object from the DataSource java.sql.Connection conn = ds.getConnection();
5-9
System.out.println("Default row prefetch is " + default_prefetch); java.sql.Statement stmt = conn.createStatement(); // Cast to OracleStatement and set the row prefetch // value for this statement. Note that this // prefetch value applies to the connection between // WebLogic Server and the database. ((oracle.jdbc.OracleStatement)stmt).setRowPrefetch(20); // This replaces the deprecated process of casting the // statement to a weblogic.jdbc.vendor.oracle.OracleStatement. // For example: // ((weblogic.jdbc.vendor.oracle.OracleStatement)stmt). // setRowPrefetch(20);
5-10
// Perform a normal sql query and process the results... String query = "select empno,ename from emp"; java.sql.ResultSet rs = stmt.executeQuery(query); 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; }
WebLogic Server provides its own interfaces to access the extension methods for those classes:
weblogic.jdbc.vendor.oracle.OracleArray weblogic.jdbc.vendor.oracle.OracleStruct weblogic.jdbc.vendor.oracle.OracleRef weblogic.jdbc.vendor.oracle.OracleThinBlob weblogic.jdbc.vendor.oracle.OracleThinClob
The following sections provide code samples for using the WebLogic Server interfaces for Oracle extensions. For a list of supported methods, see Tables of Oracle Extension Interfaces and
5-11
Supported Methods on page 5-28. For more information, please refer to the Oracle documentation. Note: You can use this process to use any of the WebLogic Server interfaces for Oracle extensions listed in the Tables of Oracle Extension Interfaces and Supported Methods on page 5-28. However, all but the interfaces listed above are deprecated and will be removed in a future release of WebLogic Server.
5-13
// care of those details. javax.sql.DataSource ds = getDataSource(args); // get a java.sql.Connection object from the DataSource java.sql.Connection conn = ds.getConnection();
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 columnan 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]); } }
5-14
1. Create an array in the database using PL/SQL, if the array you want to update does not already exist in the database. 2. Get the ARRAY using a result set or a callable statement. 3. Work with the array in your Java application as either a java.sql.Array or a weblogic.jdbc.vendor.oracle.OracleArray. 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();
You can use STRUCTs in server-side applications only. You cannot use STRUCTs in client applications. To use STRUCTs in WebLogic Server applications: 1. Import the required classes. (See Import Packages to Access Oracle Extensions on page 5-13.) 2. Get a connection. (See Establish the Connection on page 5-13.) 3. Use getObject to get the STRUCT. 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(); rs = stmt.executeQuery("select * from people");
WebLogic Server supports all of the JDBC API methods for STRUCTs:
getAttributes() getAttributes(java.util.Dictionary map) 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.
5-16
//The third column uses an object data type. //Use getObject() to assign the object to an array of values. struct = (java.sql.Struct)(rs.getObject(2)); Object[] attrs = ((java.sql.Struct)struct).getAttributes(); String address = attrs[1];
5-17
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")); Object[] attrs = ((java.sql.Struct)struct).getAttributes(map); String address = attrs[1];
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).getOracleAttributes(); oracle.sql.STRUCT address = (oracle.sql.STRUCT) attrs[1]; Object address_attrs[] = address.getAttributes();
The preceding example includes a nested STRUCT. That is, the second attribute in the datum array returned is another STRUCT.
stmt = conn.createStatement(); ps = conn.prepareStatement ("UPDATE SCHEMA.people SET EMPLNAME = ?, EMPID = ? where EMPID = 101"); ps.setString (1, "Smith"); ps.setObject (2, struct);
5-18
ps.executeUpdate();
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.
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 = stmt.executeQuery("SELECT ref (s) FROM t1 s where s.ob1=5");
rs.next();
5-20
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() methodone 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).getValue ();
5-21
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")); oracle.sql.STRUCT result = (oracle.sql.STRUCT) ((weblogic.jdbc.vendor.oracle.OracleRef)ref).getValue (map);
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.
5-22
2. Create a string for the SQL command to replace the location of an existing REF with the value of the new REF. 3. Create and execute a prepared statement. For example:
try { conn = ds.getConnection(); stmt = conn.createStatement(); //Get the REF. rs = stmt.executeQuery("SELECT ref (s) FROM t1 s where s.ob1=5");
rs.next(); ref = (java.sql.Ref) rs.getRef(1); //cast the REF as a java.sql.Ref } //Create and execute the prepared statement. String sqlUpdate = "update t3 s2 set col = ? where s2.col.ob1=20"; 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(); ref1 = (java.sql.Ref) rs.getRef(1); // Prepare the stored procedure sql = "{call SP1 (?, ?)}";
5-23
cstmt = conn.prepareCall(sql); // Set IN and register OUT params cstmt.setRef(1, ref1); cstmt.registerOutParameter(2, getRefType(), "USER.OB"); // 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.
5-24
Note: When working with BLOBs and CLOBs (referred to as LOBs), you must take transaction boundaries into account; for example, direct all read/writes to a particular LOB within a transaction. For additional information, refer to Oracle documentation about LOB Locators and Transaction Boundaries at the Oracle Web site at http://www.oracle.com.
5-25
sqe.getMessage()); }
Once you cast to the Oracle.ThinBlob interface, you can access the BEA supported methods.
5-26
Pr og ra m mi n g with O ra c l e V i rtu a l P ri v a te D at ab a s es
/* perform application specific work, preferably using conn instead of orConn */ // clean up connection before returning to WLS JDBC data source orConn.clearClientIdentifier(clientId); // As soon as you are finished with vendor-specific calls, // nullify the reference to the physical connection. orConn = null; // close the pooled connection conn.close();
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-3 for usage guidelines.
5-27
Support for Vendor Extensions Between Versions of WebLogic Server Clients and Servers
Because the way WebLogic Server supports vendor JDBC extensions was changed in WebLogic Server 8.1, interoperability between versions of client and servers is affected. When a WebLogic Server 8.1 or later client interacts with a WebLogic Server 7.0 or earlier server, Oracle extensions are not supported. When the client application tries to cast the JDBC objects to the Oracle extension interfaces, it will get a ClassCastException. However, when a WebLogic Server 7.0 or earlier client interacts with a WebLogic Server 8.1 or later server, Oracle extensions are supported. This applies to the following Oracle extension interfaces:
weblogic.jdbc.vendor.oracle.OracleConnection weblogic.jdbc.vendor.oracle.OracleStatement weblogic.jdbc.vendor.oracle.OraclePreparedStatement weblogic.jdbc.vendor.oracle.OracleCallableStatement weblogic.jdbc.vendor.oracle.OracleResultSet weblogic.jdbc.vendor.oracle.OracleThinBlob weblogic.jdbc.vendor.oracle.OracleThinClob weblogic.jdbc.vendor.oracle.OracleArray weblogic.jdbc.vendor.oracle.OracleRef weblogic.jdbc.vendor.oracle.OracleStruct
Note: Standard JDBC interfaces are supported regardless of the client or server version.
5-28
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Table 5-1 Deprecated Interfaces for Oracle JDBC Extensions Deprecated Interface (supported in WebLogic Server 7.0 and earlier)
weblogic.jdbc.vendor.oracle. OracleConnection weblogic.jdbc.vendor.oracle. OracleStatement weblogic.jdbc.vendor.oracle. OracleCallableStatement weblogic.jdbc.vendor.oracle. OraclePreparedStatement weblogic.jdbc.vendor.oracle. OracleResultSet
Instead, use this interface from Oracle (supported in WebLogic Server version 8.1 and later)
oracle.jdbc.OracleConnection
oracle.jdbc.OracleStatement
oracle.jdbc.OracleCallableStatement
oracle.jdbc.OraclePreparedStatement
oracle.jdbc.OracleResultSet
The interfaces listed in Table 5-2 are still valid because Oracle does not provide interfaces to access these extension methods.
Table 5-2 Oracle Interfaces with Continued Support in WebLogic Server Oracle Interface
weblogic.jdbc.vendor.oracle.OracleArray weblogic.jdbc.vendor.oracle.OracleRef weblogic.jdbc.vendor.oracle.OracleStruct weblogic.jdbc.vendor.oracle.OracleThinClob weblogic.jdbc.vendor.oracle.OracleThinBlob
5-29
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. Table 5-3 OracleConnection Interface Extends Method Signature
void clearClientIdentifier(String s) OracleConnection throws java.sql.SQLException; extends java.sql.Connection boolean getAutoClose() (This interface is throws java.sql.SQLException; deprecated. See Table 5-1.) String getDatabaseProductVersion() throws java.sql.SQLException; String getProtocolType() throws java.sql.SQLException; String getURL() throws java.sql.SQLException; String getUserName() throws java.sql.SQLException; boolean getBigEndian() throws java.sql.SQLException; boolean getDefaultAutoRefetch() throws java.sql.SQLException; boolean getIncludeSynonyms() throws java.sql.SQLException; boolean getRemarksReporting() throws java.sql.SQLException; boolean getReportRemarks() throws java.sql.SQLException;
5-30
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
boolean getRestrictGetTables() OracleConnection throws java.sql.SQLException; extends java.sql.Connection boolean getUsingXAFlag() (continued) throws java.sql.SQLException; (This interface is boolean getXAErrorFlag() deprecated. See Table 5-1.) throws java.sql.SQLException; boolean isCompatibleTo816() throws java.sql.SQLException; (Deprecated) byte[] getFDO(boolean b) throws java.sql.SQLException; int getDefaultExecuteBatch() 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 getDbCsId() throws java.sql.SQLException; short getJdbcCsId() throws java.sql.SQLException; short getStructAttrCsId() throws java.sql.SQLException; short getVersionNumber() throws java.sql.SQLException; void archive(int i, int j, String s) throws java.sql.SQLException;
5-31
Method Signature
close_statements() throws java.sql.SQLException; initUserName() throws java.sql.SQLException; logicalClose() throws java.sql.SQLException; needLine() throws java.sql.SQLException; printState() throws java.sql.SQLException;
void registerSQLType(String s, String t) throws java.sql.SQLException; void releaseLine() throws java.sql.SQLException; void removeAllDescriptor() throws java.sql.SQLException; void removeDescriptor(String s) throws java.sql.SQLException; void setAutoClose(boolean on) throws java.sql.SQLException; void setClientIdentifier(String s) throws java.sql.SQLException; void clearClientIdentifier(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;
5-32
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
void setPhysicalStatus(boolean b) OracleConnection throws java.sql.SQLException; extends java.sql.Connection void setRemarksReporting(boolean b) (continued) throws java.sql.SQLException; (This interface is void setRestrictGetTables(boolean b) deprecated. See Table 5-1.) throws java.sql.SQLException; void setStmtCacheSize(int i) throws java.sql.SQLException; void setStmtCacheSize(int i, boolean b) 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; void startup(String s, int i) throws java.sql.SQLException;
5-33
Method Signature
String getOriginalSql() throws java.sql.SQLException; String getRevisedSql()
throws java.sql.SQLException; (Deprecated in Oracle 8.1.7, removed in Oracle 9i.) boolean getAutoRefetch() throws java.sql.SQLException; boolean is_value_null(boolean b, int i) 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;
5-34
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Method Signature
throws java.sql.SQLException; void defineColumnType(int i, int j, String s) throws java.sql.SQLException; void defineColumnType(int i, int j, int k) 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)
5-35
Method Signature
boolean getAutoRefetch() throws java.sql.SQLException; int getFirstUserColumnIndex() throws java.sql.SQLException;
void closeStatementOnClose() throws java.sql.SQLException; 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;
5-36
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Method Signature
void clearParameters() throws java.sql.SQLException; void registerIndexTableOutParameter(int i,
int j, int k, int l) 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;
5-37
Method Signature
int getExecuteBatch() throws java.sql.SQLException; void defineParameterType(int i, int j, int k) throws java.sql.SQLException; void setDisableStmtCaching(boolean b) throws java.sql.SQLException; void setExecuteBatch(int i) throws java.sql.SQLException; void setFixedCHAR(int i, String s) throws java.sql.SQLException; void setInternalBytes(int i, byte[] b, int j) throws java.sql.SQLException;
5-38
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Method Signature
public ArrayDescriptor getDescriptor() throws java.sql.SQLException; public Datum[] getOracleArray() throws SQLException; public Datum[] getOracleArray(long l, int i) throws SQLException; public String getSQLTypeName() throws java.sql.SQLException; public int length() throws java.sql.SQLException; public double[] getDoubleArray() throws java.sql.SQLException; public double[] getDoubleArray(long l, int i) throws java.sql.SQLException; public float[] getFloatArray() throws java.sql.SQLException; public float[] getFloatArray(long l, int i) throws java.sql.SQLException; public int[] getIntArray() throws java.sql.SQLException; public int[] getIntArray(long l, int i) throws java.sql.SQLException; public long[] getLongArray() throws java.sql.SQLException; public long[] getLongArray(long l, int i) throws java.sql.SQLException;
5-39
Method Signature
public short[] getShortArray() throws java.sql.SQLException; public short[] getShortArray(long l, int i)
(continued)
throws java.sql.SQLException; public void setAutoBuffering(boolean flag) throws java.sql.SQLException; public void setAutoIndexing(boolean flag) throws java.sql.SQLException; public boolean getAutoBuffering() throws java.sql.SQLException; public boolean getAutoIndexing() throws java.sql.SQLException; public void setAutoIndexing(boolean flag, int i) throws java.sql.SQLException;
5-40
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Method Signature
public Object[] getAttributes() throws java.sql.SQLException; public Object[] getAttributes(java.util.Dictionary map) throws java.sql.SQLException; public Datum[] getOracleAttributes() throws java.sql.SQLException; public oracle.sql.StructDescriptor getDescriptor() throws java.sql.SQLException; public String getSQLTypeName() throws java.sql.SQLException; public void setAutoBuffering(boolean flag) throws java.sql.SQLException; public boolean getAutoBuffering() throws java.sql.SQLException;
5-41
Method Signature
public String getBaseTypeName() throws SQLException; public oracle.sql.StructDescriptor getDescriptor() throws SQLException; public oracle.sql.STRUCT getSTRUCT() throws SQLException; public Object getValue() throws SQLException; public Object getValue(Map map) throws SQLException; public void setValue(Object obj) throws SQLException;
Method Signature
int getBufferSize()throws java.sql.Exception int getChunkSize()throws java.sql.Exception int putBytes(long, int, byte[])throws java.sql.Exception int getBinaryOutputStream()throws java.sql.Exception
5-42
T ab l e s of O ra cl e E xt en s i o n I nt er fa ce s an d S u pp o rte d Me th o ds
Method Signature
public OutputStream getAsciiOutputStream() throws java.sql.Exception; public Writer getCharacterOutputStream() throws java.sql.Exception; public int getBufferSize() throws java.sql.Exception; public int getChunkSize() throws java.sql.Exception; public char[] getChars(long l, int i) throws java.sql.Exception; public int putChars(long start, char myChars[]) throws java.sql.Exception; public int putString(long l, String s) throws java.sql.Exception;
5-43
5-44
CHAPTER
The following sections describe characteristics and usage of WebLogic RowSets: About RowSets on page 6-2 Types of RowSets on page 6-2 Programming with RowSets on page 6-3 CachedRowSets on page 6-4 RowSet MetaData Settings for Database Updates on page 6-14 WebLogic RowSet Extensions for Working with MetaData on page 6-14 RowSets and Transactions on page 6-16 FilteredRowSets on page 6-18 WebRowSets on page 6-26 JoinRowSets on page 6-27 JDBCRowSets on page 6-28 Handling SyncProviderExceptions with a SyncResolver on page 6-28 WLCachedRowSets on page 6-34 SharedRowSets on page 6-35 SortedRowSets on page 6-35
Programming WebLogic JDBC 6-1
U sin g R o wS et s wi th We bL o gi c S e rv er
SQLPredicate, a SQL-Style RowSet Filter on page 6-36 Optimistic Concurrency Policies on page 6-37 Performance Options on page 6-41
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: CachedRowSets FilteredRowSets WebRowSets JoinRowSets JDBCRowSets
WebLogic RowSet Extensions: WLCachedRowSets SharedRowSets SortedRowSets SQLPredicate, a SQL-Style RowSet Filter
6-2 Programming WebLogic JDBC
P ro g ra mm i ng w ith R ow Se ts
6-3
U sin g R o wS et s wi th We bL o gi c S e rv er
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 on page 6-4 Special Programming Considerations and Limitations for CachedRowSets on page 6-5 Code Example on page 6-5 Importing Classes and Interfaces for a CachedRowSet on page 6-8 Creating a CachedRowSet on page 6-8 Setting CachedRowSet Properties on page 6-8 Database Connection Options on page 6-9 Populating a CachedRowSet on page 6-10 Setting CachedRowSet MetaData on page 6-10 Working with Data in a CachedRowSet on page 6-11 Synchronizing RowSet Changes with the Database on page 6-13 Also see WLCachedRowSets on page 6-34 for information about using WebLogic extensions to the standard CachedRowSet object.
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: Can be used to insert, update, or delete data. Are serializable, so they can be passed to various application components, including thin clients and wireless devices.
6-4 Programming WebLogic JDBC
C a c he d R ow Se ts
Include transaction handling to enable rowset reuse. See Reusing a WebLogic RowSet After Completing a Transaction on page 6-17. Use an optimistic concurrency control for synchronizing data changes in the rowset with the database. Use a SyncResolver object from a SyncProvider exception to resolve conflicts between data changes in the rowset and the database. See Handling SyncProviderExceptions with a SyncResolver on page 6-28.
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.
6-5
U sin g R o wS et s wi th We bL o gi c S e rv er
6-6
C a c he d R ow Se ts
//Working with data //Delete rows in the rowset try { //MANIPULATING lifecycle stage - navigate to a row //(manually moving the cursor) rs.last(); rs.deleteRow(); //Note that the database is not updated yet. } //Update a row in the rowset 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. } //INSERTING lifecycle stage - Insert rows in the rowset try { rs.moveToInsertRow(); rs.updateInt(1, 104); rs.updateString("FIRST_NAME", "Yuri"); rs.updateString("MIDDLE_NAME", "M"); rs.updateString("LAST_NAME", "Zhivago"); rs.updateString("PHONE", "1234567812"); rs.updateString("EMAIL", "[email protected]"); rs.insertRow(); //"Finish Update" action; //MANIPULATING lifecycle stage - navigate to a row rs.moveToCurrentRow(); //Note that the database is not updated yet. } //Send all changes (delete, update, and insert) to the database. //DESIGNING or POPULATING lifecycle stage - after synchronizing changes
6-7
U sin g R o wS et s wi th We bL o gi c S e rv er
//with the database, lifecycle stage depends on other environment settings. //See Reusing a WebLogic RowSet After Completing a Transaction on page 6-17. try { 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();
2. Create a WLCachedRowSet and cast it as a javax.sql.rowset.CachedRowSet object. By default, the WebLogic newCachedRowSet() RowSetFactory method creates a WLCachedRowSet object. You can use it as-is, but if you prefer to use the standard CachedRowSet object, you can cast the object as such.
CachedRowSet rs = rsfact.newCachedRowSet();
C a c he d R ow Se ts
information about available properties, see the Javadoc for the javax.sql.rowset.BaseRowSet class.
Manually get a database connectionIn 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. Load the JDBC driver for a direct connectionWhen 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();
Set connectivity properties in the RowSetFactoryWhen you set database connection properties in the RowSetFactory, all rowsets created from the RowSetFactory inherit the
6-9
U sin g R o wS et s wi th We bL o gi c S e rv er
connectivity properties. The preferred method is to lookup a datasource and then set the datasource property in the RowSetFactory with the setDataSource() method.
//Lookup DataSource and get a connection ctx = new InitialContext(ht); javax.sql.DataSource ds = (javax.sql.DataSource) ctx.lookup ("myDS"); //Set the datasource property on the RowSetFactory rsfact.setDataSource(ds);
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: 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();
Note: If using a result set that is ResultSet.TYPE_FORWARD_ONLY , a SQLException will be thrown if you attempt to populate a row set with the following conditions: If you call CachedRowset.populate(ResultSet rs) when the result set cursor is at a position beyond row 1. If you call CachedRowset.populate(ResultSet rs, int newPosition) when newPosition is less than the current result set cursor position.
6-10
C a c he d R ow Se ts
U sin g R o wS et s wi th We bL o gi c S e rv er
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.
6-12
C a c he d R ow Se ts
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();
6-13
U sin g R o wS et s wi th We bL o gi c S e rv er
Without the table name, you can use the rowset for read-only operations only. The rowset cannot issue updates unless the table name is specified programmatically. You may also need to set the primary key columns with the setKeyColumns() method. For example:
rs.setTableName(PHYSICIAN); rs.setKeyColumns(new int[] { 1 });
See the documentation for the javax.sql.rowset.CachedRowSet interface for more details.
6-14
We bL o gi c Ro wS et E xte n si on s fo r Wo rk in g with Me ta Da ta
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.
Setting Table and Primary Key Information Using the MetaData Interface
You can also choose to manually set the table and primary key information using the WLRowSetMetaData interface.
WLRowSetMetaData metaData = (WLRowSetMetaData) rowSet.getMetaData(); // Sets one table name for all columns metaData.setTableName("employees");
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);
U sin g R o wS et s wi th We bL o gi c S e rv er
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.
6-16
R o wS et s an d T ra ns a ct io ns
should not be used when trying to integrate with a JTA transaction that was started by the EJB or JMS containers. If an Optimistic conflict or other exception occurs during acceptChanges, the RowSet rolls back the local transaction. In this case, none of the SQL issued in acceptChanges will commit to 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: Call rowset.refresh to update the rowset with data from the database. Create a new rowset with current data.
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.
6-17
U sin g R o wS et s wi th We bL o gi c S e rv er
WebLogic Server cannot automatically be sure that all transactions are complete if you use a rowset in either of the following scenarios: In a global transaction In a local transaction using a connection object with autocommit=false to synchronize data changes with the database With either of these conditions, before you can reuse a rowset with its current data, after calling
acceptChanges() to synchronize your changes with the database, you must call javax.sql.rowset.CachedRowSet.commit() instead of tx.commit() or java.sql.Connection.commit() to commit the transaction. The CachedRowSet.commit()
method wraps the Connection.commit() method and enables WebLogic Server to ensure that the transaction is complete before allowing changes to the rowset.
FilteredRowSets
The following sections describe using standard FilteredRowSets with WebLogic Server: FilteredRowSet Characteristics on page 6-18 Special Programming Considerations on page 6-19 FilteredRowSet Code Example on page 6-20 Importing Classes and Interfaces for FilteredRowSets on page 6-22 Creating a FilteredRowSet on page 6-22 Setting FilteredRowSet Properties on page 6-23 Database Connection Options for a FilteredRowSet on page 6-23 Populating a FilteredRowSet on page 6-23 Setting FilteredRowSet MetaData on page 6-23 Setting the Filter for a FilteredRowSet on page 6-23 Working with Data in a FilteredRowSet on page 6-26
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
6-18 Programming WebLogic JDBC
F ilte re d R ow Se ts
only certain rows are available for viewing, navigating, and manipulating. FilteredRowSets have the following characteristics: The rows available are determined by a javax.sql.rowset.Predicate object supplied by the application and set with the setFilter() method. The Predicate object must implement the javax.sql.rowset.Predicate interface. The Predicate interface includes the public boolean evaluate(RowSet rs) method, which evaluates each row in the rowset If the method returns true, the row is available and visible. If the method returns false, the row is not available or visible. See Setting the Filter for a FilteredRowSet on page 6-23 for more information. WebLogic Server provides the weblogic.jdbc.rowset.SQLPredicate class, which is an implementation of the javax.sql.rowset.Predicate interface that you can use to define a filter for a FilteredRowSet using SQL-like WHERE clause syntax. See SQLPredicate, a SQL-Style RowSet Filter on page 6-36.
6-19
U sin g R o wS et s wi th We bL o gi c S e rv er
allowing you to change the rowset filter. Note that acceptChanges() includes a round-trip to the database, whereas restoreOriginal() does not.
6-20
F ilte re d R ow Se ts
System.out.println ("PHONE: " +rs.getString (5)); System.out.println ("EMAIL: " +rs.getString (6)); } } //Need to accept changes or call restoreOriginal to put the rowset //into the DESIGNING or POPULATING stage. //After navigating, the rowset is in MANIPULATING stage, //and you cannot change properties in that lifecycle stage. rs.restoreOriginal(); //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. SQLPredicate filter = new SQLPredicate("ID >= 103"); rs.setFilter(filter); System.out.println("Filtered data: "); 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 (" "); } //Need to accept changes or call restoreOriginal to put the rowset //into the DESIGNING or POPULATING lifecycle stage. //After navigating, the rowset is in MANIPULATING stage, //and you cannot change properties in that lifecycle stage. rs.restoreOriginal(); //C H A N G I N G F I L T E R
SQLPredicate filter2 = new SQLPredicate("ID <= 103"); rs.setFilter(filter2); System.out.println("Filtered data: "); 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));
6-21
U sin g R o wS et s wi th We bL o gi c S e rv er
System.out.println (" "); } //Need to accept changes or call restoreOriginal to put the rowset //into the DESIGNING or POPULATING lifecycle stage. //After navigating, the rowset is in MANIPULATING stage, //and you cannot change properties in that lifecycle stage. rs.restoreOriginal(); //R E M O V I N G F I L T E R
rs.setFilter(null); while (rs.next ()) { System.out.println System.out.println System.out.println System.out.println System.out.println System.out.println System.out.println } rs.close(); } }
("ID: " +rs.getInt (1)); ("FIRST_NAME: " +rs.getString (2)); ("MIDDLE_NAME: " +rs.getString (3)); ("LAST_NAME: " +rs.getString (4)); ("PHONE: " +rs.getString (5)); ("EMAIL: " +rs.getString (6)); (" ");
The preceding code example also uses the weblogic.jdbc.rowset.SQLPredicate class to create a filter. In your application, you can use the weblogic.jdbc.rowset.SQLPredicate class or you can create your own filter class. See Setting the Filter for a FilteredRowSet on page 6-23 for more information.
Creating a FilteredRowSet
Rowsets are created from a factory interface. To create a FilteredRowSet with WebLogic Server, follow these main steps:
6-22
F ilte re d R ow Se ts
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();
2. Create a WLCachedRowSet and cast it as a javax.sql.rowset.FilteredRowSet object. By default, the WebLogic newCachedRowSet() RowSetFactory method creates a WLCachedRowSet object. You can use it as-is, but if you prefer to use the standard FilteredRowSet object, you can cast the object as such. For example:
FilteredRowSet rs = rsfact.newCachedRowSet();
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.
6-23
U sin g R o wS et s wi th We bL o gi c S e rv er
public class SearchPredicate implements Predicate, java.io.Serializable { private boolean DEBUG = false; private String col = null; private String criteria = null; //Constructor to create case-insensitive column - value comparison. public SearchPredicate(String col, String criteria) { this.col = col; this.criteria = criteria; } public boolean evaluate(RowSet rs) { CachedRowSet crs = (CachedRowSet)rs; boolean bool = false;
6-24
F ilte re d R ow Se ts
try { debug("evaluate(): "+crs.getString(col).toUpperCase()+" contains "+ criteria.toUpperCase()+" = "+ crs.getString(col).toUpperCase().contains(criteria.toUpperCase())); if (crs.getString(col).toUpperCase().contains(criteria.toUpperCase())) bool = true; } catch(Throwable t) { t.printStackTrace(); throw new RuntimeException(t.getMessage()); } return bool; } public boolean evaluate(Object o, String s) throws SQLException { throw new SQLException("String evaluation is not supported."); } public boolean evaluate(Object o, int i) throws SQLException { throw new SQLException("Int evaluation is not supported."); } }
See SQLPredicate, a SQL-Style RowSet Filter on page 6-36 for more information.
6-25
U sin g R o wS et s wi th We bL o gi c S e rv er
WebRowSets
A WebRowSet is a cached rowset that can read and write a rowset in XML format. WebRowSets have the following characteristics: Uses the readXml(java.io.InputStream iStream) method to populate the rowset from an XML source. Uses the writeXml(java.io.OutputStream oStream) method to write data and metadata in XML for use by other application components or to send to a remote client. 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.
Jo in Row Se ts
Has more element types. Is used by rowsets in WebLogic Workshop. 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: 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);
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
Enables you to join data while disconnected from the database. JoinRowSets are for read-only use. JoinRowSets cannot be used to update data in the database. 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. Table 6-1 Data Types Allowed for Match Columns Left Data Type in the Join
Number
Boolean
Boolean String
6-27
U sin g R o wS et s wi th We bL o gi c S e rv er
Table 6-1 Data Types Allowed for Match Columns Left Data Type in the Join
Date
String
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.
6-28
H an d l i n g S yn cP ro v i d er Ex ce p tio n s w ith a S yn cR e s ol v e r
1. Catch the javax.sql.rowset.spi.SyncProviderException. 2. Get the SyncResolver object from the exception. See Getting a SyncResolver Object on page 6-32. 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. 5. Repeat steps 3 and 4 for each conflicted value. 6. Call rowset.acceptChanges() on the rowset (not the SyncResolver) to synchronize changes with the database using the new resolved values. See Synchronizing Changes on page 6-34. For more details about SyncResolvers and the SyncProviderException, see the RowSets specification or the Javadoc for the SyncResolver interface. Note: Before you begin to resolve the SyncProviderException, make sure that no other processes will update the data.
6-29
U sin g R o wS et s wi th We bL o gi c S e rv er
Notes
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. 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().
6-30
H an d l i n g S yn cP ro v i d er Ex ce p tio n s w ith a S yn cR e s ol v e r
Table 6-2 Conflict Types When Synchronizing RowSet Changes in the Database RowSet Data Change Type
Delete
Notes
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.
6-31
U sin g R o wS et s wi th We bL o gi c S e rv er
//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) { } }
6-32
H an d l i n g S yn cP ro v i d er Ex ce p tio n s w ith a S yn cR e s ol v e r
. . . }
A SyncResolver is a rowset that implements the SyncResolver interface. A SyncResolver object contains a row for every row in the original rowset. For values without a conflict, the value in the SyncResolver is null. For values with a conflict, the value is the current value in the database.
6-33
U sin g R o wS et s wi th We bL o gi c S e rv er
The setResolvedValue() method makes the following changes: 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. 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. 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: In the WebLogic Server RowSets implementation, all rowsets originate as a WLCachedRowset. WLCachedRowSets can be interchangeably used as any of the standard rowset types that it extends. 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.
6-34
S ha re d R ow Se ts
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: 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). When data changes from any of the SharedRowSets are synchronized with the database, the base CachedRowSet is updated as well. Using SharedRowSets can increase performance by reducing the number of database round-trips required by an application. To create a SharedRowSet, you use the createShared() method in the WLCachedRowSet interface and cast the result as a WLCachedRowSet. For example:
WLCachedRowSet sharedrowset = (WLCachedRowSet)rowset.createShared();
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: 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. b. The application then sets the sorting criteria with the setSorter(java.util.Comparator) method. 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. WebLogic Server provides the SQLComparator object, which implements java.util.Comparator. You can use it to sort rows in a SortedRowSet by passing the list of columns that you want use as sorting criteria. For example:
Programming WebLogic JDBC 6-35
U sin g R o wS et s wi th We bL o gi c S e rv er
rs.setSorter(new weblogic.jdbc.rowset.SQLComparator("columnA,columnB,columnC"));
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: When referencing a column, you must use the column name; you cannot use column index number. The grammar supports the use of operators and mathematical operations, for example:
(colA + ColB) >=100.
In constructing the WHERE clause, you can use simple datatypes only, including: String Int Boolean Float Complex data types are not supported: Array BLOB
6-36 Programming WebLogic JDBC
O pt imis ti c C o n cu rr en c y P o l i c i es
CLOB Date
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. SQLPredicate filter = new SQLPredicate("ID >= 103"); rs.setFilter(filter);
For more information, see the Javadoc for the weblogic.jdbc.rowset.SQLPredicate class.
U sin g R o wS et s wi th We bL o gi c S e rv er
VERIFY_NONE VERIFY_AUTO_VERSION_COLUMNS VERIFY_VERSION_COLUMNS To illustrate the differences between these policies, we will use an example that uses the following: A very simple employees table with 3 columns:
CREATE TABLE employees ( e_id integer primary key, e_salary integer, e_name varchar(25) );
In the example for each of the optimistic concurrency policies listed below, the rowset will read this row from the employees table and set John Smith's salary to 20000. The example will then show how the optimistic concurrency policy affects the SQL code issued by the rowset.
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. In our example update, the rowset issues:
UPDATE employees SET e_salary = 20000 WHERE e_id = 1 AND e_salary=10000 AND e_name = 'John Smith';
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:
6-38
O pt imis ti c C o n cu rr en c y P o l i c i es
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. In our example update, the rowset issues:
UPDATE employees SET e_salary = 20000 WHERE e_id = 1
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.
6-39
U sin g R o wS et s wi th We bL o gi c S e rv er
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.
6-40
P e rfo rm a nc e Opt io ns
When multiple tables are included in the rowset, the rowset only verifies tables that have been updated.
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.
Programming WebLogic JDBC 6-41
U sin g R o wS et s wi th We bL o gi c S e rv er
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;
6-42
P e rfo rm a nc e Opt io ns
DELETE FROM employees WHERE e_id = 3 AND e_version = 1 OR e_id = 4 AND e_version = 3 OR e_id = 5 AND e_version = 10;
You can use the WLRowSetMetaData.setGroupDeleteSize to determine the number of rows included in a single DELETE statement. The default value is 50.
6-43
U sin g R o wS et s wi th We bL o gi c S e rv er
6-44
CHAPTER
Troubleshooting JDBC
The following sections describe some common issues when developing JDBC applications: Problems with Oracle on UNIX on page 7-1 Thread-related Problems on UNIX on page 7-1 Closing JDBC Objects on page 7-2 Using Microsoft SQL with Nested Triggers on page 7-3
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.
7-1
Tr ou b l e sh o ot i ng J D B C
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: If you are using Oracle drivers, use native threads. 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. 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.
7-2
} 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.
7-3
Tr ou b l e sh o ot i ng J D B C
CREATE TABLE CardEJBTable (cardno varchar(50) not null, employee varchar(50), primary key (cardno), foreign key (employee) references EmployeeEJB Table(name) on delete cascade) CREATE TRIGGER card on EmployeeEJBTable for delete as delete CardEJBTable where employee in (select name from deleted)
CREATE TRIGGER emp on CardEJBTable for delete as delete EmployeeEJBTable where card in (select cardno from deleted)
insert into EmployeeEJBTable values ('1',1000,'1') insert into CardEJBTable values ('1','1') DELETE FROM CardEJBTable WHERE cardno = 1
To work around this issue, do the following: 1. Run the following script to reset the nested trigger level to 0:
-- Start batch exec sp_configure 'nested triggers', 0 -- This set's the new value. reconfigure with override -- This makes the change permanent -- End batch
2. Verify the current value the SQL server by running the following script:
exec sp_configure 'nested triggers'
7-4
CREATE TABLE CardEJBTable (cardno varchar(50) not null, employee varchar(50), primary key (cardno), foreign key (employee) references EmployeeEJB Table(name) on delete cascade) CREATE TRIGGER emp on CardEJBTable for delete as delete EmployeeEJBTable where card in (select cardno from deleted)
insert into EmployeeEJBTable values ('1',1000,'1') insert into CardEJBTable values ('1','1') DELETE FROM CardEJBTable WHERE cardno = 1
The EJB code assumes that the record is not found and throws an appropriate error message. To work around this issue, run the following script:
exec sp_configure 'show advanced options', 1 reconfigure with override exec sp_configure 'disallow results from triggers',1 reconfigure with override
7-5