SQLExceptionTranslator – это интерфейс, который должен быть реализован классами, способными выполнять преобразование между SQLExceptions и собственными org.springframework.dao.DataAccessException, которые не зависят от стратегии доступа к данным. Реализации могут быть типизированными (например, использование кодов SQLState для JDBC) или проприетарными (например, использование кодов ошибок Oracle) для большей точности.

SQLErrorCodeSQLExceptionTranslator – это реализация SQLExceptionTranslator, которая используется по умолчанию. В данной реализации используются определенные коды производителей. Она является более точной, чем реализация SQLState. Преобразование кодов ошибок основано на кодах, хранящихся в классе типа JavaBean под названием SQLErrorCodes. Этот класс создается и заполняется SQLErrorCodesFactory, которая (как следует из названия) является фабрикой для создания SQLErrorCodes на основе содержимого конфигурационного файла sql-error-codes.xml. Этот файл заполняется кодами производителей и основывается на DatabaseProductName, взятом из DatabaseMetaData. Используются коды для фактической базы данных, которую вы используете.

SQLErrorCodeSQLExceptionTranslator применяет правила согласования в следующей последовательности:

  1. Любое кастомное преобразование, реализованное подклассом. Обычно используется конкретный SQLErrorCodeSQLExceptionTranslator, поэтому это правило не применяется. Оно применимо только в том случае, если вы действительно предоставили реализацию подкласса.

  2. Любая кастомная реализация интерфейса SQLExceptionTranslator, которая предоставляется как свойство customSqlExceptionTranslator класса SQLErrorCodes.

  3. В списке экземпляров класса CustomSQLErrorCodesTranslation (предоставленных для свойства customTranslations класса SQLErrorCodes) выполняется поиск совпадения.

  4. Применяется сопоставление кодов ошибок.

  5. Используется возвратный преобразователь (translator). SQLExceptionSubclassTranslator является преобразователем по умолчанию. Если этот перевод недоступен, следующим возвратным преобразователем является SQLStateSQLExceptionTranslator.

SQLErrorCodesFactory используется по умолчанию для определения кодов Error и кастомных преобразований исключений. Они ищутся в файле sql-error-codes.xml из classpath, а соответствующий экземпляр SQLErrorCodes находится на основе имени базы данных из метаданных используемой базы данных.

Можно расширить SQLErrorCodeSQLExceptionTranslator, как показано в следующем примере:

Java
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;
    }
}
Kotlin
class CustomSQLErrorCodesTranslator : SQLErrorCodeSQLExceptionTranslator() {
    override fun customTranslate(task: String, sql: String?, sqlEx: SQLException): DataAccessException? {
        if (sqlEx.errorCode == -12345) {
            return DeadlockLoserDataAccessException(task, sqlEx)
        }
        return null
    }
}

В предыдущем примере преобразовывается конкретный код ошибки (-12345), а остальные ошибки остаются на усмотрение стандартной реализации преобразователя. Чтобы использовать этот кастомный преобразователь, нужно передать его в JdbcTemplate через метод setExceptionTranslator, а затем использовать этот JdbcTemplate для любой обработки доступа к данным, где этот преобразователь необходим. В следующем примере показано, как можно использовать этот кастомный преобразователь:

Java
private JdbcTemplate jdbcTemplate;
public void setDataSource(DataSource dataSource) {
    // создаем шаблон JdbcTemplate и устанавливаем источник данных
    this.jdbcTemplate = new JdbcTemplate();
    this.jdbcTemplate.setDataSource(dataSource);
    // создаем кастомный преобразователь и устанавливаем источник данных для поиска преобразования по умолчанию
    CustomSQLErrorCodesTranslator tr = new CustomSQLErrorCodesTranslator();
    tr.setDataSource(dataSource);
    this.jdbcTemplate.setExceptionTranslator(tr);
}
public void updateShippingCharge(long orderId, long pct) {
    // используем подготовленный шаблон JdbcTemplate для этого обновления
    this.jdbcTemplate.update("update orders" +
        " set shipping_charge = shipping_charge * ? / 100" +
        " where id = ?", pct, orderId);
}
Kotlin
// создаем шаблон JdbcTemplate и устанавливаем источник данных
private val jdbcTemplate = JdbcTemplate(dataSource).apply {
    // создаем кастомный преобразователь и устанавливаем источник данных для поиска преобразования по умолчанию
    exceptionTranslator = CustomSQLErrorCodesTranslator().apply {
        this.dataSource = dataSource
    }
}
fun updateShippingCharge(orderId: Long, pct: Long) {
    // используем подготовленный шаблон JdbcTemplate для этого обновления
    this.jdbcTemplate!!.update("update orders" +
            " set shipping_charge = shipping_charge * ? / 100" +
            " where id = ?", pct, orderId)
}

Кастомному преобразователю передается источник данных для поиска кодов ошибок в sql-error-codes.xml.