如何在Oracle UCP连接池环境下使用SQLExceptionTranslator?
解决Oracle UCP与Spring JdbcTemplate SQLExceptionTranslator兼容问题
问题根源
Oracle UCP抛出的oracle.ucp.UniversalConnectionPoolException继承自java.lang.Exception而非java.sql.SQLException,而Spring的SQLExceptionTranslator仅负责处理SQLException类型的异常。当UCP直接抛出连接池相关异常时,若强行将其传入SQLExceptionTranslator的方法中,就会触发ClassCastException。
解决方案
方案1:自定义SQLExceptionTranslator处理异常链中的UCP异常
针对SQL执行过程中被SQLException包装的UCP异常,我们可以扩展翻译器,先遍历异常链提取UCP异常,转换为SQLException后再交给默认逻辑处理:
import oracle.ucp.UniversalConnectionPoolException; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.jdbc.support.SqlErrorCodesSQLExceptionTranslator; public class UcpAwareSQLExceptionTranslator implements SQLExceptionTranslator { // 复用Spring默认的SQL错误码翻译器 private final SQLExceptionTranslator defaultTranslator = new SqlErrorCodesSQLExceptionTranslator(); @Override public DataAccessException translate(String task, String sql, SQLException ex) { // 遍历异常链,查找UCP异常 Throwable current = ex; while (current != null) { if (current instanceof UniversalConnectionPoolException) { UniversalConnectionPoolException ucpEx = (UniversalConnectionPoolException) current; // 构造包含UCP异常信息的SQLException SQLException translatedSqlEx = new SQLException( String.format("UCP连接池错误: %s", ucpEx.getMessage()), ucpEx ); // 交给默认翻译器生成对应的DataAccessException return defaultTranslator.translate(task, sql, translatedSqlEx); } current = current.getCause(); } // 无UCP异常,直接用默认逻辑处理 return defaultTranslator.translate(task, sql, ex); } }
然后将该翻译器设置给JdbcTemplate:
JdbcTemplate jdbcTemplate = new JdbcTemplate(ucpDataSource); jdbcTemplate.setExceptionTranslator(new UcpAwareSQLExceptionTranslator());
方案2:扩展JdbcTemplate捕获直接抛出的UCP异常
对于UCP在获取连接阶段直接抛出的UniversalConnectionPoolException(未被SQLException包装),需要扩展JdbcTemplate的执行逻辑,提前捕获并转换为Spring标准的DataAccessException:
import oracle.ucp.UniversalConnectionPoolException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.core.StatementCallback; public class UcpJdbcTemplate extends JdbcTemplate { public UcpJdbcTemplate(DataSource dataSource) { super(dataSource); // 绑定自定义的SQL异常翻译器 setExceptionTranslator(new UcpAwareSQLExceptionTranslator()); } @Override protected <T> T execute(StatementCallback<T> action) throws DataAccessException { try { return super.execute(action); } catch (DataAccessException dae) { // 检查DataAccessException的根源是否为UCP异常 Throwable cause = dae.getCause(); if (cause instanceof UniversalConnectionPoolException) { throw new CannotGetJdbcConnectionException("无法从UCP连接池获取连接", cause); } throw dae; } catch (UniversalConnectionPoolException ucpEx) { // 直接捕获UCP异常,转换为Spring连接异常 throw new CannotGetJdbcConnectionException("UCP连接池操作失败", ucpEx); } } }
之后直接使用这个自定义的UcpJdbcTemplate即可,它会同时处理连接池异常和SQL执行异常。
方案3:全局异常处理(补充)
如果希望在应用层面统一处理UCP异常,可以添加全局异常处理器,将UniversalConnectionPoolException转换为Spring的DataAccessException:
import oracle.ucp.UniversalConnectionPoolException; import org.springframework.jdbc.CannotGetJdbcConnectionException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.dao.DataAccessException; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(UniversalConnectionPoolException.class) public DataAccessException handleUcpException(UniversalConnectionPoolException ex) { return new CannotGetJdbcConnectionException("UCP连接池异常", ex); } }
内容的提问来源于stack exchange,提问作者koli
相关产品推荐
相关产品推荐

