001/* 002 * Copyright 2012-2017 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 * http://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.boot.autoconfigure.jooq; 018 019import java.sql.SQLException; 020 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.jooq.ExecuteContext; 024import org.jooq.SQLDialect; 025import org.jooq.impl.DefaultExecuteListener; 026 027import org.springframework.dao.DataAccessException; 028import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator; 029import org.springframework.jdbc.support.SQLExceptionTranslator; 030import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; 031 032/** 033 * Transforms {@link java.sql.SQLException} into a Spring-specific 034 * {@link DataAccessException}. 035 * 036 * @author Lukas Eder 037 * @author Andreas Ahlenstorf 038 * @author Phillip Webb 039 * @author Stephane Nicoll 040 * @since 1.5.10 041 */ 042public class JooqExceptionTranslator extends DefaultExecuteListener { 043 044 // Based on the jOOQ-spring-example from https://github.com/jOOQ/jOOQ 045 046 private static final Log logger = LogFactory.getLog(JooqExceptionTranslator.class); 047 048 @Override 049 public void exception(ExecuteContext context) { 050 SQLExceptionTranslator translator = getTranslator(context); 051 // The exception() callback is not only triggered for SQL exceptions but also for 052 // "normal" exceptions. In those cases sqlException() returns null. 053 SQLException exception = context.sqlException(); 054 while (exception != null) { 055 handle(context, translator, exception); 056 exception = exception.getNextException(); 057 } 058 } 059 060 private SQLExceptionTranslator getTranslator(ExecuteContext context) { 061 SQLDialect dialect = context.configuration().dialect(); 062 if (dialect != null && dialect.thirdParty() != null) { 063 String dbName = dialect.thirdParty().springDbName(); 064 if (dbName != null) { 065 return new SQLErrorCodeSQLExceptionTranslator(dbName); 066 } 067 } 068 return new SQLStateSQLExceptionTranslator(); 069 } 070 071 /** 072 * Handle a single exception in the chain. SQLExceptions might be nested multiple 073 * levels deep. The outermost exception is usually the least interesting one ("Call 074 * getNextException to see the cause."). Therefore the innermost exception is 075 * propagated and all other exceptions are logged. 076 * @param context the execute context 077 * @param translator the exception translator 078 * @param exception the exception 079 */ 080 private void handle(ExecuteContext context, SQLExceptionTranslator translator, 081 SQLException exception) { 082 DataAccessException translated = translate(context, translator, exception); 083 if (exception.getNextException() == null) { 084 context.exception(translated); 085 } 086 else { 087 logger.error("Execution of SQL statement failed.", translated); 088 } 089 } 090 091 private DataAccessException translate(ExecuteContext context, 092 SQLExceptionTranslator translator, SQLException exception) { 093 return translator.translate("jOOQ", context.sql(), exception); 094 } 095 096}