diff -r ab576c51e77c -r fa3f382b9e9d src/net/lemnik/eodsql/impl/BaseQueryImpl.java --- a/src/net/lemnik/eodsql/impl/BaseQueryImpl.java Sun Sep 19 22:06:50 2010 +0200 +++ b/src/net/lemnik/eodsql/impl/BaseQueryImpl.java Sun Sep 19 00:00:00 2010 +0200 @@ -65,7 +65,8 @@ try { return connectionSource.isClosed(); } catch (SQLException ex) { - throw new EoDException(ex); + // Cannot use exception translator here as one cannot use closed connections for that. + throw new EoDException("isClosed", ex); } } @@ -145,8 +146,8 @@ final Object[] args) throws Throwable { + final Callable impl = methods.get(method); try { - final Callable impl = methods.get(method); return impl.invoke(method, args); } catch(final RuntimeException runtimeException) { // we catch this, so that it's not caught in the catch(Exception) @@ -163,7 +164,7 @@ } // if we got here, the Exception was not declared, wrap it in a RuntimeException - throw new RuntimeException(exception); + throw ExceptionTranslationUtils.translateException(connectionSource, method, impl, exception); } } @@ -230,6 +231,11 @@ } } } + + AbstractMethodImplementation getMethodImpl() + { + return (AbstractMethodImplementation) implementation; + } } @@ -335,7 +341,7 @@ try { tmp.setAutoCommit(autoCommit); } catch(SQLException ex) { - throw new EoDException("Cannot set auto-commit on Connection", ex); + throw ExceptionTranslationUtils.translateException(tmp, "setAutoCommit", "-", ex); } connections.put(tmp, PRESENT); @@ -363,6 +369,11 @@ } } } + + DataSource getDataSource() + { + return datasource; + } private static class ConnectionUtil { diff -r ab576c51e77c -r fa3f382b9e9d src/net/lemnik/eodsql/impl/ExceptionTranslationUtils.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/lemnik/eodsql/impl/ExceptionTranslationUtils.java Sun Sep 19 00:00:00 2010 +0200 @@ -0,0 +1,162 @@ +package net.lemnik.eodsql.impl; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import net.lemnik.eodsql.EoDException; +import net.lemnik.eodsql.impl.BaseQueryImpl.Callable; +import net.lemnik.eodsql.impl.BaseQueryImpl.ConnectionSource; +import net.lemnik.eodsql.impl.BaseQueryImpl.DataSourceConnectionSource; +import net.lemnik.eodsql.impl.BaseQueryImpl.MethodImpl; + +/** + * A class that supports exception translation of {@link SQLException} into a + * custom exception hierarchy. + * + * @author Bernd Rinn + */ +public class ExceptionTranslationUtils { + + private static ExceptionTranslator exceptionTranslator = new DefaultExceptionTranslator(); + + private static class DefaultExceptionTranslator implements + ExceptionTranslator { + + public RuntimeException translateException(DataSource dataSource, + String task, String sql, SQLException ex) { + return new EoDException("'" + task + "' [SQL: '" + sql + "']", + ex); + } + + public RuntimeException translateException(Connection connection, + String task, String sql, SQLException ex) { + return new EoDException("'" + task + "' [SQL: '" + sql + "']", + ex); + } + + public RuntimeException uniqueResultExpected() { + return new EoDException( + "A unique result was expected but the database returned multiple rows."); + } + + } + + /** + * Sets the {@link ExceptionTranslator} for EoDSQL. Call once before EoDSQL + * is used. + */ + public static void setExceptionTranslator(ExceptionTranslator factory) { + exceptionTranslator = factory; + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException( + ConnectionSource connectionSource, final Method method, + final Callable callable, final Exception ex) { + return translateException(connectionSource, method.getName(), + getSql(callable), ex); + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException( + ConnectionSource connectionSource, final String task, + final String sql, final Exception ex) { + if (connectionSource instanceof DataSourceConnectionSource) { + final DataSource source = ((DataSourceConnectionSource) connectionSource) + .getDataSource(); + return translateException(source, task, sql, ex); + } else { + Connection conn = null; + try { + conn = connectionSource.getConnection(); + return translateException(conn, task, sql, ex); + } catch (SQLException ex1) { + return new EoDException(toMessage(task, sql), ex); + } finally { + if (conn != null) { + try { + connectionSource.releaseConnection(conn); + } catch (SQLException ex1) { + // Nothing we can do here. + } + } + } + } + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException(DataSource dataSource, + final Method method, final Callable callable, final Exception ex) { + return translateException(dataSource, method.getName(), + getSql(callable), ex); + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException(DataSource dataSource, + final String task, final String sql, final Exception ex) { + if (ex instanceof SQLException) { + return exceptionTranslator.translateException(dataSource, task, + sql, (SQLException) ex); + } else { + return new EoDException(toMessage(task, sql), ex); + } + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException(Connection connection, + final Method method, final Callable callable, final Exception ex) { + return translateException(connection, method.getName(), + getSql(callable), ex); + } + + /** + * Translates the exception ex to a RuntimeException. + */ + public static RuntimeException translateException(Connection connection, + final String task, final String sql, final Exception ex) { + if (ex instanceof SQLException) { + return exceptionTranslator.translateException(connection, task, + sql, (SQLException) ex); + } else { + return new EoDException(toMessage(task, sql), ex); + } + } + + /** + * Returns an exception when a unique result is expected from the database, + * but the database returns multiple rows. + */ + public static RuntimeException uniqueResultExpected() + { + return exceptionTranslator.uniqueResultExpected(); + } + + private static String toMessage(final String task, final String sql) { + return task + "[SQL: '" + sql + "']."; + } + + private static String getSql(final Callable callable) { + if (callable instanceof MethodImpl) { + final AbstractMethodImplementation m = ((MethodImpl) callable) + .getMethodImpl(); + return m.query.toString(); + } else { + return "?"; + } + } + +} diff -r ab576c51e77c -r fa3f382b9e9d src/net/lemnik/eodsql/impl/ExceptionTranslator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/net/lemnik/eodsql/impl/ExceptionTranslator.java Sun Sep 19 00:00:00 2010 +0200 @@ -0,0 +1,50 @@ +package net.lemnik.eodsql.impl; + +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +/** + * A roles that translates {@link SQLException}s into RuntimeExceptions. + * + * @author Bernd Rinn + */ +public interface ExceptionTranslator { + + /** + * Translates the exception ex. + * + * @param dataSource + * the DataSource that the exception occurred on. + * @param task + * the task when the exception occurred. + * @param sql + * the sql command when the exception occurred. + * @param ex + * the original exception. + */ + public RuntimeException translateException(DataSource dataSource, + String task, String sql, SQLException ex); + + /** + * Translates the exception ex. + * + * @param connection + * the Connection that the exception occurred on. Can be expected to be open. + * @param task + * the task when the exception occurred. + * @param sql + * the sql command when the exception occurred. + * @param ex + * the original exception. + */ + public RuntimeException translateException(Connection connection, + String task, String sql, SQLException ex); + + /** + * Returns an exception when a unique result is expected from the database, + * but the database returns multiple rows. + */ + public RuntimeException uniqueResultExpected(); +} diff -r ab576c51e77c -r fa3f382b9e9d src/net/lemnik/eodsql/spi/util/SingleRowResultSetWrapper.java --- a/src/net/lemnik/eodsql/spi/util/SingleRowResultSetWrapper.java Sun Sep 19 22:06:50 2010 +0200 +++ b/src/net/lemnik/eodsql/spi/util/SingleRowResultSetWrapper.java Sun Sep 19 00:00:00 2010 +0200 @@ -3,22 +3,29 @@ import java.sql.ResultSet; import java.sql.SQLException; +import net.lemnik.eodsql.impl.ExceptionTranslationUtils; + /** * Created on 2008/06/15 + * * @author Jason Morris */ class SingleRowResultSetWrapper extends AbstractResultSetWrapper { SingleRowResultSetWrapper(final DataObjectBinding binding) { - super(binding); + super(binding); } @Override public T wrap(final ResultSet results) throws SQLException { - if(results.next()) { - return binding.unmarshall(results); - } else { - return null; - } + if (results.next()) { + final T wrapped = binding.unmarshall(results); + if (results.next()) { + throw ExceptionTranslationUtils.uniqueResultExpected(); + } + return wrapped; + } else { + return null; + } } }