All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.hibernate.engine.jdbc.spi.SqlExceptionHelper Maven / Gradle / Ivy

There is a newer version: 7.0.0.Alpha1
Show newest version
/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or .
 */
package org.hibernate.engine.jdbc.spi;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.JDBCException;
import org.hibernate.exception.internal.SQLStateConverter;
import org.hibernate.exception.spi.SQLExceptionConverter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.StringHelper;

import org.jboss.logging.Logger;
import org.jboss.logging.Logger.Level;

/**
 * Helper for handling SQLExceptions in various manners.
 *
 * @author Steve Ebersole
 */
public class SqlExceptionHelper {
	private static final CoreMessageLogger LOG = Logger.getMessageLogger(
			CoreMessageLogger.class,
			SqlExceptionHelper.class.getName()
	);

	private static final String DEFAULT_EXCEPTION_MSG = "SQL Exception";
	private static final String DEFAULT_WARNING_MSG = "SQL Warning";
	private final boolean logWarnings;

	private static final SQLExceptionConverter DEFAULT_CONVERTER = new SQLStateConverter(
			new ViolatedConstraintNameExtracter() {
				public String extractConstraintName(SQLException e) {
					return null;
				}
			}
	);

	private SQLExceptionConverter sqlExceptionConverter;

	/**
	 * Create an exception helper with a default exception converter.
	 */
	public SqlExceptionHelper( boolean logWarnings) {
		this( DEFAULT_CONVERTER, logWarnings );
	}

	/**
	 * Create an exception helper with a specific exception converter.
	 *
	 * @param sqlExceptionConverter The exception converter to use.
	 */
	public SqlExceptionHelper(SQLExceptionConverter sqlExceptionConverter, boolean logWarnings) {
		this.sqlExceptionConverter = sqlExceptionConverter;
		this.logWarnings = logWarnings;
	}

	/**
	 * Access the current exception converter being used internally.
	 *
	 * @return The current exception converter.
	 */
	public SQLExceptionConverter getSqlExceptionConverter() {
		return sqlExceptionConverter;
	}

