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}