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 按以下顺序应用匹配规则:

    1. 任何由子类实现的自定义翻译。通常,提供的具体的 SQLErrorCodeSQLExceptionTranslator 被使用,所以这个规则并不适用。只有当你实际提供了一个子类的实现时,它才适用。

    2. SQLExceptionTranslator 接口的任何自定义实现,它被作为 SQLErrorCodes 类的 customSqlExceptionTranslator 属性提供。

    3. CustomSQLErrorCodesTranslation 类的实例列表(为 SQLErrorCodes 类的 customTranslations 属性提供)被搜索匹配。

    4. 错误代码匹配被应用。

    5. 使用后备翻译器。SQLExceptionSubclassTranslator 是默认的后备翻译器。如果这个翻译不可用,下一个回退翻译器是 SQLStateSQLExceptionTranslator。

    :::info SQLErrorCodesFactory 默认用于定义错误代码和自定义异常翻译。它们在 classpath 的一个名为 sql-error-codes.xml 的文件中被查找,并且根据使用中的数据库元数据中的数据库名称找到匹配的 SQLErrorCodes 实例。 :::

    下面是 sql-error-codes.xml 中 mysql 错误代码的定义

    1. <bean id="MySQL" class="org.springframework.jdbc.support.SQLErrorCodes">
    2. <property name="databaseProductNames">
    3. <list>
    4. <value>MySQL</value>
    5. <value>MariaDB</value>
    6. </list>
    7. </property>
    8. <property name="badSqlGrammarCodes">
    9. <value>1054,1064,1146</value>
    10. </property>
    11. <property name="duplicateKeyCodes">
    12. <value>1062</value>
    13. </property>
    14. <property name="dataIntegrityViolationCodes">
    15. <value>630,839,840,893,1169,1215,1216,1217,1364,1451,1452,1557</value>
    16. </property>
    17. <property name="dataAccessResourceFailureCodes">
    18. <value>1</value>
    19. </property>
    20. <property name="cannotAcquireLockCodes">
    21. <value>1205,3572</value>
    22. </property>
    23. <property name="deadlockLoserCodes">
    24. <value>1213</value>
    25. </property>
    26. </bean>

    看上去也就 6 种类型的异常。

    你可以扩展 SQLErrorCodeSQLExceptionTranslator,如下例所示:

    1. public class CustomSQLErrorCodesTranslator extends SQLErrorCodeSQLExceptionTranslator {
    2. protected DataAccessException customTranslate(String task, String sql, SQLException sqlEx) {
    3. if (sqlEx.getErrorCode() == -12345) {
    4. return new DeadlockLoserDataAccessException(task, sqlEx);
    5. }
    6. return null;
    7. }
    8. }

    在前面的例子中,特定的错误代码(-12345)被翻译了,而其他错误则由默认翻译器实现来翻译。要使用这个自定义翻译器,你必须通过setExceptionTranslator方法将其传递给 JdbcTemplate,而且你必须在所有需要这个翻译器的数据访问处理中使用这个 JdbcTemplate。下面的例子展示了如何使用这个自定义翻译器:

    1. private JdbcTemplate jdbcTemplate;
    2. public void setDataSource(DataSource dataSource) {
    3. // create a JdbcTemplate and set data source
    4. this.jdbcTemplate = new JdbcTemplate();
    5. this.jdbcTemplate.setDataSource(dataSource);
    6. // 创建一个自定义的翻译器,并为默认的翻译查询设置数据源。
    7. CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
    8. tr.setDataSource(dataSource); // 设置数据源的目的是为了识别数据库是什么类型,好从 sql-error-codes.xml 中查找错误代码。
    9. this.jdbcTemplate.setExceptionTranslator(tr);
    10. }
    11. public void updateShippingCharge(long orderId, long pct) {
    12. // use the prepared JdbcTemplate for this update
    13. this.jdbcTemplate.update("update orders" +
    14. " set shipping_charge = shipping_charge * ? / 100" +
    15. " where id = ?", pct, orderId);
    16. }

    自定义翻译器被传递给一个数据源,以便在 sql-error-codes.xml 中查找错误代码。