/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.cocoon.acting.modular; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.Map; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.ConfigurationException; import org.apache.avalon.framework.service.ServiceException; /** * Executes an arbitrary query. The query is associated with a table * and selected through the others mode. All keys and values are set * in order of appearance, starting with keys, thus the query needs to * have as many placeholders for prepared statement parameters. If it * is an update query, the number of affected rows is returned to the * sitemap. * *<pre> * <table name="example"> * <queries> * <query mode="one">update example set count=count+1 where id=?</query> * <query mode="two">select count, name from example where id=?</query> * </queries> * <keys> * <key name="id"/> * </keys> * <values/> * </table> *</pre> * * @author <a href="mailto:haul@apache.org">Christian Haul</a> * @version CVS $Id$ */ public class DatabaseQueryAction extends DatabaseAction { /** * determine which mode to use as default mode * here: SELECT * highly specific to operation INSERT / UPDATE / DELETE / SELECT */ protected String selectMode ( boolean isAutoIncrement, Map modes ) { return (String) modes.get( MODE_OTHERS ); } /** * determine whether autoincrement columns should be honoured by * this operation. This is usually snsible only for INSERTs. */ protected boolean honourAutoIncrement() { return false; } /** * Get the String representation of the PreparedStatement. This is * mapped to the Configuration object itself, so if it doesn't exist, * it will be created. * * @param table the table's configuration object * @return the insert query as a string */ protected CacheHelper getQuery( Configuration table, Map modeTypes, Map defaultModeNames ) throws ConfigurationException, ServiceException { LookUpKey lookUpKey = new LookUpKey( table, modeTypes ); CacheHelper queryData = null; synchronized( this.cachedQueryData ) { queryData = (CacheHelper) this.cachedQueryData.get( lookUpKey ); if (queryData == null) { Configuration[] queries = table.getChild("queries").getChildren("query"); Configuration[] keys = table.getChild("keys").getChildren("key"); Configuration[] values = table.getChild("values").getChildren("value"); boolean found = false; String queryModeName = ""; String query = ""; boolean useValues = true; for (int i=0; i<queries.length; i++) { queryModeName = queries[i].getAttribute("mode",null); if ( queryModeName.equals(modeTypes.get(MODE_OTHERS)) || "all".equals(queryModeName)) { query = queries[i].getValue(); useValues = queries[i].getAttributeAsBoolean("use-values", useValues); found = true; break; } } if (!found) { throw new ConfigurationException("Could not find query mode " + modeTypes.get(MODE_OTHERS) + " for table " + table.getAttribute("name",null)); } queryData = new CacheHelper( keys.length, keys.length + (useValues ? values.length : 0)); queryData.queryString = query; fillModes( keys , true , defaultModeNames, modeTypes, queryData ); if (useValues) fillModes( values, false, defaultModeNames, modeTypes, queryData ); this.cachedQueryData.put( lookUpKey, queryData ); } } return queryData; } /** * Fetch all values for all columns that are needed to do the database operation. */ protected Object[][] getColumnValues( Configuration tableConf, CacheHelper queryData, Map objectModel ) throws ConfigurationException, ServiceException { Object[][] columnValues = new Object[ queryData.columns.length ][]; for ( int i = 0; i < queryData.columns.length; i++ ){ columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], objectModel ); } return columnValues; } /** * set all necessary ?s and execute the query */ protected int processRow ( Map objectModel, Connection conn, PreparedStatement statement, String outputMode, Configuration table, CacheHelper queryData, Object[][] columnValues, int rowIndex, Map results ) throws SQLException, ConfigurationException, Exception { int currentIndex = 1; // ordering is different for SELECT just needs keys for (int i = 0; i < queryData.columns.length; i++) { Column col = queryData.columns[i]; if ( col.isKey ) { this.setColumn(objectModel, outputMode, results, table, col.columnConf, rowIndex, columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ], statement, currentIndex ); currentIndex++; } } boolean hasResult = statement.execute(); if (!hasResult) { return statement.getUpdateCount(); } else { // retrieve values ResultSet resultset = statement.getResultSet(); ResultSetMetaData metadata = resultset.getMetaData(); rowIndex = 0; while ( resultset.next() ){ //if ( ! ( rowIndex == -1 && resultset.isLast() ) ) { rowIndex++; //} String tableName = ""; String columnName = ""; for (int i = 1; i <= metadata.getColumnCount(); i++) { Object value = resultset.getObject(i); tableName = metadata.getTableName(i); columnName = metadata.getColumnLabel(i) + "[" + rowIndex + "]"; if (!tableName.equals("")) { columnName = tableName + "." + columnName; } if (this.getLogger().isDebugEnabled()) { this.getLogger().debug("retrieving " + columnName + " as " + value); } results.put(metadata.getTableName(i)+"."+metadata.getColumnLabel(i),value); } } return rowIndex; } } }