001/* 002 * Copyright 2002-2018 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jdbc.support; 018 019import java.sql.SQLException; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023 024import org.springframework.dao.DataAccessException; 025import org.springframework.jdbc.UncategorizedSQLException; 026import org.springframework.lang.NonNull; 027import org.springframework.lang.Nullable; 028import org.springframework.util.Assert; 029 030/** 031 * Base class for {@link SQLExceptionTranslator} implementations that allow for 032 * fallback to some other {@link SQLExceptionTranslator}. 033 * 034 * @author Juergen Hoeller 035 * @since 2.5.6 036 */ 037public abstract class AbstractFallbackSQLExceptionTranslator implements SQLExceptionTranslator { 038 039 /** Logger available to subclasses. */ 040 protected final Log logger = LogFactory.getLog(getClass()); 041 042 @Nullable 043 private SQLExceptionTranslator fallbackTranslator; 044 045 046 /** 047 * Override the default SQL state fallback translator 048 * (typically a {@link SQLStateSQLExceptionTranslator}). 049 */ 050 public void setFallbackTranslator(@Nullable SQLExceptionTranslator fallback) { 051 this.fallbackTranslator = fallback; 052 } 053 054 /** 055 * Return the fallback exception translator, if any. 056 */ 057 @Nullable 058 public SQLExceptionTranslator getFallbackTranslator() { 059 return this.fallbackTranslator; 060 } 061 062 063 /** 064 * Pre-checks the arguments, calls {@link #doTranslate}, and invokes the 065 * {@link #getFallbackTranslator() fallback translator} if necessary. 066 */ 067 @Override 068 @NonNull 069 public DataAccessException translate(String task, @Nullable String sql, SQLException ex) { 070 Assert.notNull(ex, "Cannot translate a null SQLException"); 071 072 DataAccessException dae = doTranslate(task, sql, ex); 073 if (dae != null) { 074 // Specific exception match found. 075 return dae; 076 } 077 078 // Looking for a fallback... 079 SQLExceptionTranslator fallback = getFallbackTranslator(); 080 if (fallback != null) { 081 dae = fallback.translate(task, sql, ex); 082 if (dae != null) { 083 // Fallback exception match found. 084 return dae; 085 } 086 } 087 088 // We couldn't identify it more precisely. 089 return new UncategorizedSQLException(task, sql, ex); 090 } 091 092 /** 093 * Template method for actually translating the given exception. 094 * <p>The passed-in arguments will have been pre-checked. Furthermore, this method 095 * is allowed to return {@code null} to indicate that no exception match has 096 * been found and that fallback translation should kick in. 097 * @param task readable text describing the task being attempted 098 * @param sql the SQL query or update that caused the problem (if known) 099 * @param ex the offending {@code SQLException} 100 * @return the DataAccessException, wrapping the {@code SQLException}; 101 * or {@code null} if no exception match found 102 */ 103 @Nullable 104 protected abstract DataAccessException doTranslate(String task, @Nullable String sql, SQLException ex); 105 106 107 /** 108 * Build a message {@code String} for the given {@link java.sql.SQLException}. 109 * <p>To be called by translator subclasses when creating an instance of a generic 110 * {@link org.springframework.dao.DataAccessException} class. 111 * @param task readable text describing the task being attempted 112 * @param sql the SQL statement that caused the problem 113 * @param ex the offending {@code SQLException} 114 * @return the message {@code String} to use 115 */ 116 protected String buildMessage(String task, @Nullable String sql, SQLException ex) { 117 return task + "; " + (sql != null ? ("SQL [" + sql + "]; ") : "") + ex.getMessage(); 118 } 119 120}