For MySQL (mysql-connector-j), “proper” SQLException handling is the same core approach as JDBC in general, plus a few MySQL-specific signals (SQLState + error code) that are worth using for translation/retry decisions.
1) Keep the important diagnostics (MySQL error code + SQLState)
MySQL gives you two invaluable fields:
e.getErrorCode()→ MySQL vendor error code (e.g.,1062for duplicate key)e.getSQLState()→ SQLState (often23000,40001, etc.)
A good pattern is: wrap once with context, but preserve those fields.
package org.kodejava.jdbc;
import java.sql.SQLException;
public final class MySqlExceptions {
public static RuntimeException translate(String operation, SQLException e) {
String msg = operation
+ " failed (SQLState=" + e.getSQLState()
+ ", errorCode=" + e.getErrorCode() + ")";
// Keep e as the cause.
return switch (e.getErrorCode()) {
case 1062 ->
new IllegalStateException(msg + " - duplicate key", e); // unique constraint violation
case 1213 ->
new IllegalStateException(msg + " - deadlock", e); // often retryable
case 1205 ->
new IllegalStateException(msg + " - lock wait timeout", e); // often retryable
default -> new RuntimeException(msg, e);
};
}
}
2) MySQL error codes you’ll commonly care about
These are the ones that usually drive different handling:
| Situation | MySQL error code | Typical SQLState | What to do |
|---|---|---|---|
| Unique constraint violation (“Duplicate entry”) | 1062 | 23000 | Return “already exists” / map to 409 / domain error |
| Deadlock found | 1213 | 40001 | Often safe to retry the whole transaction |
| Lock wait timeout exceeded | 1205 | often 41000 |
Often retry or surface “please retry” |
| Foreign key constraint fails | 1451/1452 | 23000 |
Map to domain validation (cannot delete/insert due to FK) |
| Connection/link failure | varies | 08xxx / 08S01 |
Treat as transient infra failure; maybe retry with backoff |
Rule of thumb: prefer the vendor error code for MySQL-specific branching (it’s the most consistent), and keep SQLState for general categorization/logging.
3) Retrying safely (only for the right failures)
Only retry if:
- the operation is idempotent, or you’re retrying the entire transaction from the beginning, and
- the failure is one of the known transient classes (deadlock / lock timeout / connection hiccup).
A minimal “should retry?” helper:
package org.kodejava.jdbc;
import java.sql.SQLException;
public final class MySqlRetry {
public static boolean isRetryable(SQLException e) {
int code = e.getErrorCode();
String state = e.getSQLState();
// MySQL deadlock / lock wait timeout
if (code == 1213 || code == 1205) return true;
// Connection exception class (SQLState starts with "08")
if (state != null && state.startsWith("08")) return true;
return false;
}
}
If you do retry, keep it small (e.g., 2–3 attempts) with jittered backoff, and log the final failure with the full chain (getNextException()) and suppressed exceptions.
4) Transactions: rollback without hiding the original error
With MySQL, rollback can also throw if the connection is broken. Best practice: attach rollback failure as suppressed so you don’t lose the root cause.
package org.kodejava.jdbc;
import java.sql.Connection;
import java.sql.SQLException;
public final class TxUtil {
public static void rollbackQuietly(Connection con, SQLException original) {
try {
con.rollback();
} catch (SQLException rb) {
original.addSuppressed(rb);
}
}
}
5) MySQL Connector/J note: you usually don’t need Class.forName(...)
With modern JDBC drivers (including MySQL Connector/J 8+), the driver is auto-registered via the Service Provider mechanism. Calling Class.forName("com.mysql.cj.jdbc.Driver") is typically unnecessary unless you’re in a very unusual classloading environment.
6) What to log (and what not to log)
Log:
- operation name (e.g.,
"insert user") - SQLState, error code
- exception chain (
getNextException()) - safe parameter identifiers (e.g., user id), not secrets
Avoid logging:
- credentials
- sensitive values (passwords, tokens)
- huge SQL strings with embedded data (use prepared statements so you don’t have that problem)
