Index: src/net/lemnik/eodsql/QueryTool.java =================================================================== --- src/net/lemnik/eodsql/QueryTool.java (revision 217) +++ src/net/lemnik/eodsql/QueryTool.java (working copy) @@ -22,6 +22,7 @@ import net.lemnik.eodsql.spi.MethodImplementationFactory; import net.lemnik.eodsql.spi.util.DataObjectBinding; +import net.lemnik.eodsql.spi.util.MapDataObjectBinding; /** *

@@ -309,7 +310,7 @@ *

* Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method * is significantly slower than building a Query interface, it provides needed functionality that a Query - * interface cannot. The returned {@link DataSet} will be connected to the database, and the Connection will be + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable, and the Connection will be * taken from the {@link #setDefaultDataSource(DataSource) defaultDataSource}. *

* This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however @@ -351,7 +352,7 @@ *

* Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method * is significantly slower than building a Query interface, it provides needed functionality that a Query - * interface cannot. The returned {@link DataSet} will be connected to the database and the Connection is + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable and the Connection is * obtained from the DataSource provided. *

* This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however @@ -419,7 +420,7 @@ *

* Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method * is significantly slower than building a Query interface, it provides needed functionality that a Query - * interface cannot. The returned {@link DataSet} will be connected to the database. + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable. *

* This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however * provides the same primitive-type abstraction that a Query interface provides you with. The query string is @@ -484,6 +485,118 @@ } /** + *

+ * Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method + * is significantly slower than building a Query interface, it provides needed functionality that a Query + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable, and the Connection will be + * taken from the {@link #setDefaultDataSource(DataSource) defaultDataSource}. + *

+ * This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however + * provides the same primitive-type abstraction that a Query interface provides you with. The query string is + * parsed in the same way as a query string passed to an {@link Select @Select} annotation. + *

+ * This method is the same as calling select(getDefaultDataSource(), type, query, parameters). + *

+ * + * @param query the SQL query to be executed + * @param parameters the parameters to be used in conjunction with the query string. + * @return a connected {@code DataSet} containing the selected rows + * reflected as a map where each key is one column name + * @see #select(DataSource, Class, String, Object...) + * @throws InvalidQueryException if the query string given cannot + * be parsed, or doesn't match the given parameters + * @throws IllegalStateException is the defaultDataSource has not yet been set + */ + public static DataSet> select( + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(defaultDataSource != null) { + return select(defaultDataSource, + MapDataObjectBinding.getStringObjectMapObjectType(), + query, + parameters); + } else { + throw new IllegalStateException( + "the default DataSource has not been set."); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method + * is significantly slower than building a Query interface, it provides needed functionality that a Query + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable and the Connection is + * obtained from the DataSource provided. + *

+ * This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however + * provides the same primitive-type abstraction that a Query interface provides you with. The query string is + * parsed in the same way as a query string passed to an {@link Select @Select} annotation. + *

+ * + * @param dataSource the DataSource to obtain a database connection through + * @param query the SQL query to be executed + * @param parameters the parameters to be used in conjunction + * with the query string. + * @return a connected {@code DataSet} containing the selected rows + * reflected as a map where each key is one column name + * @throws InvalidQueryException if the query string given cannot + * be parsed, or doesn't match the given parameters + * @throws IllegalArgumentException is the DataSource + * provided is null + */ + public static DataSet> select( + final DataSource dataSource, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + return select(dataSource, + MapDataObjectBinding.getStringObjectMapObjectType(), + query, + parameters); + } + + /** + *

+ * Sometimes you need to perform a less structured "SELECT" than a Query interface allows. Although this method + * is significantly slower than building a Query interface, it provides needed functionality that a Query + * interface cannot. The returned {@link DataSet} will be connected to the database and will be updatable. + *

+ * This method does no caching of any sort, and the query will be re-parsed each time it is executed. It however + * provides the same primitive-type abstraction that a Query interface provides you with. The query string is + * parsed in the same way as a query string passed to an {@link Select @Select} annotation. + *

+ * + * @param connection the Connection to use to perform the query, closing the + * {@code DataSet} will not close this {@code Connection} + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * @return a connected {@code DataSet} containing the selected rows + * reflected as a map where each key is one column name + * @throws InvalidQueryException if the query string given cannot + * be parsed, or doesn't match the given parameters + * @throws IllegalArgumentException is the DataSource + * provided is null + */ + public static DataSet> select( + final Connection connection, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + return select(connection, + MapDataObjectBinding.getStringObjectMapObjectType(), + query, + parameters); + } + + /** *

* Returns the Map that specifies how Java types should * be mapped to SQL types and back again. This Map may be --- src/net/lemnik/eodsql/QuickQueryUtil.java (revision 217) +++ src/net/lemnik/eodsql/QuickQueryUtil.java (working copy) @@ -12,6 +12,7 @@ import java.text.ParseException; +import java.util.HashMap; import java.util.Map; import java.util.Collections; @@ -32,10 +33,14 @@ * @author Jason Morris */ class QuickQueryUtil { - private static final Map DEFAULT_DATASET_PARAMETERS = - Collections.singletonMap( - DataSetWrapper.PARAMETER_BINDING_TYPE, - (Object)BindingType.NORMAL_BINDING); + private static final Map DEFAULT_DATASET_PARAMETERS; + + static { + final Map map = new HashMap(2); + map.put(DataSetWrapper.PARAMETER_BINDING_TYPE, BindingType.NORMAL_BINDING); + map.put(DataSetWrapper.PARAMETER_UPDATABLE, true); + DEFAULT_DATASET_PARAMETERS = Collections.unmodifiableMap(map); + } private static final Select DEFAULT_SELECT_PARAMETERS = new Select() { public String value() { @@ -55,7 +60,7 @@ } public boolean readOnly() { - return true; + return false; } public Class cache() { Index: src/net/lemnik/eodsql/spi/util/DataObjectBinding.java --- src/net/lemnik/eodsql/spi/util/DataObjectBinding.java (revision 217) +++ src/net/lemnik/eodsql/spi/util/DataObjectBinding.java (working copy) @@ -33,6 +33,10 @@ private static Constructor DEFAULT_BINDING_CONSTRUCTOR = null; + static { + setDataObjectBinding(MapDataObjectBinding.getStringObjectMapObjectType(), MapDataObjectBinding.class); + } + /** * An empty array of String's that can be used when there are no key columns * known for the data-object type. This should only be returned when this Index: src/net/lemnik/eodsql/spi/util/MapDataObjectBinding.java --- src/net/lemnik/eodsql/spi/util/MapDataObjectBinding.java (revision 0) +++ src/net/lemnik/eodsql/spi/util/MapDataObjectBinding.java (revision 0) @@ -0,0 +1,186 @@ +package net.lemnik.eodsql.spi.util; + +import java.math.BigDecimal; +import java.sql.Array; +import java.sql.Date; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Time; +import java.sql.Timestamp; +import java.sql.Types; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import net.lemnik.eodsql.EoDException; + +/** + * A {@link DataObjectBinding} that stores the {@link ResultSet} in a {@link HashMap}. + * + * @author Bernd Rinn + */ +public class MapDataObjectBinding extends DataObjectBinding> +{ + @Override + public Class> getObjectType() + { + return getStringObjectMapObjectType(); + } + + @SuppressWarnings("unchecked") + public static Class> getStringObjectMapObjectType() + { + return (Class>) Collections.emptyMap().getClass(); + } + + @Override + public Map newInstance() throws EoDException + { + return new HashMap(); + } + + @Override + public void marshall(Map from, ResultSet results) throws SQLException, + EoDException + { + final ResultSetMetaData metaData = results.getMetaData(); + for (Map.Entry entry : from.entrySet()) + { + final int i = results.findColumn(entry.getKey()); + final int type = metaData.getColumnType(i); + if (entry.getValue() == null) + { + results.updateNull(i); + continue; + } + switch (type) + { + case Types.BIT: + case Types.BOOLEAN: + results.updateBoolean(i, (Boolean) entry.getValue()); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + results.updateString(i, entry.getValue().toString()); + break; + case Types.DATE: + results.updateDate(i, new Date(((java.util.Date) entry.getValue()).getTime())); + break; + case Types.TIME: + results.updateTime(i, new Time(((java.util.Date) entry.getValue()).getTime())); + break; + case Types.TIMESTAMP: + results.updateTimestamp(i, new Timestamp(((java.util.Date) entry.getValue()) + .getTime())); + break; + case Types.DECIMAL: + case Types.NUMERIC: + results.updateBigDecimal(i, (BigDecimal) entry.getValue()); + break; + case Types.REAL: + results.updateFloat(i, (Float) entry.getValue()); + break; + case Types.DOUBLE: + case Types.FLOAT: + results.updateDouble(i, (Double) entry.getValue()); + break; + case Types.TINYINT: + results.updateByte(i, (Byte) entry.getValue()); + break; + case Types.SMALLINT: + results.updateShort(i, (Short) entry.getValue()); + break; + case Types.INTEGER: + results.updateInt(i, (Integer) entry.getValue()); + break; + case Types.BIGINT: + results.updateLong(i, (Long) entry.getValue()); + break; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + results.updateBytes(i, (byte[]) entry.getValue()); + break; + case Types.JAVA_OBJECT: + results.updateObject(i, entry.getValue()); + break; + case Types.ARRAY: + results.updateArray(i, (Array) entry.getValue()); + break; + } + } + } + + @Override + public void unmarshall(ResultSet row, Map into) throws SQLException, + EoDException + { + final ResultSetMetaData metaData = row.getMetaData(); + for (int i = 1; i <= metaData.getColumnCount(); ++i) + { + final int type = metaData.getColumnType(i); + final String name = metaData.getColumnName(i); + switch (type) + { + case Types.BIT: + case Types.BOOLEAN: + into.put(name, row.getBoolean(i)); + break; + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + into.put(name, row.getString(i)); + break; + case Types.DATE: + into.put(name, row.getDate(i)); + break; + case Types.TIME: + into.put(name, row.getTime(i)); + break; + case Types.TIMESTAMP: + into.put(name, row.getTimestamp(i)); + break; + case Types.DECIMAL: + case Types.NUMERIC: + into.put(name, row.getBigDecimal(i)); + break; + case Types.REAL: + into.put(name, row.getFloat(i)); + break; + case Types.DOUBLE: + case Types.FLOAT: + into.put(name, row.getDouble(i)); + break; + case Types.TINYINT: + into.put(name, row.getByte(i)); + break; + case Types.SMALLINT: + into.put(name, row.getShort(i)); + break; + case Types.INTEGER: + into.put(name, row.getInt(i)); + break; + case Types.BIGINT: + into.put(name, row.getLong(i)); + break; + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + into.put(name, row.getBytes(i)); + break; + case Types.JAVA_OBJECT: + into.put(name, row.getObject(i)); + break; + case Types.ARRAY: + into.put(name, row.getArray(i)); + break; + } + if (row.wasNull()) + { + into.put(name, null); + } + } + } +}