From bf2bca2843eea1a5f80ba4816e202f7d508c3211 Mon Sep 17 00:00:00 2001 From: bernd Date: Sat, 30 Jul 2011 20:00:06 +0200 Subject: [PATCH 6/6] Add methods QueryTool.insert() and QueryTool.insertMultiKeys() that return the generated key(s) --- eodsql/src/net/lemnik/eodsql/QueryTool.java | 337 ++++++++++++++++++++++ eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java | 88 ++++++ 2 files changed, 425 insertions(+), 0 deletions(-) diff --git a/eodsql/src/net/lemnik/eodsql/QueryTool.java b/eodsql/src/net/lemnik/eodsql/QueryTool.java index 0618823..7cc0512 100644 --- a/eodsql/src/net/lemnik/eodsql/QueryTool.java +++ b/eodsql/src/net/lemnik/eodsql/QueryTool.java @@ -777,6 +777,343 @@ public class QueryTool { } /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 + * @param generatedIdColumns the column names of the auto-generated ids, or null, + * if the columns automatically detected by the driver should be used + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * + * @return the map of generated key column names to generated keys + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static Map insertMultiKeys( + final Connection connection, + final String[] generatedIdColumns, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(connection == null) { + throw new IllegalArgumentException( + "A Connection must be specified for insertMultiKeys"); + } + + if(query == null || query.length() == 0) { + throw new IllegalArgumentException( + "You must specify a query for insertMultiKeys"); + } + + try { + return QuickQueryUtil.insert( + generatedIdColumns, + connection, + false, + query, + parameters); + } catch(SQLException sqle) { + if (ExceptionTranslationUtils.isDefaultTranslator()) { + throw new InvalidQueryException( + "Couldn't execute query: '" + query + "'", sqle); + } else { + throw ExceptionTranslationUtils.translateException(connection, "QueryTool.select()", + query, sqle); + } + } catch(ParseException pe) { + throw new InvalidQueryException( + "Cannot parser EoD SQL query: '" + query + "'", pe); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 use get the connection from to perform the query + * @param generatedIdColumns the column names of the auto-generated ids, or null, + * if the columns automatically detected by the driver should be used + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * + * @return the map of generated key column names to generated keys + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static Map insertMultiKeys( + final DataSource dataSource, + final String[] generatedIdColumns, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(dataSource == null) { + throw new IllegalArgumentException( + "A DataSource must be specified for insertMultiKeys"); + } + + if(query == null || query.length() == 0) { + throw new IllegalArgumentException( + "You must specify a query for insertMultiKeys"); + } + + try { + return QuickQueryUtil.insert( + generatedIdColumns, + dataSource.getConnection(), + true, + query, + parameters); + } catch(SQLException sqle) { + if (ExceptionTranslationUtils.isDefaultTranslator()) { + throw new InvalidQueryException( + "Couldn't execute query: '" + query + "'", sqle); + } else { + throw ExceptionTranslationUtils.translateException(dataSource, "QueryTool.select()", + query, sqle); + } + } catch(ParseException pe) { + throw new InvalidQueryException( + "Cannot parser EoD SQL query: '" + query + "'", pe); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 generatedIdColumns the column names of the auto-generated ids, or null, + * if the columns automatically detected by the driver should be used + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * @return the map of generated key column names to generated keys + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static Map insertMultiKeys( + final String[] generatedIdColumns, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(defaultDataSource != null) { + return insertMultiKeys(defaultDataSource, generatedIdColumns, query, parameters); + } else { + throw new IllegalStateException( + "the default DataSource has not been set."); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * @return the generated key of the row inserted or -1, if no key was generated + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static long insert( + final Connection connection, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(connection == null) { + throw new IllegalArgumentException( + "A Connection must be specified for insert"); + } + + if(query == null || query.length() == 0) { + throw new IllegalArgumentException( + "You must specify a query for insert"); + } + + try { + return QuickQueryUtil.insert( + connection, + false, + query, + parameters); + } catch(SQLException sqle) { + if (ExceptionTranslationUtils.isDefaultTranslator()) { + throw new InvalidQueryException( + "Couldn't execute query: '" + query + "'", sqle); + } else { + throw ExceptionTranslationUtils.translateException(connection, "QueryTool.select()", + query, sqle); + } + } catch(ParseException pe) { + throw new InvalidQueryException( + "Cannot parser EoD SQL query: '" + query + "'", pe); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 use get the connection from to perform the query + * @param query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * @return the generated key of the row inserted or -1, if no key was generated + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static long insert( + final DataSource dataSource, + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(dataSource == null) { + throw new IllegalArgumentException( + "A DataSource must be specified for insert"); + } + + if(query == null || query.length() == 0) { + throw new IllegalArgumentException( + "You must specify a query for insert"); + } + + try { + return QuickQueryUtil.insert( + dataSource.getConnection(), + true, + query, + parameters); + } catch(SQLException sqle) { + if (ExceptionTranslationUtils.isDefaultTranslator()) { + throw new InvalidQueryException( + "Couldn't execute query: '" + query + "'", sqle); + } else { + throw ExceptionTranslationUtils.translateException(dataSource, "QueryTool.select()", + query, sqle); + } + } catch(ParseException pe) { + throw new InvalidQueryException( + "Cannot parser EoD SQL query: '" + query + "'", pe); + } + } + + /** + *

+ * Sometimes you need to perform a less structured "INSERT" 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. + *

+ * 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 query the SQL query to be executed + * @param parameters the parameters to be used in + * conjunction with the query string. + * @return the generated key of the row inserted or -1, if no key was generated + * @throws InvalidDataTypeException if the given {@code type} cannot + * be bound as a row data-type + * @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 + * @since 2.2.1 + */ + public static long insert( + final String query, + final Object... parameters) + throws InvalidDataTypeException, + InvalidQueryException { + + if(defaultDataSource != null) { + return insert(defaultDataSource, query, parameters); + } else { + throw new IllegalStateException( + "the default DataSource has not been set."); + } + } + + /** *

* Returns the Map that specifies how Java types should * be mapped to SQL types and back again. This Map may be diff --git a/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java b/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java index 15a7e02..d2450e4 100644 --- a/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java +++ b/eodsql/src/net/lemnik/eodsql/QuickQueryUtil.java @@ -9,6 +9,7 @@ import java.sql.ResultSet; import java.sql.Connection; import java.sql.SQLException; import java.sql.PreparedStatement; +import java.sql.Statement; import java.text.ParseException; @@ -257,6 +258,93 @@ class QuickQueryUtil { } } + public static long insert( + final Connection connection, + final boolean closeConnection, + final String query, + final Object... parameters) + throws SQLException, ParseException { + + + final Context( + DEFAULT_SELECT_PARAMETERS, + parameters); + + try { + final Class[] parameterTypes = getParameterTypes(parameters); + + context.setResource(new ConnectionResource(connection, closeConnection)); + final Query eodquery = Query.getQuery(query, parameterTypes); + final PreparedStatement statement; + statement = connection.prepareStatement( + eodquery.toString(), Statement.RETURN_GENERATED_KEYS); + + fillStatementParameters(eodquery, statement, context); + context.setResource(new StatementResource(statement)); + statement.executeUpdate(); + final ResultSet keys = statement.getGeneratedKeys(); + try { + if (keys.next()) { + return keys.getLong(1); + } else + { + return -1; + } + } finally { + keys.close(); + } + } finally { + context.close(); + } + } + + public static Map insert( + final String[] generatedIdColumns, + final Connection connection, + final boolean closeConnection, + final String query, + final Object... parameters) + throws SQLException, ParseException { + + + final Context( + DEFAULT_SELECT_PARAMETERS, + parameters); + + try { + final Class[] parameterTypes = getParameterTypes(parameters); + + context.setResource(new ConnectionResource(connection, closeConnection)); + final Query eodquery = Query.getQuery(query, parameterTypes); + final PreparedStatement statement; + if (generatedIdColumns == null) { + statement = connection.prepareStatement( + eodquery.toString(), Statement.RETURN_GENERATED_KEYS); + } else { + statement = connection.prepareStatement( + eodquery.toString(), generatedIdColumns); + } + + fillStatementParameters(eodquery, statement, context); + context.setResource(new StatementResource(statement)); + statement.executeUpdate(); + final ResultSet keys = statement.getGeneratedKeys(); + try { + final Map result = new HashMap(); + while (keys.next()) { + for (int col = 1; col <= keys.getMetaData().getColumnCount(); ++col) { + result.put(keys.getMetaData().getColumnName(col), keys.getObject(col)); + } + } + return result; + } finally { + keys.close(); + } + } finally { + context.close(); + } + } + private static class ConnectionResource implements Resource { private Connection connection; -- 1.7.6