Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/rxm.git

Named parameter statement used as inspiration for changing the RXM SQL.

AuthorDave Jarvis <email>
Date2015-03-16 21:00:09 GMT-0700
Commitd91ae8a7d5e2692a12cc3d5eaf3911f42ae65d40
Parent80a3444
source/java/com/whitemagicsoftware/rxm/NamedParameterStatement.java
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+package com.whitemagicsoftware.rxm;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class wraps around a {@link PreparedStatement} and allows
+ * the programmer to set parameters by name instead of by
+ * index. This eliminates any confusion as to which parameter index
+ * represents what. This also means that rearranging the SQL statement
+ * or adding a parameter doesn't involve renumbering your indices.
+ * Code such as this:
+ * <pre>
+ * Connection con=getConnection(); String query="select * from my_table where name=? or address=?"; PreparedStatement
+ * p=con.prepareStatement(query); p.setString(1, "bob"); p.setString(2, "123 terrace ct"); ResultSet
+ * rs=p.executeQuery();
+ * </pre>
+ *
+ * can be replaced with:
+ *
+ * <pre>
+ * Connection con=getConnection(); String query="select * from my_table where name=:name or address=:address";
+ * NamedParameterStatement p=new NamedParameterStatement(con, query); p.setString("name", "bob"); p.setString("address",
+ * "123 terrace ct"); ResultSet rs=p.executeQuery();
+ * </pre>
+ *
+ * @author Adam Crume
+ */
+public class NamedParameterStatement {
+ /** The statement this object is wrapping. */
+ private final PreparedStatement statement;
+
+ /** Maps parameter names to arrays of ints (the parameter indices). */
+ private Map<String, int[]> indexMap;
+
+ /**
+ * Creates a NamedParameterStatement.
+ * Wraps c.{@link Connection#prepareStatement(java.lang.String) prepareStatement}.
+ *
+ * @param connection
+ * the database connection
+ * @param query
+ * the parameterized query
+ * @throws SQLException
+ * if the statement could not be created
+ */
+ public NamedParameterStatement(Connection connection, String query)
+ throws SQLException {
+ String parsedQuery = parse(query);
+ statement = connection.prepareStatement(parsedQuery);
+ }
+
+ /**
+ * Parses a query with named parameters. The parameter-index mappings
+ * are put into the map, and the parsed query is returned.
+ *
+ * @param query
+ * query to parse
+ * @param paramMap
+ * map to hold parameter-index mappings
+ * @return the parsed query
+ */
+ private final String parse(String query) {
+ // Regular expressions didn't work well for ignoring
+ // parameter-like strings inside quotes.
+ int length = query.length();
+ StringBuffer parsedQuery = new StringBuffer(length);
+ boolean inSingleQuote = false;
+ boolean inDoubleQuote = false;
+ int index = 1;
+ HashMap<String, List<Integer>> indexes = new HashMap<String, List<Integer>>(10);
+
+ for (int i = 0; i < length; i++) {
+ char c = query.charAt(i);
+ if (inSingleQuote) {
+ if (c == '\'') {
+ inSingleQuote = false;
+ }
+ } else if (inDoubleQuote) {
+ if (c == '"') {
+ inDoubleQuote = false;
+ }
+ } else {
+ if (c == '\'') {
+ inSingleQuote = true;
+ } else if (c == '"') {
+ inDoubleQuote = true;
+ } else if (c == ':' && i + 1 < length && Character.isJavaIdentifierStart(query.charAt(i + 1))) {
+ int j = i + 2;
+ while (j < length && Character.isJavaIdentifierPart(query.charAt(j))) {
+ j++;
+ }
+ String name = query.substring(i + 1, j);
+ c = '?'; // replace the parameter with a question mark
+ i += name.length(); // skip past the end if the parameter
+
+ List<Integer> indexList = indexes.get(name);
+ if (indexList == null) {
+ indexList = new LinkedList<Integer>();
+ indexes.put(name, indexList);
+ }
+ indexList.add(Integer.valueOf(index));
+
+ index++;
+ }
+ }
+ parsedQuery.append(c);
+ }
+
+ indexMap = new HashMap<String, int[]>(indexes.size());
+ // replace the lists of Integer objects with arrays of ints
+ for (Map.Entry<String, List<Integer>> entry : indexes.entrySet()) {
+ List<Integer> list = entry.getValue();
+ int[] intIndexes = new int[list.size()];
+ int i = 0;
+ for (Integer x : list) {
+ intIndexes[i++] = x.intValue();
+ }
+ indexMap.put(entry.getKey(), intIndexes);
+ }
+
+ return parsedQuery.toString();
+ }
+
+ /**
+ * Returns the indexes for a parameter.
+ *
+ * @param name
+ * parameter name
+ * @return parameter indexes
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ */
+ private int[] getIndexes(String name) {
+ int[] indexes = indexMap.get(name);
+ if (indexes == null) {
+ throw new IllegalArgumentException("Parameter not found: " + name);
+ }
+ return indexes;
+ }
+
+ /**
+ * Sets a parameter.
+ *
+ * @param name
+ * parameter name
+ * @param value
+ * parameter value
+ * @throws SQLException
+ * if an error occurred
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ * @see PreparedStatement#setObject(int, java.lang.Object)
+ */
+ public void setObject(String name, Object value) throws SQLException {
+ int[] indexes = getIndexes(name);
+ for (int i = 0; i < indexes.length; i++) {
+ statement.setObject(indexes[i], value);
+ }
+ }
+
+ /**
+ * Sets a parameter.
+ *
+ * @param name
+ * parameter name
+ * @param value
+ * parameter value
+ * @throws SQLException
+ * if an error occurred
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ * @see PreparedStatement#setString(int, java.lang.String)
+ */
+ public void setString(String name, String value) throws SQLException {
+ int[] indexes = getIndexes(name);
+ for (int i = 0; i < indexes.length; i++) {
+ statement.setString(indexes[i], value);
+ }
+ }
+
+ /**
+ * Sets a parameter.
+ *
+ * @param name
+ * parameter name
+ * @param value
+ * parameter value
+ * @throws SQLException
+ * if an error occurred
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ * @see PreparedStatement#setInt(int, int)
+ */
+ public void setInt(String name, int value) throws SQLException {
+ int[] indexes = getIndexes(name);
+ for (int i = 0; i < indexes.length; i++) {
+ statement.setInt(indexes[i], value);
+ }
+ }
+
+ /**
+ * Sets a parameter.
+ *
+ * @param name
+ * parameter name
+ * @param value
+ * parameter value
+ * @throws SQLException
+ * if an error occurred
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ * @see PreparedStatement#setInt(int, int)
+ */
+ public void setLong(String name, long value) throws SQLException {
+ int[] indexes = getIndexes(name);
+ for (int i = 0; i < indexes.length; i++) {
+ statement.setLong(indexes[i], value);
+ }
+ }
+
+ /**
+ * Sets a parameter.
+ *
+ * @param name
+ * parameter name
+ * @param value
+ * parameter value
+ * @throws SQLException
+ * if an error occurred
+ * @throws IllegalArgumentException
+ * if the parameter does not exist
+ * @see PreparedStatement#setTimestamp(int, java.sql.Timestamp)
+ */
+ public void setTimestamp(String name, Timestamp value) throws SQLException {
+ int[] indexes = getIndexes(name);
+ for (int i = 0; i < indexes.length; i++) {
+ statement.setTimestamp(indexes[i], value);
+ }
+ }
+
+ /**
+ * Returns the underlying statement.
+ *
+ * @return the statement
+ */
+ public PreparedStatement getStatement() {
+ return statement;
+ }
+
+ /**
+ * Executes the statement.
+ *
+ * @return true if the first result is a {@link ResultSet}
+ * @throws SQLException
+ * if an error occurred
+ * @see PreparedStatement#execute()
+ */
+ public boolean execute() throws SQLException {
+ return statement.execute();
+ }
+
+ /**
+ * Executes the statement, which must be a query.
+ *
+ * @return the query results
+ * @throws SQLException
+ * if an error occurred
+ * @see PreparedStatement#executeQuery()
+ */
+ public ResultSet executeQuery() throws SQLException {
+ return statement.executeQuery();
+ }
+
+ /**
+ * Executes the statement, which must be an SQL INSERT, UPDATE or DELETE
+ * statement; or an SQL statement that returns nothing, such as a
+ * DDL statement.
+ *
+ * @return number of rows affected
+ * @throws SQLException
+ * if an error occurred
+ * @see PreparedStatement#executeUpdate()
+ */
+ public int executeUpdate() throws SQLException {
+ return statement.executeUpdate();
+ }
+
+ /**
+ * Closes the statement.
+ *
+ * @throws SQLException
+ * if an error occurred
+ * @see Statement#close()
+ */
+ public void close() throws SQLException {
+ statement.close();
+ }
+
+ /**
+ * Adds the current set of parameters as a batch entry.
+ *
+ * @throws SQLException
+ * if something went wrong
+ */
+ public void addBatch() throws SQLException {
+ statement.addBatch();
+ }
+
+ /**
+ * Executes all of the batched statements.
+ *
+ * See {@link Statement#executeBatch()} for details.
+ *
+ * @return update counts for each statement
+ * @throws SQLException
+ * if something went wrong
+ */
+ public int[] executeBatch() throws SQLException {
+ return statement.executeBatch();
+ }
+}
+
Delta356 lines added, 0 lines removed, 356-line increase