	/**
	 * Inject the exception converter to use.
	 * 

* NOTE : null is allowed and signifies to use the default. * * @param sqlExceptionConverter The converter to use. */ public void setSqlExceptionConverter(SQLExceptionConverter sqlExceptionConverter) { this.sqlExceptionConverter = ( sqlExceptionConverter == null ? DEFAULT_CONVERTER : sqlExceptionConverter ); } // SQLException ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Convert an SQLException using the current converter, doing some logging first. * * @param sqlException The exception to convert * @param message An error message. * * @return The converted exception */ public JDBCException convert(SQLException sqlException, String message) { return convert( sqlException, message, "n/a" ); } /** * Convert an SQLException using the current converter, doing some logging first. * * @param sqlException The exception to convert * @param message An error message. * @param sql The SQL being executed when the exception occurred * * @return The converted exception */ public JDBCException convert(SQLException sqlException, String message, String sql) { logExceptions( sqlException, message + " [" + sql + "]" ); return sqlExceptionConverter.convert( sqlException, message, sql ); } /** * Log the given (and any nested) exception. * * @param sqlException The exception to log * @param message The message text to use as a preamble. */ public void logExceptions(SQLException sqlException, String message) { if ( LOG.isEnabled( Level.ERROR ) ) { if ( LOG.isDebugEnabled() ) { message = StringHelper.isNotEmpty( message ) ? message : DEFAULT_EXCEPTION_MSG; LOG.debug( message, sqlException ); } final boolean warnEnabled = LOG.isEnabled( Level.WARN ); List previousWarnMessages = new ArrayList<>(); List previousErrorMessages = new ArrayList<>(); while ( sqlException != null ) { if ( warnEnabled ) { String warnMessage = "SQL Error: " + sqlException.getErrorCode() + ", SQLState: " + sqlException.getSQLState(); if ( !previousWarnMessages.contains( warnMessage ) ) { LOG.warn( warnMessage ); previousWarnMessages.add( warnMessage ); } } if ( !previousErrorMessages.contains( sqlException.getMessage() ) ) { LOG.error( sqlException.getMessage() ); previousErrorMessages.add( sqlException.getMessage() ); } sqlException = sqlException.getNextException(); } } } // SQLWarning ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /** * Contract for handling {@link SQLWarning warnings} */ public interface WarningHandler { /** * Should processing be done? Allows short-circuiting if not. * * @return True to process warnings, false otherwise. */ boolean doProcess(); /** * Prepare for processing of a {@link SQLWarning warning} stack. *

* Note that the warning here is also the first passed to {@link #handleWarning} * * @param warning The first warning in the stack. */ void prepare(SQLWarning warning); /** * Handle an individual warning in the stack. * * @param warning The warning to handle. */ void handleWarning(SQLWarning warning); } /** * Basic support for {@link WarningHandler} implementations which handle {@link SQLWarning warnings} */ public abstract static class WarningHandlerLoggingSupport implements WarningHandler { @Override public final void handleWarning(SQLWarning warning) { logWarning( "SQL Warning Code: " + warning.getErrorCode() + ", SQLState: " + warning.getSQLState(), warning.getMessage() ); } /** * Delegate to log common details of a {@link SQLWarning warning} * * @param description A description of the warning * @param message The warning message */ protected abstract void logWarning(String description, String message); } /** * Standard SQLWarning handler for logging warnings */ public static class StandardWarningHandler extends WarningHandlerLoggingSupport { private final String introMessage; /** * Creates a StandardWarningHandler * * @param introMessage The introduction message for the hierarchy */ public StandardWarningHandler(String introMessage) { this.introMessage = introMessage; } @Override public boolean doProcess() { return LOG.isEnabled( Level.WARN ); } @Override public void prepare(SQLWarning warning) { LOG.debug( introMessage, warning ); } @Override protected void logWarning( String description, String message) { LOG.warn( description ); LOG.warn( message ); } } /** * Static access to the standard handler for logging warnings */ public static final StandardWarningHandler STANDARD_WARNING_HANDLER = new StandardWarningHandler( DEFAULT_WARNING_MSG ); /** * Generic algorithm to walk the hierarchy of SQLWarnings * * @param warning The warning to walk * @param handler The handler */ public void walkWarnings( SQLWarning warning, WarningHandler handler) { if ( warning == null || !handler.doProcess() ) { return; } handler.prepare( warning ); while ( warning != null ) { handler.handleWarning( warning ); warning = warning.getNextWarning(); } } /** * Standard (legacy) behavior for logging warnings associated with a JDBC {@link Connection} and clearing them. *

* Calls {@link #handleAndClearWarnings(Connection, WarningHandler)} using {@link #STANDARD_WARNING_HANDLER} * * @param connection The JDBC connection potentially containing warnings */ public void logAndClearWarnings(Connection connection) { handleAndClearWarnings( connection, STANDARD_WARNING_HANDLER ); } public void logAndClearWarnings(Statement statement) { handleAndClearWarnings( statement, STANDARD_WARNING_HANDLER ); } /** * General purpose handling of warnings associated with a JDBC {@link Connection}. * * @param connection The JDBC connection potentially containing warnings * @param handler The handler for each individual warning in the stack. * * @see #walkWarnings */ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) public void handleAndClearWarnings( Connection connection, WarningHandler handler) { try { if ( logWarnings ) { walkWarnings( connection.getWarnings(), handler ); } } catch (SQLException sqle) { // workaround for WebLogic LOG.debug( "could not log warnings", sqle ); } try { // Sybase fail if we don't do that, sigh... connection.clearWarnings(); } catch (SQLException sqle) { LOG.debug( "could not clear warnings", sqle ); } } /** * General purpose handling of warnings associated with a JDBC {@link Statement}. * * @param statement The JDBC statement potentially containing warnings * @param handler The handler for each individual warning in the stack. * * @see #walkWarnings */ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) public void handleAndClearWarnings( Statement statement, WarningHandler handler) { // See HHH-9174. Statement#getWarnings can be an expensive call for many JDBC libs. Don't do it unless // the log level would actually allow a warning to be logged. if ( logWarnings ) { try { walkWarnings( statement.getWarnings(), handler ); } catch (SQLException sqlException) { // workaround for WebLogic LOG.debug( "could not log warnings", sqlException ); } } try { // Sybase fail if we don't do that, sigh... statement.clearWarnings(); } catch (SQLException sqle) { LOG.debug( "could not clear warnings", sqle ); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy