001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.cocoon.acting.modular;
019:
020: import java.sql.Connection;
021: import java.sql.PreparedStatement;
022: import java.sql.ResultSet;
023: import java.sql.ResultSetMetaData;
024: import java.sql.SQLException;
025: import java.util.Map;
026:
027: import org.apache.avalon.framework.configuration.Configuration;
028: import org.apache.avalon.framework.configuration.ConfigurationException;
029: import org.apache.avalon.framework.service.ServiceException;
030:
031: /**
032: * Executes an arbitrary query. The query is associated with a table
033: * and selected through the others mode. All keys and values are set
034: * in order of appearance, starting with keys, thus the query needs to
035: * have as many placeholders for prepared statement parameters. If it
036: * is an update query, the number of affected rows is returned to the
037: * sitemap.
038: *
039: *<pre>
040: * <table name="example">
041: * <queries>
042: * <query mode="one">update example set count=count+1 where id=?</query>
043: * <query mode="two">select count, name from example where id=?</query>
044: * </queries>
045: * <keys>
046: * <key name="id"/>
047: * </keys>
048: * <values/>
049: * </table>
050: *</pre>
051: *
052: * @author <a href="mailto:haul@apache.org">Christian Haul</a>
053: * @version CVS $Id: DatabaseQueryAction.java 433543 2006-08-22 06:22:54Z crossley $
054: */
055: public class DatabaseQueryAction extends DatabaseAction {
056:
057: /**
058: * determine which mode to use as default mode
059: * here: SELECT
060: * highly specific to operation INSERT / UPDATE / DELETE / SELECT
061: */
062: protected String selectMode(boolean isAutoIncrement, Map modes) {
063:
064: return (String) modes.get(MODE_OTHERS);
065: }
066:
067: /**
068: * determine whether autoincrement columns should be honoured by
069: * this operation. This is usually snsible only for INSERTs.
070: */
071: protected boolean honourAutoIncrement() {
072: return false;
073: }
074:
075: /**
076: * Get the String representation of the PreparedStatement. This is
077: * mapped to the Configuration object itself, so if it doesn't exist,
078: * it will be created.
079: *
080: * @param table the table's configuration object
081: * @return the insert query as a string
082: */
083: protected CacheHelper getQuery(Configuration table, Map modeTypes,
084: Map defaultModeNames) throws ConfigurationException,
085: ServiceException {
086:
087: LookUpKey lookUpKey = new LookUpKey(table, modeTypes);
088: CacheHelper queryData = null;
089: synchronized (this .cachedQueryData) {
090: queryData = (CacheHelper) this .cachedQueryData
091: .get(lookUpKey);
092: if (queryData == null) {
093: Configuration[] queries = table.getChild("queries")
094: .getChildren("query");
095: Configuration[] keys = table.getChild("keys")
096: .getChildren("key");
097: Configuration[] values = table.getChild("values")
098: .getChildren("value");
099:
100: boolean found = false;
101: String queryModeName = "";
102: String query = "";
103: boolean useValues = true;
104: for (int i = 0; i < queries.length; i++) {
105: queryModeName = queries[i].getAttribute("mode",
106: null);
107: if (queryModeName
108: .equals(modeTypes.get(MODE_OTHERS))
109: || "all".equals(queryModeName)) {
110: query = queries[i].getValue();
111: useValues = queries[i].getAttributeAsBoolean(
112: "use-values", useValues);
113: found = true;
114: break;
115: }
116: }
117:
118: if (!found) {
119: throw new ConfigurationException(
120: "Could not find query mode "
121: + modeTypes.get(MODE_OTHERS)
122: + " for table "
123: + table.getAttribute("name", null));
124: }
125:
126: queryData = new CacheHelper(keys.length, keys.length
127: + (useValues ? values.length : 0));
128: queryData.queryString = query;
129: fillModes(keys, true, defaultModeNames, modeTypes,
130: queryData);
131: if (useValues)
132: fillModes(values, false, defaultModeNames,
133: modeTypes, queryData);
134:
135: this .cachedQueryData.put(lookUpKey, queryData);
136: }
137: }
138:
139: return queryData;
140: }
141:
142: /**
143: * Fetch all values for all columns that are needed to do the database operation.
144: */
145: protected Object[][] getColumnValues(Configuration tableConf,
146: CacheHelper queryData, Map objectModel)
147: throws ConfigurationException, ServiceException {
148:
149: Object[][] columnValues = new Object[queryData.columns.length][];
150: for (int i = 0; i < queryData.columns.length; i++) {
151: columnValues[i] = this .getColumnValue(tableConf,
152: queryData.columns[i], objectModel);
153: }
154: return columnValues;
155: }
156:
157: /**
158: * set all necessary ?s and execute the query
159: */
160: protected int processRow(Map objectModel, Connection conn,
161: PreparedStatement statement, String outputMode,
162: Configuration table, CacheHelper queryData,
163: Object[][] columnValues, int rowIndex, Map results)
164: throws SQLException, ConfigurationException, Exception {
165:
166: int currentIndex = 1;
167:
168: // ordering is different for SELECT just needs keys
169: for (int i = 0; i < queryData.columns.length; i++) {
170: Column col = queryData.columns[i];
171: if (col.isKey) {
172: this .setColumn(objectModel, outputMode, results, table,
173: col.columnConf, rowIndex,
174: columnValues[i][(col.isSet ? rowIndex : 0)],
175: statement, currentIndex);
176: currentIndex++;
177: }
178: }
179: boolean hasResult = statement.execute();
180: if (!hasResult) {
181: return statement.getUpdateCount();
182: } else {
183: // retrieve values
184: ResultSet resultset = statement.getResultSet();
185: ResultSetMetaData metadata = resultset.getMetaData();
186: rowIndex = 0;
187: while (resultset.next()) {
188: //if ( ! ( rowIndex == -1 && resultset.isLast() ) ) {
189: rowIndex++;
190: //}
191: String tableName = "";
192: String columnName = "";
193: for (int i = 1; i <= metadata.getColumnCount(); i++) {
194: Object value = resultset.getObject(i);
195: tableName = metadata.getTableName(i);
196: columnName = metadata.getColumnLabel(i) + "["
197: + rowIndex + "]";
198: if (!tableName.equals("")) {
199: columnName = tableName + "." + columnName;
200: }
201: if (this .getLogger().isDebugEnabled()) {
202: this .getLogger().debug(
203: "retrieving " + columnName + " as "
204: + value);
205: }
206: results.put(metadata.getTableName(i) + "."
207: + metadata.getColumnLabel(i), value);
208: }
209: }
210: return rowIndex;
211: }
212: }
213:
214: }
|