spring boot 全局异常处理可以通过配置 @controlleradvice 结合自定义的 exceptionhandler 来捕获存储过程异常。具体做法是:在存储过程调用层(如使用 jdbctemplate 或 mybatis)将数据库抛出的 sqlexception 或特定 sqlstate 转换为业务异常,然后在 @controlleradvice 中统一拦截这些异常并返回标准化的 json 响应。

如何识别存储过程执行失败的具体原因
要区分存储过程内部逻辑错误与外部调用参数错误,关键在于检查 SQLException 的 getSQLState() 和 getErrorCode()。不同数据库(如 MySQL、Oracle)对存储过程错误的编码定义不同,需根据实际使用的数据库类型进行映射。

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
@Service
public class ProcedureService {
private final JdbcTemplate jdbcTemplate;
public ProcedureService(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public void callProcedure(int id, String name) {
// 假设存储过程名为 proc_update_user
String sql = "{call proc_update_user(?, ?)}";
try {
jdbcTemplate.execute(sql, cs -> {
cs.setInt(1, id);
cs.setString(2, name);
return null;
});
} catch (Exception e) {
// 记录日志并向上抛出,由全局异常处理器捕获
throw new RuntimeException("存储过程执行失败", e);
}
}
}
当传入非法参数时,上述代码会抛出 RuntimeException,其 Cause 为 SQLException。通过检查 SQLException 的 getErrorCode(),可以判断是主键冲突、非空约束违反还是其他业务逻辑错误。
如何构建统一的错误响应结构
为了前端能够方便地解析错误信息,需要定义一个标准的错误响应对象,并在 @ControllerAdvice 中将其序列化为 JSON。这样可以确保所有异常都返回相同的结构,便于前端统一处理。

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleRuntimeException(RuntimeException ex) {
// 提取原始异常信息
Throwable cause = ex.getCause();
String errorCode = "UNKNOWN";
if (cause instanceof java.sql.SQLException) {
java.sql.SQLException sqlEx = (java.sql.SQLException) cause;
errorCode = sqlEx.getSQLState() != null ? sqlEx.getSQLState() : String.valueOf(sqlEx.getErrorCode());
}
return new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"存储过程调用失败",
errorCode,
ex.getMessage()
);
}
}
class ErrorResponse {
private int status;
private String message;
private String errorCode;
private String details;
public ErrorResponse(int status, String message, String errorCode, String details) {
this.status = status;
this.message = message;
this.errorCode = errorCode;
this.details = details;
}
// getters and setters omitted for brevity
}
该示例展示了如何将 SQLException 中的 SQLState 或 ErrorCode 提取出来,放入 ErrorResponse 对象中。这样前端可以根据 errorCode 进行更精细的错误提示,例如显示“数据已存在”或“必填字段缺失”。
当存储过程返回特定错误码时如何处理
如果存储过程内部使用了 SIGNAL SQLSTATE 或类似机制返回自定义错误码,全局异常处理器可以针对这些特定错误码进行特殊处理。例如,对于错误码 '45000'(用户定义异常),可以返回特定的业务状态码。
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class CustomProcedureExceptionHandler {
@ExceptionHandler(java.sql.SQLException.class)
public ErrorResponse handleSqlException(java.sql.SQLException ex) {
String sqlState = ex.getSQLState();
if ("45000".equals(sqlState)) {
// 用户自定义异常,返回 BAD_REQUEST
return new ErrorResponse(
HttpStatus.BAD_REQUEST.value(),
"业务规则校验失败",
sqlState,
ex.getMessage()
);
}
// 其他 SQL 异常,返回 INTERNAL_SERVER_ERROR
return new ErrorResponse(
HttpStatus.INTERNAL_SERVER_ERROR.value(),
"数据库操作失败",
sqlState,
ex.getMessage()
);
}
}
此段代码展示了如何针对特定的 SQLState 进行分支处理。在实际项目中,建议将所有常见的存储过程错误码映射到具体的业务含义,以便前端展示友好的提示信息。











