Exception translation from JPA and Hibernate exceptions to Spring's DataAccessException hierarchy for consistent error handling across persistence technologies.
Spring automatically translates JPA/Hibernate exceptions to DataAccessException:
@Service
@Transactional
public class UserService {
@PersistenceContext
private EntityManager entityManager;
public User findById(Long id) {
try {
User user = entityManager.find(User.class, id);
if (user == null) {
throw new EntityNotFoundException("User not found: " + id);
}
return user;
} catch (EntityNotFoundException ex) {
// Automatically translated to JpaObjectRetrievalFailureException
throw ex;
}
}
public void updateUser(User user) {
try {
entityManager.merge(user);
} catch (OptimisticLockException ex) {
// Automatically translated to JpaOptimisticLockingFailureException
throw ex;
}
}
}// Entity not found
class JpaObjectRetrievalFailureException extends ObjectRetrievalFailureException {
JpaObjectRetrievalFailureException(EntityNotFoundException ex);
}
// Optimistic locking failure
class JpaOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
JpaOptimisticLockingFailureException(OptimisticLockException ex);
}
// Uncategorized errors
class JpaSystemException extends UncategorizedDataAccessException {
JpaSystemException(RuntimeException ex);
}// Optimistic locking violation
class ObjectOptimisticLockingFailureException extends OptimisticLockingFailureException {
ObjectOptimisticLockingFailureException(Class<?> persistentClass, Object identifier);
ObjectOptimisticLockingFailureException(String persistentClassName, Object identifier);
ObjectOptimisticLockingFailureException(
Class<?> persistentClass, Object identifier, String msg, Throwable cause);
Class<?> getPersistentClass();
String getPersistentClassName();
Object getIdentifier();
}
// Object not found
class ObjectRetrievalFailureException extends DataRetrievalFailureException {
ObjectRetrievalFailureException(Class<?> persistentClass, Object identifier);
ObjectRetrievalFailureException(String persistentClassName, Object identifier);
ObjectRetrievalFailureException(
Class<?> persistentClass, Object identifier, String msg, Throwable cause);
Class<?> getPersistentClass();
String getPersistentClassName();
Object getIdentifier();
}import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(JpaObjectRetrievalFailureException.class)
public ResponseEntity<ErrorResponse> handleObjectNotFound(
JpaObjectRetrievalFailureException ex) {
ErrorResponse error = new ErrorResponse();
error.setMessage("Entity not found: " +
ex.getPersistentClassName() + " with id " + ex.getIdentifier());
error.setStatus(HttpStatus.NOT_FOUND.value());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
@ExceptionHandler(JpaOptimisticLockingFailureException.class)
public ResponseEntity<ErrorResponse> handleOptimisticLocking(
JpaOptimisticLockingFailureException ex) {
ErrorResponse error = new ErrorResponse();
error.setMessage("The entity was modified by another transaction. " +
"Please reload and try again.");
error.setStatus(HttpStatus.CONFLICT.value());
return ResponseEntity.status(HttpStatus.CONFLICT).body(error);
}
@ExceptionHandler(JpaSystemException.class)
public ResponseEntity<ErrorResponse> handleJpaSystemException(
JpaSystemException ex) {
ErrorResponse error = new ErrorResponse();
error.setMessage("A system error occurred: " + ex.getMessage());
error.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<ErrorResponse> handleDataAccessException(
DataAccessException ex) {
ErrorResponse error = new ErrorResponse();
error.setMessage("Data access error: " + ex.getMessage());
error.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}@Service
@Transactional
public class OrderService {
@PersistenceContext
private EntityManager entityManager;
public Order createOrder(OrderRequest request) {
try {
// Verify product exists
Product product = entityManager.find(Product.class, request.getProductId());
if (product == null) {
throw new ObjectRetrievalFailureException(
Product.class, request.getProductId());
}
// Create order
Order order = new Order();
order.setProduct(product);
order.setQuantity(request.getQuantity());
entityManager.persist(order);
return order;
} catch (EntityNotFoundException ex) {
// Translated to JpaObjectRetrievalFailureException
throw ex;
} catch (PersistenceException ex) {
// Translated to JpaSystemException
throw ex;
}
}
public void updateOrder(Long orderId, int newQuantity) {
try {
Order order = entityManager.find(Order.class, orderId);
if (order == null) {
throw new ObjectRetrievalFailureException(Order.class, orderId);
}
order.setQuantity(newQuantity);
entityManager.merge(order);
} catch (OptimisticLockException ex) {
// Translated to JpaOptimisticLockingFailureException
throw new ObjectOptimisticLockingFailureException(
Order.class, orderId,
"Order was modified by another user", ex
);
}
}
}For Hibernate-specific exception translation:
import org.springframework.orm.hibernate5.HibernateExceptionTranslator;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
@Configuration
public class HibernateConfig {
@Bean
public HibernateExceptionTranslator hibernateExceptionTranslator(
DataSource dataSource) {
HibernateExceptionTranslator translator = new HibernateExceptionTranslator();
// Use custom JDBC exception translator
SQLExceptionTranslator jdbcTranslator =
new SQLErrorCodeSQLExceptionTranslator(dataSource);
translator.setJdbcExceptionTranslator(jdbcTranslator);
return translator;
}
}class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
// Set JDBC exception translator
void setJdbcExceptionTranslator(SQLExceptionTranslator translator);
// Translate exception
DataAccessException translateExceptionIfPossible(RuntimeException ex);
}
@FunctionalInterface
interface PersistenceExceptionTranslator {
// Returns null if exception should not be translated
DataAccessException translateExceptionIfPossible(RuntimeException ex);
}| Exception | Description |
|---|---|
DataIntegrityViolationException | Constraint violations (unique, foreign key, etc.) |
DataAccessResourceFailureException | Resource failures (connection lost, etc.) |
InvalidDataAccessApiUsageException | Invalid use of persistence API |
OptimisticLockingFailureException | Optimistic locking violations |
PessimisticLockingFailureException | Pessimistic locking failures |
DataRetrievalFailureException | Failed to retrieve data |
UncategorizedDataAccessException | Uncategorized or unknown errors |
These exceptions allow consistent error handling regardless of the underlying persistence technology (JPA, Hibernate, JDBC, etc.).