Exception handling in java


!! While adding data in database, types of exception can occurs !!

@PostMapping("/add")
public Employee addEmployee(@RequestBody Employee employee) {
return employeeService.saveEmployee(employee);
}


When adding data to a database in a Spring Boot application, several types of exceptions can be thrown. These exceptions may arise from various layers, including Spring Data, JPA, or the underlying JDBC driver. Below is a categorization of potential exceptions:

1. Data Access Exceptions (Spring Framework)

Spring wraps exceptions from the persistence framework or database in subclasses of DataAccessException. Common exceptions include:

  • DataIntegrityViolationException

    • Occurs when constraints (e.g., unique, foreign key, not null) are violated.
  • DuplicateKeyException

    • Thrown for unique constraint violations or duplicate entries.
  • EmptyResultDataAccessException

    • Happens if a query expecting a result (e.g., findById) returns no data.
  • IncorrectResultSizeDataAccessException

    • Thrown when a query returns an unexpected number of rows.
  • DataRetrievalFailureException

    • Issues with fetching data (e.g., missing row in a join).
  • QueryTimeoutException

    • Database operation times out.

2. JPA Exceptions (Hibernate or Other Providers)

If you are using JPA with Hibernate or another provider, the following exceptions may occur:

  • EntityExistsException

    • Trying to persist an entity with the same primary key as an existing entity.
  • EntityNotFoundException

    • When an entity reference is resolved but no actual entity exists in the database.
  • TransactionRequiredException

    • Attempting an operation outside a transaction context when one is required.
  • OptimisticLockException

    • Version conflict during an update with optimistic locking.
  • PersistenceException

    • General JPA persistence errors.
  • ConstraintViolationException (Hibernate-specific)

    • Violation of database constraints.

3. JDBC Exceptions

Underlying JDBC exceptions may bubble up if not handled by Spring or JPA:

  • SQLSyntaxErrorException

    • Issues with SQL syntax, such as malformed queries.
  • SQLIntegrityConstraintViolationException

    • Constraint violations at the database level.
  • SQLTimeoutException

    • Timeout during a database operation.
  • SQLException

    • General SQL-related issues.

4. Custom Application Exceptions

You may define your own custom exceptions for business logic errors, such as:

  • InvalidDataException

    • Thrown when input validation fails before reaching the database.
  • EntityAlreadyExistsException

    • Indicates that the entity already exists in your application's domain logic.
  • DatabaseConnectionException

    • Custom handling for connection issues.

5. Transactional Exceptions

When dealing with transactions, exceptions can occur, such as:

  • RollbackException

    • Thrown if a transaction fails or is explicitly rolled back.
  • HeuristicRollbackException

    • Partial rollback in a distributed transaction.

6. Others

  • IllegalArgumentException

    • Incorrect arguments passed to repository methods.
  • ConversionFailedException

    • Issues with type conversion during query operations.

Best Practices for Handling Exceptions

  1. Global Exception Handling: Use @ControllerAdvice and @ExceptionHandler to manage exceptions globally.
  2. Transaction Management: Ensure proper transaction boundaries using @Transactional.
  3. Validation: Validate data using @Valid or @Validated to avoid sending invalid data to the database.
  4. Logging: Log exceptions for debugging and monitoring.
  5. Database-Specific Codes: Be mindful of database-specific exceptions or error codes (e.g., SQLState).

ScenarioCauseRecommended Handling
๐Ÿ›‘ Validation FailureMissing or invalid fields in requestReturn 400 Bad Request
๐Ÿ” Constraint ViolationDuplicate key, foreign key errorReturn 409 Conflict or 400 Bad Request
๐Ÿ”Œ Database Connectivity IssuesDB is down, timeout, etc.Return 503 Service Unavailable
๐Ÿงจ Unexpected ExceptionNull pointer, mapping error, etc.Return 500 Internal Server Error
Business Rule ViolationE.g., “cannot pay more than balance”Return 400 Bad Request with custom error message


How to Handle These Gracefully

Step 1: ❗ Validate Input with @Valid

Update the controller to validate incoming requests:

java
@PostMapping("/payments") public ResponseEntity<PaymentResponse> savePayments(@Valid @RequestBody PaymentRequest paymentRequest) { PaymentResponse paymentResponse = paymentService.savePayments(paymentRequest); return new ResponseEntity<>(paymentResponse, HttpStatus.CREATED); }

Use annotations in PaymentRequest:

java
public class PaymentRequest { @NotNull(message = "Amount is required") private BigDecimal amount; @NotNull(message = "User ID is required") private Long userId; // Getters and setters }

Step 2: ✅ Clean Up Service Layer

In paymentService.savePayments(), throw meaningful exceptions:

java
public PaymentResponse savePayments(PaymentRequest request) { if (request.getAmount().compareTo(BigDecimal.ZERO) <= 0) { throw new InvalidPaymentException("Amount must be greater than zero"); } try { Payment payment = paymentRepository.save(mapToPayment(request)); return new PaymentResponse(payment); } catch (DataIntegrityViolationException ex) { throw new DuplicatePaymentException("Payment already exists or violates constraints", ex); } catch (Exception ex) { throw new ApplicationException("Error while saving payment", ex); } }

Step 3: ๐Ÿงพ Global Exception Handler

Handle these exceptions centrally:

java
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(InvalidPaymentException.class) public ResponseEntity<ApiError> handleInvalidPayment(InvalidPaymentException ex) { return new ResponseEntity<>(new ApiError(HttpStatus.BAD_REQUEST, ex.getMessage()), HttpStatus.BAD_REQUEST); } @ExceptionHandler(DuplicatePaymentException.class) public ResponseEntity<ApiError> handleDuplicate(DuplicatePaymentException ex) { return new ResponseEntity<>(new ApiError(HttpStatus.CONFLICT, ex.getMessage()), HttpStatus.CONFLICT); } @ExceptionHandler(DataAccessException.class) public ResponseEntity<ApiError> handleDatabaseError(DataAccessException ex) { return new ResponseEntity<>(new ApiError(HttpStatus.SERVICE_UNAVAILABLE, "Database error"), HttpStatus.SERVICE_UNAVAILABLE); } @ExceptionHandler(Exception.class) public ResponseEntity<ApiError> handleGenericError(Exception ex) { return new ResponseEntity<>(new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, "Unexpected error"), HttpStatus.INTERNAL_SERVER_ERROR); } }

๐Ÿงพ ApiError Class

java
public class ApiError { private HttpStatus status; private String message; public ApiError(HttpStatus status, String message) { this.status = status; this.message = message; } // Getters and setters }

✅ Final Advice

  • Avoid checking for null manually in the controller. Let your service and exception handler handle abnormal conditions.

  • Use HTTP status codes properly: 400 for bad input, 409 for conflict, 500+ for server issues.

  • Never return raw null in response. Always return an informative error body.


Comments

Popular posts from this blog

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration': Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource' defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception; nested exception is java.lang.IllegalStateException: Cannot load driver class: com.mysql.jdbc.Driver