0
# Exception Handling
1
2
jOOQ-specific exception hierarchy for handling database errors, data access issues, and configuration problems with detailed error information and recovery strategies.
3
4
## Capabilities
5
6
### Base Exception Classes
7
8
Core exception hierarchy providing structured error handling for database operations.
9
10
```java { .api }
11
/**
12
* Base exception for all database access errors in jOOQ
13
* Runtime exception that wraps SQL exceptions and provides additional context
14
*/
15
public class DataAccessException extends RuntimeException {
16
/**
17
* Create exception with message
18
* @param message Error message
19
*/
20
public DataAccessException(String message);
21
22
/**
23
* Create exception with message and cause
24
* @param message Error message
25
* @param cause Underlying cause
26
*/
27
public DataAccessException(String message, Throwable cause);
28
29
/**
30
* Get the underlying SQL state if available
31
* @return SQL state from SQLException or null
32
*/
33
public String sqlState();
34
35
/**
36
* Get the underlying SQL error code if available
37
* @return SQL error code from SQLException or 0
38
*/
39
public int sqlErrorCode();
40
41
/**
42
* Get the SQL that caused this exception if available
43
* @return SQL string or null
44
*/
45
public String sql();
46
}
47
48
/**
49
* Exception for database definition (DDL) related errors
50
* Thrown when DDL operations fail or are invalid
51
*/
52
public class DataDefinitionException extends DataAccessException {
53
/**
54
* Create exception with message
55
* @param message Error message
56
*/
57
public DataDefinitionException(String message);
58
59
/**
60
* Create exception with message and cause
61
* @param message Error message
62
* @param cause Underlying cause
63
*/
64
public DataDefinitionException(String message, Throwable cause);
65
}
66
67
/**
68
* Base exception for configuration-related errors
69
* Thrown when jOOQ configuration is invalid or incomplete
70
*/
71
public class ConfigurationException extends DataAccessException {
72
/**
73
* Create exception with message
74
* @param message Error message
75
*/
76
public ConfigurationException(String message);
77
78
/**
79
* Create exception with message and cause
80
* @param message Error message
81
* @param cause Underlying cause
82
*/
83
public ConfigurationException(String message, Throwable cause);
84
}
85
```
86
87
### Query Result Exceptions
88
89
Exceptions related to unexpected query results or data validation failures.
90
91
```java { .api }
92
/**
93
* Base exception for invalid query results
94
* Thrown when query results don't match expectations
95
*/
96
public class InvalidResultException extends DataAccessException {
97
/**
98
* Create exception with message
99
* @param message Error message
100
*/
101
public InvalidResultException(String message);
102
103
/**
104
* Create exception with message and cause
105
* @param message Error message
106
* @param cause Underlying cause
107
*/
108
public InvalidResultException(String message, Throwable cause);
109
}
110
111
/**
112
* Exception thrown when no data is found but was expected
113
* Commonly thrown by fetchOne() when no records match
114
*/
115
public class NoDataFoundException extends InvalidResultException {
116
/**
117
* Create exception with default message
118
*/
119
public NoDataFoundException();
120
121
/**
122
* Create exception with custom message
123
* @param message Error message
124
*/
125
public NoDataFoundException(String message);
126
127
/**
128
* Create exception with message and cause
129
* @param message Error message
130
* @param cause Underlying cause
131
*/
132
public NoDataFoundException(String message, Throwable cause);
133
}
134
135
/**
136
* Exception thrown when more rows are returned than expected
137
* Commonly thrown by fetchOne() when multiple records match
138
*/
139
public class TooManyRowsException extends InvalidResultException {
140
/**
141
* Create exception with default message
142
*/
143
public TooManyRowsException();
144
145
/**
146
* Create exception with custom message
147
* @param message Error message
148
*/
149
public TooManyRowsException(String message);
150
151
/**
152
* Create exception with message and cause
153
* @param message Error message
154
* @param cause Underlying cause
155
*/
156
public TooManyRowsException(String message, Throwable cause);
157
158
/**
159
* Get the number of rows that were found
160
* @return Actual row count
161
*/
162
public int getRowCount();
163
}
164
165
/**
166
* Exception for data integrity constraint violations
167
* Thrown when database constraints are violated during DML operations
168
*/
169
public class DataChangedException extends DataAccessException {
170
/**
171
* Create exception with message
172
* @param message Error message
173
*/
174
public DataChangedException(String message);
175
176
/**
177
* Create exception with message and cause
178
* @param message Error message
179
* @param cause Underlying cause
180
*/
181
public DataChangedException(String message, Throwable cause);
182
}
183
```
184
185
### Mapping and Type Conversion Exceptions
186
187
Exceptions related to record mapping and data type conversions.
188
189
```java { .api }
190
/**
191
* Exception for record mapping errors
192
* Thrown when records cannot be mapped to POJOs or other types
193
*/
194
public class MappingException extends DataAccessException {
195
/**
196
* Create exception with message
197
* @param message Error message
198
*/
199
public MappingException(String message);
200
201
/**
202
* Create exception with message and cause
203
* @param message Error message
204
* @param cause Underlying cause
205
*/
206
public MappingException(String message, Throwable cause);
207
}
208
209
/**
210
* Exception for data type conversion errors
211
* Thrown when values cannot be converted between Java and SQL types
212
*/
213
public class DataTypeException extends DataAccessException {
214
/**
215
* Create exception with message
216
* @param message Error message
217
*/
218
public DataTypeException(String message);
219
220
/**
221
* Create exception with message and cause
222
* @param message Error message
223
* @param cause Underlying cause
224
*/
225
public DataTypeException(String message, Throwable cause);
226
}
227
```
228
229
**Usage Examples:**
230
231
```java
232
// Handling specific jOOQ exceptions
233
try {
234
// Expect exactly one record
235
AuthorRecord author = create.selectFrom(AUTHOR)
236
.where(AUTHOR.EMAIL.eq("user@example.com"))
237
.fetchOne();
238
239
} catch (NoDataFoundException e) {
240
// No author found with that email
241
System.out.println("Author not found: " + e.getMessage());
242
243
} catch (TooManyRowsException e) {
244
// Multiple authors found (data integrity issue)
245
System.out.println("Multiple authors found: " + e.getRowCount());
246
247
} catch (DataAccessException e) {
248
// General database error
249
System.out.println("Database error: " + e.getMessage());
250
System.out.println("SQL State: " + e.sqlState());
251
System.out.println("SQL Error Code: " + e.sqlErrorCode());
252
}
253
254
// Handling mapping exceptions
255
try {
256
List<AuthorPojo> authors = create.selectFrom(AUTHOR)
257
.fetch()
258
.into(AuthorPojo.class);
259
260
} catch (MappingException e) {
261
System.out.println("Failed to map records to POJO: " + e.getMessage());
262
}
263
264
// Handling constraint violations
265
try {
266
create.insertInto(AUTHOR)
267
.set(AUTHOR.EMAIL, "duplicate@example.com") // Violates unique constraint
268
.execute();
269
270
} catch (DataChangedException e) {
271
System.out.println("Constraint violation: " + e.getMessage());
272
}
273
```
274
275
### Exception Context and Information
276
277
Additional information available in jOOQ exceptions for debugging and error recovery.
278
279
```java { .api }
280
public interface ExecuteContext {
281
/**
282
* Get the SQL that was being executed when exception occurred
283
* @return SQL string
284
*/
285
String sql();
286
287
/**
288
* Get the bind values that were used
289
* @return Array of bind values
290
*/
291
Object[] bindings();
292
293
/**
294
* Get the SQLException that caused the error
295
* @return Original SQLException or null
296
*/
297
SQLException sqlException();
298
299
/**
300
* Get the execution time before the exception
301
* @return Execution time in nanoseconds
302
*/
303
long executionTime();
304
305
/**
306
* Get the connection that was being used
307
* @return JDBC Connection
308
*/
309
Connection connection();
310
311
/**
312
* Get the configuration context
313
* @return Configuration instance
314
*/
315
Configuration configuration();
316
}
317
```
318
319
### Error Recovery Strategies
320
321
Common patterns for handling and recovering from jOOQ exceptions.
322
323
**Usage Examples:**
324
325
```java
326
// Graceful degradation for optional data
327
public Optional<AuthorRecord> findAuthorByEmail(String email) {
328
try {
329
return Optional.of(
330
create.selectFrom(AUTHOR)
331
.where(AUTHOR.EMAIL.eq(email))
332
.fetchOne()
333
);
334
} catch (NoDataFoundException e) {
335
return Optional.empty();
336
}
337
}
338
339
// Retry logic for transient errors
340
public void updateAuthorWithRetry(int authorId, String newName) {
341
int maxRetries = 3;
342
int retryCount = 0;
343
344
while (retryCount < maxRetries) {
345
try {
346
create.update(AUTHOR)
347
.set(AUTHOR.FIRST_NAME, newName)
348
.where(AUTHOR.ID.eq(authorId))
349
.execute();
350
return; // Success
351
352
} catch (DataAccessException e) {
353
retryCount++;
354
if (retryCount >= maxRetries) {
355
throw e; // Give up after max retries
356
}
357
358
// Wait before retry
359
try {
360
Thread.sleep(1000 * retryCount);
361
} catch (InterruptedException ie) {
362
Thread.currentThread().interrupt();
363
throw new RuntimeException("Interrupted during retry", ie);
364
}
365
}
366
}
367
}
368
369
// Safe batch operations with partial failure handling
370
public List<String> batchInsertAuthors(List<AuthorPojo> authors) {
371
List<String> errors = new ArrayList<>();
372
373
for (AuthorPojo author : authors) {
374
try {
375
create.insertInto(AUTHOR)
376
.set(AUTHOR.FIRST_NAME, author.getFirstName())
377
.set(AUTHOR.LAST_NAME, author.getLastName())
378
.set(AUTHOR.EMAIL, author.getEmail())
379
.execute();
380
381
} catch (DataChangedException e) {
382
// Log constraint violation but continue with other records
383
errors.add("Failed to insert " + author.getEmail() + ": " + e.getMessage());
384
385
} catch (DataAccessException e) {
386
// Log general error but continue
387
errors.add("Database error for " + author.getEmail() + ": " + e.getMessage());
388
}
389
}
390
391
return errors;
392
}
393
394
// Connection recovery for connection pool issues
395
public <T> T executeWithConnectionRecovery(Function<DSLContext, T> operation) {
396
try {
397
return operation.apply(create);
398
399
} catch (DataAccessException e) {
400
// Check if it's a connection-related error
401
if (e.sqlState() != null && e.sqlState().startsWith("08")) {
402
// Connection error - try to get a new connection
403
try (CloseableDSLContext newCreate = using(dataSource, SQLDialect.POSTGRES)) {
404
return operation.apply(newCreate);
405
}
406
}
407
throw e; // Re-throw if not connection-related
408
}
409
}
410
```
411
412
### Custom Exception Handling
413
414
Patterns for creating application-specific exception handling around jOOQ.
415
416
```java { .api }
417
// Custom exception wrapper
418
public class AuthorServiceException extends RuntimeException {
419
private final String sqlState;
420
private final int errorCode;
421
422
public AuthorServiceException(String message, DataAccessException cause) {
423
super(message, cause);
424
this.sqlState = cause.sqlState();
425
this.errorCode = cause.sqlErrorCode();
426
}
427
428
public String getSqlState() { return sqlState; }
429
public int getErrorCode() { return errorCode; }
430
431
public boolean isConstraintViolation() {
432
return "23000".equals(sqlState) || "23505".equals(sqlState);
433
}
434
435
public boolean isConnectionError() {
436
return sqlState != null && sqlState.startsWith("08");
437
}
438
}
439
440
// Service layer exception translation
441
public class AuthorService {
442
private final DSLContext create;
443
444
public AuthorRecord createAuthor(String firstName, String lastName, String email) {
445
try {
446
return create.insertInto(AUTHOR)
447
.set(AUTHOR.FIRST_NAME, firstName)
448
.set(AUTHOR.LAST_NAME, lastName)
449
.set(AUTHOR.EMAIL, email)
450
.returning()
451
.fetchOne();
452
453
} catch (DataChangedException e) {
454
if (e.sqlState() != null && e.sqlState().equals("23505")) {
455
throw new AuthorServiceException("Email already exists: " + email, e);
456
}
457
throw new AuthorServiceException("Failed to create author", e);
458
459
} catch (DataAccessException e) {
460
throw new AuthorServiceException("Database error while creating author", e);
461
}
462
}
463
}
464
```