0
# Exception Handling
1
2
Spring ORM provides comprehensive exception translation from ORM-specific exceptions to Spring's consistent DataAccessException hierarchy. This unified approach ensures consistent error handling across different ORM frameworks while preserving detailed error information for proper application response and debugging.
3
4
## Capabilities
5
6
### Base ORM Exception Types
7
8
Foundation exception classes that provide common functionality for object-relational mapping error scenarios, including optimistic locking failures and object retrieval problems.
9
10
```java { .api }
11
public class ObjectOptimisticLockingFailureException extends OptimisticLockingFailureException {
12
public ObjectOptimisticLockingFailureException(String msg, Throwable cause);
13
public ObjectOptimisticLockingFailureException(Class<?> persistentClass, Object identifier);
14
public ObjectOptimisticLockingFailureException(Class<?> persistentClass, Object identifier, Throwable cause);
15
public ObjectOptimisticLockingFailureException(String persistentClassName, Object identifier);
16
public ObjectOptimisticLockingFailureException(String persistentClassName, Object identifier, String msg, Throwable cause);
17
18
public Class<?> getPersistentClass();
19
public String getPersistentClassName();
20
public Object getIdentifier();
21
}
22
23
public class ObjectRetrievalFailureException extends DataRetrievalFailureException {
24
public ObjectRetrievalFailureException(String msg, Throwable cause);
25
public ObjectRetrievalFailureException(Class<?> persistentClass, Object identifier);
26
public ObjectRetrievalFailureException(Class<?> persistentClass, Object identifier, String msg, Throwable cause);
27
public ObjectRetrievalFailureException(String persistentClassName, Object identifier);
28
public ObjectRetrievalFailureException(String persistentClassName, Object identifier, String msg, Throwable cause);
29
30
public Class<?> getPersistentClass();
31
public String getPersistentClassName();
32
public Object getIdentifier();
33
}
34
```
35
36
#### Usage Example
37
38
```java
39
@Service
40
@Transactional
41
public class EntityService {
42
43
@PersistenceContext
44
private EntityManager entityManager;
45
46
public void updateEntity(Long id, String newValue) {
47
try {
48
MyEntity entity = entityManager.find(MyEntity.class, id);
49
if (entity == null) {
50
throw new ObjectRetrievalFailureException(MyEntity.class, id);
51
}
52
53
entity.setValue(newValue);
54
entity.setVersion(entity.getVersion() + 1); // Manual versioning for example
55
entityManager.merge(entity);
56
57
} catch (OptimisticLockException ex) {
58
// JPA exception translated to Spring exception
59
throw new ObjectOptimisticLockingFailureException(MyEntity.class, id, ex);
60
}
61
}
62
63
public MyEntity findEntityById(Long id) {
64
MyEntity entity = entityManager.find(MyEntity.class, id);
65
if (entity == null) {
66
throw new ObjectRetrievalFailureException(
67
MyEntity.class,
68
id,
69
"Entity not found with id: " + id,
70
null
71
);
72
}
73
return entity;
74
}
75
}
76
77
// Exception handling in controller
78
@RestController
79
@RequestMapping("/api/entities")
80
public class EntityController {
81
82
@Autowired
83
private EntityService entityService;
84
85
@GetMapping("/{id}")
86
public ResponseEntity<EntityDto> getEntity(@PathVariable Long id) {
87
try {
88
MyEntity entity = entityService.findEntityById(id);
89
return ResponseEntity.ok(convertToDto(entity));
90
} catch (ObjectRetrievalFailureException ex) {
91
log.error("Entity not found: class={}, id={}",
92
ex.getPersistentClassName(), ex.getIdentifier());
93
return ResponseEntity.notFound().build();
94
}
95
}
96
97
@PutMapping("/{id}")
98
public ResponseEntity<String> updateEntity(@PathVariable Long id, @RequestBody UpdateRequest request) {
99
try {
100
entityService.updateEntity(id, request.getValue());
101
return ResponseEntity.ok("Updated successfully");
102
} catch (ObjectOptimisticLockingFailureException ex) {
103
log.warn("Optimistic locking failure: class={}, id={}",
104
ex.getPersistentClassName(), ex.getIdentifier());
105
return ResponseEntity.status(HttpStatus.CONFLICT)
106
.body("Entity was modified by another user. Please refresh and try again.");
107
} catch (ObjectRetrievalFailureException ex) {
108
return ResponseEntity.notFound().build();
109
}
110
}
111
}
112
```
113
114
### JPA Exception Translation
115
116
Automatic translation of JPA-specific exceptions to Spring's DataAccessException hierarchy with preservation of detailed error information.
117
118
```java { .api }
119
public class JpaObjectRetrievalFailureException extends ObjectRetrievalFailureException {
120
public JpaObjectRetrievalFailureException(EntityNotFoundException ex);
121
}
122
123
public class JpaOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
124
public JpaOptimisticLockingFailureException(OptimisticLockException ex);
125
}
126
127
public class JpaSystemException extends UncategorizedDataAccessException {
128
public JpaSystemException(PersistenceException ex);
129
}
130
131
// Exception translation interface
132
public interface PersistenceExceptionTranslator {
133
DataAccessException translateExceptionIfPossible(RuntimeException ex);
134
}
135
136
public class DefaultJpaDialect implements JpaDialect, Serializable {
137
public DataAccessException translateExceptionIfPossible(RuntimeException ex);
138
}
139
140
public class HibernateJpaDialect extends DefaultJpaDialect {
141
public DataAccessException translateExceptionIfPossible(RuntimeException ex);
142
}
143
```
144
145
#### Usage Example
146
147
```java
148
@Configuration
149
public class JpaExceptionHandlingConfig {
150
151
@Bean
152
public PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
153
return new PersistenceExceptionTranslationPostProcessor();
154
}
155
156
@Bean
157
public JpaDialect jpaDialect() {
158
return new HibernateJpaDialect(); // Or DefaultJpaDialect
159
}
160
}
161
162
@Repository
163
@org.springframework.dao.annotation.Repository // Enables exception translation
164
public class JpaUserRepository {
165
166
@PersistenceContext
167
private EntityManager entityManager;
168
169
public User findByEmail(String email) {
170
try {
171
return entityManager.createQuery(
172
"SELECT u FROM User u WHERE u.email = :email",
173
User.class
174
)
175
.setParameter("email", email)
176
.getSingleResult();
177
178
} catch (NoResultException ex) {
179
// Automatically translated to EmptyResultDataAccessException
180
throw ex;
181
} catch (NonUniqueResultException ex) {
182
// Automatically translated to IncorrectResultSizeDataAccessException
183
throw ex;
184
}
185
}
186
187
public void saveUser(User user) {
188
try {
189
entityManager.persist(user);
190
} catch (EntityExistsException ex) {
191
// Automatically translated to DuplicateKeyException
192
throw ex;
193
} catch (PersistenceException ex) {
194
// Automatically translated to JpaSystemException
195
throw ex;
196
}
197
}
198
199
public void updateUserOptimistically(User user) {
200
try {
201
entityManager.merge(user);
202
entityManager.flush(); // Force immediate execution
203
} catch (OptimisticLockException ex) {
204
// Automatically translated to JpaOptimisticLockingFailureException
205
throw ex;
206
}
207
}
208
}
209
210
// Global exception handler for JPA exceptions
211
@ControllerAdvice
212
public class JpaExceptionHandler {
213
214
@ExceptionHandler(JpaObjectRetrievalFailureException.class)
215
public ResponseEntity<ErrorResponse> handleObjectNotFound(JpaObjectRetrievalFailureException ex) {
216
ErrorResponse error = new ErrorResponse(
217
"ENTITY_NOT_FOUND",
218
"Requested entity not found",
219
Map.of(
220
"entityClass", ex.getPersistentClassName(),
221
"identifier", ex.getIdentifier()
222
)
223
);
224
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
225
}
226
227
@ExceptionHandler(JpaOptimisticLockingFailureException.class)
228
public ResponseEntity<ErrorResponse> handleOptimisticLocking(JpaOptimisticLockingFailureException ex) {
229
ErrorResponse error = new ErrorResponse(
230
"OPTIMISTIC_LOCK_FAILURE",
231
"Entity was modified by another user",
232
Map.of(
233
"entityClass", ex.getPersistentClassName(),
234
"identifier", ex.getIdentifier()
235
)
236
);
237
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
238
}
239
240
@ExceptionHandler(JpaSystemException.class)
241
public ResponseEntity<ErrorResponse> handleJpaSystem(JpaSystemException ex) {
242
log.error("JPA system error", ex);
243
ErrorResponse error = new ErrorResponse(
244
"PERSISTENCE_ERROR",
245
"A database error occurred",
246
null
247
);
248
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
249
}
250
}
251
```
252
253
### Hibernate Exception Translation
254
255
Comprehensive translation of Hibernate-specific exceptions to Spring's DataAccessException hierarchy with support for Hibernate's rich exception model.
256
257
```java { .api }
258
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
259
public DataAccessException translateExceptionIfPossible(RuntimeException ex);
260
}
261
262
public class HibernateJdbcException extends UncategorizedDataAccessException {
263
public HibernateJdbcException(JDBCException ex);
264
public String getSQLExceptionMessage();
265
public String getSql();
266
public int getErrorCode();
267
public String getSQLState();
268
}
269
270
public class HibernateObjectRetrievalFailureException extends ObjectRetrievalFailureException {
271
public HibernateObjectRetrievalFailureException(ObjectNotFoundException ex);
272
public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex);
273
}
274
275
public class HibernateOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
276
public HibernateOptimisticLockingFailureException(StaleObjectStateException ex);
277
public HibernateOptimisticLockingFailureException(StaleStateException ex);
278
}
279
280
public class HibernateQueryException extends InvalidDataAccessResourceUsageException {
281
public HibernateQueryException(QueryException ex);
282
public String getQueryString();
283
}
284
285
public class HibernateSystemException extends UncategorizedDataAccessException {
286
public HibernateSystemException(HibernateException ex);
287
}
288
```
289
290
#### Usage Example
291
292
```java
293
@Configuration
294
public class HibernateExceptionConfig {
295
296
@Bean
297
public HibernateExceptionTranslator hibernateExceptionTranslator() {
298
return new HibernateExceptionTranslator();
299
}
300
301
@Bean
302
public LocalSessionFactoryBean sessionFactory() {
303
LocalSessionFactoryBean factory = new LocalSessionFactoryBean();
304
factory.setDataSource(dataSource());
305
factory.setPackagesToScan("com.example.entity");
306
307
Properties hibernateProperties = new Properties();
308
hibernateProperties.setProperty("hibernate.dialect", "org.hibernate.dialect.PostgreSQLDialect");
309
hibernateProperties.setProperty("hibernate.show_sql", "true");
310
factory.setHibernateProperties(hibernateProperties);
311
312
return factory;
313
}
314
}
315
316
@Repository
317
@org.springframework.dao.annotation.Repository // Enables exception translation
318
public class HibernateProductRepository {
319
320
@Autowired
321
private HibernateTemplate hibernateTemplate;
322
323
public Product findByCode(String code) {
324
try {
325
return hibernateTemplate.execute(session -> {
326
Query<Product> query = session.createQuery(
327
"FROM Product p WHERE p.code = :code",
328
Product.class
329
);
330
query.setParameter("code", code);
331
return query.uniqueResult(); // May throw NonUniqueResultException
332
});
333
} catch (ObjectNotFoundException ex) {
334
// Automatically translated to HibernateObjectRetrievalFailureException
335
throw ex;
336
} catch (QueryException ex) {
337
// Automatically translated to HibernateQueryException
338
throw ex;
339
}
340
}
341
342
public void saveProduct(Product product) {
343
try {
344
hibernateTemplate.save(product);
345
} catch (JDBCException ex) {
346
// Automatically translated to HibernateJdbcException
347
throw ex;
348
} catch (HibernateException ex) {
349
// Automatically translated to HibernateSystemException
350
throw ex;
351
}
352
}
353
354
public void updateProductWithOptimisticLocking(Product product) {
355
try {
356
hibernateTemplate.update(product);
357
} catch (StaleObjectStateException ex) {
358
// Automatically translated to HibernateOptimisticLockingFailureException
359
throw ex;
360
}
361
}
362
363
public List<Product> findProductsByCategory(String category) {
364
try {
365
return hibernateTemplate.execute(session -> {
366
// Complex query that might have syntax errors
367
Query<Product> query = session.createQuery(
368
"SELECT p FROM Product p JOIN p.category c WHERE c.name = :categoryName ORDER BY p.name",
369
Product.class
370
);
371
query.setParameter("categoryName", category);
372
return query.getResultList();
373
});
374
} catch (QueryException ex) {
375
// Translated to HibernateQueryException with query details
376
log.error("Invalid query syntax: {}", ex.getQueryString(), ex);
377
throw ex;
378
}
379
}
380
}
381
382
// Specialized exception handler for Hibernate exceptions
383
@ControllerAdvice
384
public class HibernateExceptionHandler {
385
386
@ExceptionHandler(HibernateJdbcException.class)
387
public ResponseEntity<ErrorResponse> handleJdbcException(HibernateJdbcException ex) {
388
log.error("Hibernate JDBC exception: SQL={}, Error Code={}, SQL State={}",
389
ex.getSql(), ex.getErrorCode(), ex.getSQLState(), ex);
390
391
ErrorResponse error = new ErrorResponse(
392
"DATABASE_ERROR",
393
"Database operation failed: " + ex.getSQLExceptionMessage(),
394
Map.of(
395
"errorCode", ex.getErrorCode(),
396
"sqlState", ex.getSQLState()
397
)
398
);
399
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
400
}
401
402
@ExceptionHandler(HibernateQueryException.class)
403
public ResponseEntity<ErrorResponse> handleQueryException(HibernateQueryException ex) {
404
log.error("Hibernate query exception: Query={}", ex.getQueryString(), ex);
405
406
ErrorResponse error = new ErrorResponse(
407
"INVALID_QUERY",
408
"Query execution failed",
409
Map.of("query", ex.getQueryString())
410
);
411
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(error);
412
}
413
414
@ExceptionHandler(HibernateObjectRetrievalFailureException.class)
415
public ResponseEntity<ErrorResponse> handleObjectRetrievalFailure(HibernateObjectRetrievalFailureException ex) {
416
ErrorResponse error = new ErrorResponse(
417
"ENTITY_NOT_FOUND",
418
"Requested entity not found",
419
Map.of(
420
"entityClass", ex.getPersistentClassName(),
421
"identifier", ex.getIdentifier()
422
)
423
);
424
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
425
}
426
427
@ExceptionHandler(HibernateOptimisticLockingFailureException.class)
428
public ResponseEntity<ErrorResponse> handleOptimisticLockingFailure(HibernateOptimisticLockingFailureException ex) {
429
ErrorResponse error = new ErrorResponse(
430
"OPTIMISTIC_LOCK_FAILURE",
431
"Entity was modified by another user",
432
Map.of(
433
"entityClass", ex.getPersistentClassName(),
434
"identifier", ex.getIdentifier()
435
)
436
);
437
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
438
}
439
440
@ExceptionHandler(HibernateSystemException.class)
441
public ResponseEntity<ErrorResponse> handleSystemException(HibernateSystemException ex) {
442
log.error("Hibernate system exception", ex);
443
444
ErrorResponse error = new ErrorResponse(
445
"SYSTEM_ERROR",
446
"An internal system error occurred",
447
null
448
);
449
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
450
}
451
}
452
```
453
454
### Exception Translation Infrastructure
455
456
Core infrastructure that enables automatic exception translation through AOP and bean post-processing.
457
458
```java { .api }
459
@Target(ElementType.TYPE)
460
@Retention(RetentionPolicy.RUNTIME)
461
@Documented
462
public @interface Repository {
463
String value() default "";
464
}
465
466
public class PersistenceExceptionTranslationPostProcessor implements BeanPostProcessor, PriorityOrdered {
467
public void setRepositoryAnnotationType(Class<? extends Annotation> repositoryAnnotationType);
468
public void setPersistenceExceptionTranslators(PersistenceExceptionTranslator... persistenceExceptionTranslators);
469
470
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
471
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
472
public int getOrder();
473
}
474
475
public class PersistenceExceptionTranslationInterceptor implements MethodInterceptor, PersistenceExceptionTranslator, InitializingBean {
476
public void setPersistenceExceptionTranslators(PersistenceExceptionTranslator... persistenceExceptionTranslators);
477
public void setAlwaysTranslate(boolean alwaysTranslate);
478
479
public Object invoke(MethodInvocation mi) throws Throwable;
480
public DataAccessException translateExceptionIfPossible(RuntimeException ex);
481
}
482
```
483
484
#### Usage Example
485
486
```java
487
@Configuration
488
public class ExceptionTranslationConfig {
489
490
@Bean
491
public static PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor() {
492
PersistenceExceptionTranslationPostProcessor processor =
493
new PersistenceExceptionTranslationPostProcessor();
494
495
// Set custom repository annotation (optional)
496
processor.setRepositoryAnnotationType(MyRepository.class);
497
498
// Set custom exception translators
499
processor.setPersistenceExceptionTranslators(
500
new HibernateExceptionTranslator(),
501
new CustomExceptionTranslator()
502
);
503
504
return processor;
505
}
506
507
// Custom exception translator
508
@Bean
509
public PersistenceExceptionTranslator customExceptionTranslator() {
510
return new CustomExceptionTranslator();
511
}
512
}
513
514
// Custom exception translator implementation
515
public class CustomExceptionTranslator implements PersistenceExceptionTranslator {
516
517
@Override
518
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
519
// Handle custom exceptions
520
if (ex instanceof CustomPersistenceException) {
521
CustomPersistenceException cpe = (CustomPersistenceException) ex;
522
return new CustomDataAccessException("Custom persistence error: " + cpe.getMessage(), cpe);
523
}
524
525
// Handle validation exceptions
526
if (ex instanceof ValidationException) {
527
return new DataIntegrityViolationException("Validation failed: " + ex.getMessage(), ex);
528
}
529
530
// Handle timeout exceptions
531
if (ex instanceof QueryTimeoutException) {
532
return new QueryTimeoutException("Query execution timeout", ex);
533
}
534
535
return null; // Cannot translate this exception
536
}
537
}
538
539
// AOP-based exception translation for non-repository classes
540
@Configuration
541
@EnableAspectJAutoProxy
542
public class AopExceptionTranslationConfig {
543
544
@Bean
545
public PersistenceExceptionTranslationInterceptor persistenceExceptionTranslationInterceptor() {
546
PersistenceExceptionTranslationInterceptor interceptor =
547
new PersistenceExceptionTranslationInterceptor();
548
549
interceptor.setPersistenceExceptionTranslators(
550
new HibernateExceptionTranslator(),
551
new CustomExceptionTranslator()
552
);
553
554
interceptor.setAlwaysTranslate(true); // Always attempt translation
555
556
return interceptor;
557
}
558
559
@Bean
560
public Advisor persistenceExceptionTranslationAdvisor() {
561
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
562
pointcut.setExpression("execution(* com.example.service.*Service.*(..))");
563
564
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor();
565
advisor.setPointcut(pointcut);
566
advisor.setAdvice(persistenceExceptionTranslationInterceptor());
567
568
return advisor;
569
}
570
}
571
572
// Usage without @Repository annotation
573
@MyRepository // Custom repository annotation
574
public class CustomUserRepository {
575
576
@PersistenceContext
577
private EntityManager entityManager;
578
579
public User findByUsername(String username) {
580
// Exceptions automatically translated due to @MyRepository annotation
581
try {
582
return entityManager.createQuery(
583
"SELECT u FROM User u WHERE u.username = :username",
584
User.class
585
)
586
.setParameter("username", username)
587
.getSingleResult();
588
} catch (NoResultException ex) {
589
return null;
590
}
591
}
592
}
593
594
// Custom repository annotation
595
@Target(ElementType.TYPE)
596
@Retention(RetentionPolicy.RUNTIME)
597
@Documented
598
@Component
599
public @interface MyRepository {
600
String value() default "";
601
}
602
603
// Service class with AOP-based exception translation
604
@Service
605
public class UserService {
606
607
@Autowired
608
private UserRepository userRepository;
609
610
public User createUser(String username, String email) {
611
// Method execution wrapped by exception translation interceptor
612
User user = new User();
613
user.setUsername(username);
614
user.setEmail(email);
615
616
// Any persistence exceptions thrown here will be automatically translated
617
return userRepository.save(user);
618
}
619
}
620
```
621
622
### Custom Exception Handling Patterns
623
624
Advanced patterns for implementing custom exception handling and recovery strategies in ORM operations.
625
626
```java { .api }
627
// Custom exception types
628
public class EntityValidationException extends DataIntegrityViolationException {
629
private final Set<String> validationErrors;
630
631
public EntityValidationException(String message, Set<String> validationErrors) {
632
super(message);
633
this.validationErrors = validationErrors;
634
}
635
636
public Set<String> getValidationErrors() {
637
return validationErrors;
638
}
639
}
640
641
public class EntityConcurrentModificationException extends OptimisticLockingFailureException {
642
private final Object currentVersion;
643
private final Object attemptedVersion;
644
645
public EntityConcurrentModificationException(String message, Object currentVersion, Object attemptedVersion) {
646
super(message);
647
this.currentVersion = currentVersion;
648
this.attemptedVersion = attemptedVersion;
649
}
650
651
public Object getCurrentVersion() { return currentVersion; }
652
public Object getAttemptedVersion() { return attemptedVersion; }
653
}
654
```
655
656
#### Usage Example
657
658
```java
659
@Service
660
@Transactional
661
public class OrderProcessingService {
662
663
@PersistenceContext
664
private EntityManager entityManager;
665
666
public Order processOrder(OrderRequest request) {
667
try {
668
return doProcessOrder(request);
669
} catch (ObjectOptimisticLockingFailureException ex) {
670
// Retry logic for optimistic locking failures
671
return handleOptimisticLockingFailure(request, ex);
672
} catch (DataIntegrityViolationException ex) {
673
// Handle constraint violations
674
return handleDataIntegrityViolation(request, ex);
675
} catch (DataAccessException ex) {
676
// Generic data access error handling
677
return handleDataAccessError(request, ex);
678
}
679
}
680
681
private Order handleOptimisticLockingFailure(OrderRequest request, ObjectOptimisticLockingFailureException ex) {
682
log.warn("Optimistic locking failure for entity: {} with id: {}",
683
ex.getPersistentClassName(), ex.getIdentifier());
684
685
// Implement retry with exponential backoff
686
for (int attempt = 1; attempt <= 3; attempt++) {
687
try {
688
Thread.sleep(100 * attempt); // Simple backoff
689
entityManager.clear(); // Clear stale entities
690
return doProcessOrder(request);
691
} catch (ObjectOptimisticLockingFailureException retryEx) {
692
if (attempt == 3) {
693
throw new EntityConcurrentModificationException(
694
"Order processing failed after multiple attempts due to concurrent modifications",
695
retryEx.getIdentifier(),
696
request.getVersion()
697
);
698
}
699
} catch (InterruptedException ie) {
700
Thread.currentThread().interrupt();
701
throw new RuntimeException("Interrupted during retry", ie);
702
}
703
}
704
705
return null; // Should never reach here
706
}
707
708
private Order handleDataIntegrityViolation(OrderRequest request, DataIntegrityViolationException ex) {
709
// Analyze constraint violation and provide meaningful error
710
String message = ex.getMessage().toLowerCase();
711
Set<String> validationErrors = new HashSet<>();
712
713
if (message.contains("unique constraint") && message.contains("order_number")) {
714
validationErrors.add("Order number already exists");
715
}
716
if (message.contains("foreign key") && message.contains("customer_id")) {
717
validationErrors.add("Invalid customer reference");
718
}
719
if (message.contains("check constraint")) {
720
validationErrors.add("Order data violates business rules");
721
}
722
723
throw new EntityValidationException(
724
"Order validation failed: " + String.join(", ", validationErrors),
725
validationErrors
726
);
727
}
728
729
private Order handleDataAccessError(OrderRequest request, DataAccessException ex) {
730
log.error("Data access error during order processing", ex);
731
732
// Implement circuit breaker pattern
733
if (isCircuitBreakerOpen()) {
734
throw new ServiceUnavailableException("Order processing temporarily unavailable");
735
}
736
737
// Record failure for circuit breaker
738
recordFailure();
739
740
throw new OrderProcessingException("Unable to process order at this time", ex);
741
}
742
}
743
744
// Global exception handler with detailed error responses
745
@ControllerAdvice
746
public class GlobalExceptionHandler {
747
748
@ExceptionHandler(EntityValidationException.class)
749
public ResponseEntity<ValidationErrorResponse> handleValidationException(EntityValidationException ex) {
750
ValidationErrorResponse response = new ValidationErrorResponse(
751
"VALIDATION_FAILED",
752
"Entity validation failed",
753
ex.getValidationErrors().stream()
754
.map(error -> new ValidationError("entity", error))
755
.collect(Collectors.toList())
756
);
757
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(response);
758
}
759
760
@ExceptionHandler(EntityConcurrentModificationException.class)
761
public ResponseEntity<ErrorResponse> handleConcurrentModification(EntityConcurrentModificationException ex) {
762
ErrorResponse response = new ErrorResponse(
763
"CONCURRENT_MODIFICATION",
764
"Entity was modified by another user",
765
Map.of(
766
"currentVersion", ex.getCurrentVersion(),
767
"attemptedVersion", ex.getAttemptedVersion(),
768
"suggestedAction", "refresh_and_retry"
769
)
770
);
771
return ResponseEntity.status(HttpStatus.CONFLICT).body(response);
772
}
773
774
@ExceptionHandler(DataAccessException.class)
775
public ResponseEntity<ErrorResponse> handleDataAccessException(DataAccessException ex) {
776
// Log the full exception for debugging
777
log.error("Data access exception occurred", ex);
778
779
// Return generic error to client
780
ErrorResponse response = new ErrorResponse(
781
"DATA_ACCESS_ERROR",
782
"A database error occurred while processing your request",
783
Map.of("timestamp", Instant.now().toString())
784
);
785
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
786
}
787
}
788
```
789
790
## Types
791
792
```java { .api }
793
import org.springframework.dao.DataAccessException;
794
import org.springframework.dao.DataRetrievalFailureException;
795
import org.springframework.dao.OptimisticLockingFailureException;
796
import org.springframework.dao.UncategorizedDataAccessException;
797
import org.springframework.dao.InvalidDataAccessResourceUsageException;
798
import org.springframework.dao.DataIntegrityViolationException;
799
import org.springframework.dao.EmptyResultDataAccessException;
800
import org.springframework.dao.IncorrectResultSizeDataAccessException;
801
import org.springframework.dao.DuplicateKeyException;
802
import org.springframework.dao.QueryTimeoutException;
803
import org.springframework.dao.annotation.Repository;
804
import org.springframework.beans.factory.config.BeanPostProcessor;
805
import org.springframework.core.PriorityOrdered;
806
import org.springframework.aop.MethodInterceptor;
807
import org.springframework.aop.MethodInvocation;
808
import jakarta.persistence.PersistenceException;
809
import jakarta.persistence.EntityNotFoundException;
810
import jakarta.persistence.OptimisticLockException;
811
import jakarta.persistence.NoResultException;
812
import jakarta.persistence.NonUniqueResultException;
813
import jakarta.persistence.EntityExistsException;
814
import jakarta.persistence.QueryTimeoutException as JpaQueryTimeoutException;
815
import org.hibernate.HibernateException;
816
import org.hibernate.JDBCException;
817
import org.hibernate.ObjectNotFoundException;
818
import org.hibernate.QueryException;
819
import org.hibernate.StaleObjectStateException;
820
import org.hibernate.StaleStateException;
821
import org.hibernate.UnresolvableObjectException;
822
import java.lang.annotation.Annotation;
823
import java.util.Set;
824
import java.util.Map;
825
```