Spring MVC容器管理事务下DAO/流程层异常处理及返回Response Code1问询
Hey there, let's walk through how to set up the exception handling exactly for your Spring MVC stack, matching your layer structure and requirement to return Response Code 1 on technical failures. Let's break this down step by step:
We need a clear way to mark technical failures (like DB connection issues, JPA persistence errors, IO glitches) separate from business exceptions. Create a TechnicalException that extends RuntimeException—this plays nice with Spring's container-managed transactions, since they auto-rollback on runtime exceptions by default:
public class TechnicalException extends RuntimeException { public TechnicalException(String message) { super(message); } public TechnicalException(String message, Throwable cause) { super(message, cause); } }
Since you're using JPA, Spring automatically converts JPA's PersistenceException into subclasses of DataAccessException (think JpaSystemException or DataIntegrityViolationException). Instead of catching exceptions directly in the DAO layer (which could break transaction rollbacks), let them bubble up to the Service layer, where we'll wrap them into our custom TechnicalException:
@Service public class YourBusinessService { private final YourDAO yourDAO; private final Logger log = LoggerFactory.getLogger(YourBusinessService.class); // Constructor injection (preferred over @Autowired) public YourBusinessService(YourDAO yourDAO) { this.yourDAO = yourDAO; } public void executeDBOperation() { try { yourDAO.performPersistOrFetch(); } catch (DataAccessException e) { log.error("Failed to execute database operation", e); throw new TechnicalException("Database operation failed", e); } } }
If your DAO has custom technical issues (like a custom data source error), you can directly throw TechnicalException from the DAO itself.
The Process layer is your workflow orchestration hub—here you'll catch technical exceptions from the Service layer, handle any layer-specific technical issues (like external service timeouts, IO errors), and ensure all technical failures get wrapped into TechnicalException before bubbling up:
@Component public class YourProcessHandler { private final YourBusinessService businessService; private final Logger log = LoggerFactory.getLogger(YourProcessHandler.class); public YourProcessHandler(YourBusinessService businessService) { this.businessService = businessService; } public void runWorkflow() { try { businessService.executeDBOperation(); // Run your process-specific logic here performProcessSteps(); } catch (TechnicalException e) { log.error("Workflow hit a technical failure", e); throw e; // Pass up to Delegate/Controller for global handling } catch (IOException e) { log.error("IO error during workflow execution", e); throw new TechnicalException("Workflow IO operation failed", e); } } private void performProcessSteps() throws IOException { // Your process logic that might throw IO or other technical errors } }
Don't swallow exceptions here—pass them up so the global handler can format the correct response for the UI.
Use Spring's @RestControllerAdvice to create a global exception handler that catches TechnicalException and returns the required Response Code 1 in the JSON response. This keeps your controllers clean and ensures consistent error formatting:
@RestControllerAdvice public class GlobalExceptionHandler { private final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class); @ExceptionHandler(TechnicalException.class) public ResponseEntity<Map<String, Object>> handleTechnicalFailure(TechnicalException e) { log.error("Handling technical failure for UI response", e); Map<String, Object> errorResponse = new HashMap<>(); errorResponse.put("responseCode", 1); errorResponse.put("message", "Technical failure occurred, please try again later"); // If you need to return a 200 HTTP status (instead of 500) for UI compatibility, swap to HttpStatus.OK return new ResponseEntity<>(errorResponse, HttpStatus.INTERNAL_SERVER_ERROR); } }
Adjust the HTTP status code based on your UI's requirements—some teams prefer returning 200 with a response code in the body, others use 500 for server errors.
Since you're using container-managed transactions (via @Transactional), remember:
- Our
TechnicalExceptionis a runtime exception, so Spring will auto-rollback transactions when it's thrown—perfect for data consistency. - If you ever use checked exceptions, you'll need to add
rollbackFor = YourCheckedException.classto the@Transactionalannotation to trigger rollbacks.
- Log Everything: Always log full exception stacks in the layers where you catch exceptions—this is critical for debugging production issues.
- Separate Business vs Technical Exceptions: If you have business-specific errors (like "user not found"), create a separate
BusinessExceptionso you can handle those differently (return a different response code, for example). - Keep Controllers Lean: Don't handle exceptions in controllers—let the global advice take care of it, so controllers focus on request/response mapping.
内容的提问来源于stack exchange,提问作者Gunjan Shah

