JDBC Exception Handling
Almost every JDBC method — connecting, preparing a statement, running a query — can throw a
SQLException. It's a checked exception, which means the Java compiler forces you to
either catch it or declare it with throws on your method. This page covers what's inside a
SQLException, the modern (and much safer) way to close JDBC resources, and how batch operations can
report more than one failure at once.
SQLException: What's Inside It
A SQLException carries more than just a message. Three pieces of information are especially useful
when something goes wrong:
| Method | What it gives you |
|---|---|
getMessage() | A human-readable description of what went wrong. |
getSQLState() | A standard 5-character code identifying the class of error (e.g. constraint violation, syntax error). |
getErrorCode() | A database-vendor-specific numeric error code (MySQL, Oracle, etc. each define their own). |
try {
Connection conn = DriverManager.getConnection(url, user, password);
} catch (SQLException e) {
System.out.println("Message: " + e.getMessage());
System.out.println("SQLState: " + e.getSQLState());
System.out.println("Error code: " + e.getErrorCode());
}
The Old Way: Closing in a finally Block
Before Java 7, closing JDBC resources meant a finally block with a null check for every single
resource — Connection, Statement, and ResultSet each needed their own
guarded close() call, and they had to be closed in the right order (result set
first, then statement, then connection).
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
conn = DriverManager.getConnection(url, user, password);
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT * FROM users");
// ... use rs ...
} finally {
if (rs != null) rs.close();
if (stmt != null) stmt.close();
if (conn != null) conn.close();
}
close() call, close resources in the wrong order, or have a close() itself throw and skip the rest. Any of those quietly leaks a database connection.The Modern Way: try-with-resources
Connection, PreparedStatement, and ResultSet all implement
AutoCloseable. That means you can declare all three in a single try-with-resources header, and Java
closes them automatically — in the reverse order they were opened — even if an exception is
thrown partway through.
try (
Connection conn = DriverManager.getConnection(url, user, password);
PreparedStatement ps = conn.prepareStatement("SELECT id, name FROM users WHERE active = 1");
ResultSet rs = ps.executeQuery()
) {
while (rs.next()) {
System.out.println(rs.getInt("id") + " - " + rs.getString("name"));
}
} catch (SQLException e) {
System.err.println("Query failed: " + e.getMessage());
}
// rs, then ps, then conn are all closed automatically here, in that order
finally block, no null checks, no risk of forgetting a resource. The compiler-generated cleanup code closes rs first, then ps, then conn — the exact reverse of the order they were declared.Chained Exceptions in Batch Operations
A single SQLException only tells you about one failure. But when you run a batch of statements
(see Batch Processing), more than one statement in the batch can fail.
JDBC lets a SQLException carry a chain of additional exceptions, accessible with
getNextException().
catch (SQLException e) {
while (e != null) {
System.err.println(e.getMessage());
e = e.getNextException();
}
}
Walking this chain is how you find out exactly which rows in a batch failed, instead of just knowing that "something" in the batch went wrong.
Connection for every request is expensive. Connection Pooling shows you how real applications reuse connections instead.