After you have a Connection to DB2, JDBC is a small set of interfaces: you prepare SQL, bind parameters, fetch rows, and commit. This page covers Statement, PreparedStatement, CallableStatement, ResultSet, transactions and isolation, batch, LOBs and XML, metadata, stored-procedure result sets, and SQLException.
| Interface | Use |
|---|---|
| Statement | Ad-hoc SQL, DDL, simple executeUpdate/executeQuery |
| PreparedStatement | SQL with ? markers; setInt/setString; batch |
| CallableStatement | CALL proc(?,?,?); registerOutParameter; result sets |
executeQuery returns a ResultSet (SELECT). executeUpdate returns a row count (INSERT, UPDATE, DELETE, some DDL). execute handles mixed or unknown statement types and procedure calls that may return multiple results.
12345678try (Statement st = con.createStatement(); ResultSet rs = st.executeQuery( "SELECT EMPNO, LASTNAME FROM HR.EMPLOYEE WHERE WORKDEPT = 'A00'")) { while (rs.next()) { String empno = rs.getString(1); String name = rs.getString("LASTNAME"); } }
That form concatenates a literal department. Fine for a fixed code; unsafe for a screen field. Use a prepared statement instead.
Markers are ?, numbered from 1. Types must match the column (setInt for INTEGER, setBigDecimal for DECIMAL, setDate/setTimestamp with java.sql types).
123456String sql = "UPDATE HR.EMPLOYEE SET BONUS = ? WHERE EMPNO = ?"; try (PreparedStatement ps = con.prepareStatement(sql)) { ps.setBigDecimal(1, bonus); ps.setString(2, empno); int rows = ps.executeUpdate(); }
Reuse the same PreparedStatement object in a loop when the SQL text is fixed. The IBM driver and Db2 dynamic statement cache then see one statement. setNull(index, sqlType) for SQL NULL. Do not put quotes around markers.
12345678try (CallableStatement cs = con.prepareCall("CALL HR.GET_EMP(?, ?)")) { cs.setString(1, empno); cs.registerOutParameter(2, Types.VARCHAR); cs.execute(); String lastName = cs.getString(2); ResultSet rs = cs.getResultSet(); // drain rs, then getMoreResults() if the procedure returns more sets }
Procedures that return result sets open WITH RETURN cursors. Walk getResultSet and getMoreResults until there are none. INOUT parameters are registered and also set before execute. Match the procedure’s PARAMETER STYLE and the driver’s handling of result-set locators.
createStatement and prepareStatement overloads take resultSetType, resultSetConcurrency, and resultSetHoldability:
getString/getInt and friends: check wasNull() after getXxx for nullable columns. Column indexes are 1-based. Prefer column labels when the SELECT list is stable; indexes survive renamed labels but break when the list changes.
JDBC default is autocommit on. Each successful statement is its own unit of work. For multi-statement business logic:
123456789con.setAutoCommit(false); try { ps1.executeUpdate(); ps2.executeUpdate(); con.commit(); } catch (SQLException e) { con.rollback(); throw e; }
Changing autocommit commits the current unit of work if you are not already on a boundary. Connections in a global / XA transaction must not call commit, rollback, or setAutoCommit(true); the JTA coordinator owns the boundary (see DDF two-phase commit).
| Connection isolation | Db2 ISOLATION |
|---|---|
| TRANSACTION_READ_UNCOMMITTED | UR |
| TRANSACTION_READ_COMMITTED | CS |
| TRANSACTION_REPEATABLE_READ | RS |
| TRANSACTION_SERIALIZABLE | RR |
setTransactionIsolation on the Connection. The IBM driver maps these to UR/CS/RS/RR for dynamic SQL on that connection. CS is the usual OLTP choice, matching BIND ISOLATION(CS).
For many similar INSERTs or UPDATEs:
12345ps.setInt(1, id); ps.setString(2, name); ps.addBatch(); // repeat int[] counts = ps.executeBatch();
executeBatch returns per-statement update counts (or SUCCESS_NO_INFO). A BatchUpdateException includes counts up to the failure. Batching cuts DRDA round trips; huge batches make a single timeout or -803 harder to isolate. Keep autocommit off and commit every N rows for restartability.
BLOB/CLOB/DBCLOB: setBlob/setClob or streams. Reading, getBlob then getBinaryStream rather than getBytes on multi-megabyte values. Progressive streaming and locators avoid pulling the whole LOB into the JVM. Free locators when the driver documents that you must.
XML: java.sql.SQLXML with getSQLXML/setSQLXML, or character streams. CCSID and XMLPARSE rules still apply on the server; the driver is only the pipe.
Metadata:
Almost every JDBC method can throw SQLException. On Db2:
JDBC 4 adds subclasses (SQLTransientConnectionException, SQLIntegrityConstraintViolationException for -803). Catch the subclass you can handle; log SQLCODE and SQLSTATE for operations. Do not retry -803 blindly; fix the key. Do retry some 08xxx after a pool validation failure.
A Statement is shouting a whole sentence every time. A PreparedStatement is a fill-in worksheet: the teacher keeps the printed page and you only write today’s numbers in the blanks (?). A ResultSet is the stack of answer sheets you flip through. Autocommit is handing in each worksheet the second you finish a line; turning it off means you wait until the whole test is done (commit) or you crumple it (rollback). Batch is stapling twenty worksheets and handing them in together. SQLException is the red X with a number (SQLCODE) explaining what went wrong.
1. Why prefer PreparedStatement over Statement for repeated SQL?
2. What does Connection.setAutoCommit(false) do?
3. Which JDBC isolation maps to Db2 CS?
4. How do you call a stored procedure that returns a result set?
5. Where do you find SQLCODE in JDBC?