Using SQLExceptionTranslator
SQLExceptionTranslator 是一个由类实现的接口,可以在 SQLExceptions 和 Spring 自己的 org.springframework.dao.DataAccessException之间进行转换,它与数据访问策略无关。实现可以是通用的(例如,为 JDBC 使用 SQLState 代码)或专有的(例如,使用 Oracle 错误代码),以获得更高的精度。
SQLErrorCodeSQLExceptionTranslator 是 SQLExceptionTranslator 的实现,默认使用。这个实现使用特定的供应商代码。它比 SQLState 的实现更精确。错误代码的翻译是基于一个叫做 SQLErrorCodes 的 JavaBean 类型类中的代码。这个类是由 SQLErrorCodesFactory 创建和填充的,(顾名思义)它是一个基于名为 sql-error-codes.xml 的配置文件内容来创建 SQLErrorCodes 的工厂。这个文件是根据 DatabaseMetaData 中的 DatabaseProductName 来填充供应商代码的。你正在使用的实际数据库的代码被使用。
SQLErrorCodeSQLExceptionTranslator 按以下顺序应用匹配规则:
任何由子类实现的自定义翻译。通常,提供的具体的 SQLErrorCodeSQLExceptionTranslator 被使用,所以这个规则并不适用。只有当你实际提供了一个子类的实现时,它才适用。
SQLExceptionTranslator 接口的任何自定义实现,它被作为 SQLErrorCodes 类的 customSqlExceptionTranslator 属性提供。
CustomSQLErrorCodesTranslation 类的实例列表(为 SQLErrorCodes 类的 customTranslations 属性提供)被搜索匹配。
错误代码匹配被应用。
使用后备翻译器。SQLExceptionSubclassTranslator 是默认的后备翻译器。如果这个翻译不可用,下一个回退翻译器是 SQLStateSQLExceptionTranslator。
:::info SQLErrorCodesFactory 默认用于定义错误代码和自定义异常翻译。它们在 classpath 的一个名为 sql-error-codes.xml 的文件中被查找,并且根据使用中的数据库元数据中的数据库名称找到匹配的 SQLErrorCodes 实例。 :::
下面是 sql-error-codes.xml 中 mysql 错误代码的定义
<bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes"><property name="databaseProductNames"><list><value>MySQL</value><value>MariaDB</value></list></property><property name="badSqlGrammarCodes"><value>1054,1064,1146</value></property><property name="duplicateKeyCodes"><value>1062</value></property><property name="dataIntegrityViolationCodes"><value>630,839,840,893,1169,1215,1216,1217,1364,1451,1452,1557</value></property><property name="dataAccessResourceFailureCodes"><value>1</value></property><property name="cannotAcquireLockCodes"><value>1205,3572</value></property><property name="deadlockLoserCodes"><value>1213</value></property></bean>
看上去也就 6 种类型的异常。
你可以扩展 SQLErrorCodeSQLExceptionTranslator,如下例所示:
public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {if (sqlEx.getErrorCode() == -12345) {return new DeadlockLoserDataAccessException(task, sqlEx);}return null;}}
在前面的例子中,特定的错误代码(-12345)被翻译了,而其他错误则由默认翻译器实现来翻译。要使用这个自定义翻译器,你必须通过setExceptionTranslator方法将其传递给 JdbcTemplate,而且你必须在所有需要这个翻译器的数据访问处理中使用这个 JdbcTemplate。下面的例子展示了如何使用这个自定义翻译器:
private JdbcTemplate jdbcTemplate;public void setDataSource(DataSource dataSource) {// create a JdbcTemplate and set data sourcethis.jdbcTemplate = new JdbcTemplate();this.jdbcTemplate.setDataSource(dataSource);// 创建一个自定义的翻译器,并为默认的翻译查询设置数据源。CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();tr.setDataSource(dataSource); // 设置数据源的目的是为了识别数据库是什么类型,好从 sql-error-codes.xml 中查找错误代码。this.jdbcTemplate.setExceptionTranslator(tr);}public void updateShippingCharge(long orderId, long pct) {// use the prepared JdbcTemplate for this updatethis.jdbcTemplate.update("update orders" +" set shipping_charge = shipping_charge * ? / 100" +" where id = ?", pct, orderId);}
自定义翻译器被传递给一个数据源,以便在 sql-error-codes.xml 中查找错误代码。
