From 5e8e0328a93cd02d3d47cd41e79c0f5724739f52 Mon Sep 17 00:00:00 2001 From: bernd Date: Wed, 14 Nov 2012 21:24:31 +0100 Subject: [PATCH 12/25] Make EoDSQL work with JDBC drivers which do not support scrollable cursors (e.g. SQLite JDBC driver) for disconnected DataSets, Collections and arrays. If a connected DataSet is requested and the JDBC driver doesn't support scrollable cursors, provide a good error message. --- eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java | 4 +- .../eodsql/impl/AbstractMethodImplementation.java | 66 ++++++++++++++++++--- .../src/net/lemnik/eodsql/impl/BaseQueryImpl.java | 7 ++- .../lemnik/eodsql/impl/TransactionQueryImpl.java | 5 +- eodsql/src/net/lemnik/eodsql/spi/Context.java | 16 +++++- .../eodsql/spi/util/CollectionWrapperFactory.java | 67 ++++++++++++++++++---- .../eodsql/spi/util/DisconnectedDataSet.java | 30 ++++++---- 7 files changed, 159 insertions(+), 36 deletions(-) diff --git a/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java b/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java index e9c347f..0bdc77d 100644 --- a/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java +++ b/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java @@ -192,6 +192,7 @@ class QuickQueryUtil { final Context context = new Context( DEFAULT_SELECT_PARAMETERS, + DataSet.class, parameters); try { @@ -231,7 +232,8 @@ class QuickQueryUtil { throws SQLException, ParseException { final Context context = new Context( - DEFAULT_UPDATE_PARAMETERS, + DEFAULT_UPDATE_PARAMETERS, + Integer.class, parameters); try { diff --git a/eodsql/src/net/lemnik/eodsql/impl/AbstractMethodImplementation.java b/eodsql/src/net/lemnik/eodsql/impl/AbstractMethodImplementation.java index 89b1388..7b884bb 100644 --- a/eodsql/src/net/lemnik/eodsql/impl/AbstractMethodImplementation.java +++ b/eodsql/src/net/lemnik/eodsql/impl/AbstractMethodImplementation.java @@ -3,19 +3,23 @@ package net.lemnik.eodsql.impl; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.sql.Connection; -import java.sql.SQLException; +import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; - +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Arrays; +import java.util.List; import java.util.Map; +import net.lemnik.eodsql.DataIterator; +import net.lemnik.eodsql.DataSet; +import net.lemnik.eodsql.InvalidQueryException; import net.lemnik.eodsql.QueryTool; +import net.lemnik.eodsql.Select; import net.lemnik.eodsql.TypeMapper; -import net.lemnik.eodsql.InvalidQueryException; - import net.lemnik.eodsql.spi.Context; -import net.lemnik.eodsql.spi.StatementResource; import net.lemnik.eodsql.spi.MethodImplementation; - +import net.lemnik.eodsql.spi.StatementResource; import net.lemnik.eodsql.spi.util.Query; import net.lemnik.eodsql.spi.util.ResultSetWrapper; @@ -26,6 +30,12 @@ import net.lemnik.eodsql.spi.util.ResultSetWrapper; abstract class AbstractMethodImplementation implements MethodImplementation { + private final static List ALL_RESULT_SET_TYPES = Arrays.asList( + ResultSet.TYPE_SCROLL_SENSITIVE, + ResultSet.TYPE_SCROLL_INSENSITIVE, + ResultSet.TYPE_FORWARD_ONLY + ); + protected ResultSetWrapper> wrapper = null; protected Query query = null; @@ -37,15 +47,57 @@ abstract class AbstractMethodImplementation throws SQLException { final Connection connection = context.getResource(Connection.class).get(); + int preferredResultSetType = wrapper.getPreferredResultSetType(); + int resultSetType = getResultSetType(connection.getMetaData(), preferredResultSetType); + if (supportsScrolling(preferredResultSetType) && false == supportsScrolling(resultSetType)) { + if (isDisconnected(context)) { + resultSetType = ResultSet.TYPE_FORWARD_ONLY; + } else { + final String dbName = connection.getMetaData().getDatabaseProductName(); + throw new SQLException("This database (" + dbName + + ") does not support scrollable cursors, thus connected DataSets are not available."); + } + } final PreparedStatement statement = connection.prepareStatement( query.toString(), - wrapper.getPreferredResultSetType(), + resultSetType, wrapper.getPreferredResultSetConcurrency()); context.setResource(new StatementResource(statement)); return statement; } + + private int getResultSetType(DatabaseMetaData dbMetaData, int preferredResultSetType) throws SQLException { + if (dbMetaData.supportsResultSetType(preferredResultSetType)) { + return preferredResultSetType; + } + for (int type : ALL_RESULT_SET_TYPES) { + if (dbMetaData.supportsResultSetType(type)) { + return type; + } + } + // We assume that the dbMetaData.supportsResultSetType() call lied to us and that the most + // basic result set is supported. + return ResultSet.TYPE_FORWARD_ONLY; + } + + private boolean supportsScrolling(int resultSetType) { + return resultSetType == ResultSet.TYPE_SCROLL_SENSITIVE + || resultSetType == ResultSet.TYPE_SCROLL_INSENSITIVE; + } + + private boolean isDisconnected(final Context context) { + if (context.getAnnotation() instanceof Select + && ((Select) context.getAnnotation()).disconnected()) { + return true; + } + if (DataSet.class.isAssignableFrom(context.getReturnType()) + || DataIterator.class.isAssignableFrom(context.getReturnType())) { + return false; + } + return true; + } protected void fillPreparedStatementParameters( final Context> context, diff --git a/eodsql/src/net/lemnik/eodsql/impl/BaseQueryImpl.java b/eodsql/src/net/lemnik/eodsql/impl/BaseQueryImpl.java index a2e6ee7..1c71ff0 100644 --- a/eodsql/src/net/lemnik/eodsql/impl/BaseQueryImpl.java +++ b/eodsql/src/net/lemnik/eodsql/impl/BaseQueryImpl.java @@ -167,8 +167,9 @@ class BaseQueryImpl implements InvocationHandler { } } - protected Context createContext(Annotation annotation, final Object[] args) { - return new Context(annotation, args); + protected Context createContext(final Annotation annotation, + final Class> returnType, final Object[] args) { + return new Context(annotation, returnType, args); } static interface Callable { @@ -210,7 +211,7 @@ class BaseQueryImpl implements InvocationHandler { final Object[] args) throws Throwable { - final Context context = createContext(annotation, args); + final Context context = createContext(annotation, method.getReturnType(), args); final Resource connection = new ConnectionSourceConnectionResource(connectionSource); diff --git a/eodsql/src/net/lemnik/eodsql/impl/TransactionQueryImpl.java b/eodsql/src/net/lemnik/eodsql/impl/TransactionQueryImpl.java index 212808d..57c1ee8 100644 --- a/eodsql/src/net/lemnik/eodsql/impl/TransactionQueryImpl.java +++ b/eodsql/src/net/lemnik/eodsql/impl/TransactionQueryImpl.java @@ -44,9 +44,10 @@ class TransactionQueryImpl extends BaseQueryImpl { } @Override - protected Context createContext(Annotation annotation, final Object[] args) + protected Context createContext(final Annotation annotation, + final Class> returnType, final Object[] args) { - final Context context = new Context(annotation, args); + final Context context = new Context(annotation, returnType, args); if (noAutoClose) { context.setDontCloseConnection(true); diff --git a/eodsql/src/net/lemnik/eodsql/spi/Context.java b/eodsql/src/net/lemnik/eodsql/spi/Context.java index b9acf36..6a89dd8 100644 --- a/eodsql/src/net/lemnik/eodsql/spi/Context.java +++ b/eodsql/src/net/lemnik/eodsql/spi/Context.java @@ -45,6 +45,8 @@ public class Context { private final A annotation; private final Object[] parameters; + + private final Class> returnType; private Object returnValue = null; @@ -70,10 +72,12 @@ public class Context { *