/* * 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.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; import org.apache.cocoon.util.JDBCTypeConversions; /** * Selects a record from a database. The action can select one or more * tables, and can select from more than one row of a table at a * time. * * @author <a href="mailto:haul@apache.org">Christian Haul</a> * @version CVS $Id$ */ public class DatabaseSelectAction 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[] keys = table.getChild("keys").getChildren("key"); Configuration[] values = table.getChild("values").getChildren("value"); queryData = new CacheHelper( keys.length, keys.length + values.length ); fillModes( keys , true , defaultModeNames, modeTypes, queryData ); fillModes( values, false, defaultModeNames, modeTypes, queryData ); StringBuffer queryBuffer = new StringBuffer("SELECT "); //queryBuffer.append(table.getAttribute("name")).append(" WHERE "); int count = 0; for (int i = 0; i < queryData.columns.length; i++) { if ( !queryData.columns[i].isKey ) { if ( count > 0 ) { queryBuffer.append(", "); } queryBuffer .append( queryData.columns[i].columnConf.getAttribute( "name" ) ); count++; } } queryBuffer.append(" FROM ").append(table.getAttribute("name")).append(" WHERE "); count = 0; for (int i = 0; i < queryData.columns.length; i++) { if ( queryData.columns[i].isKey ) { if ( count > 0 ) { queryBuffer.append(" AND "); } queryBuffer .append( queryData.columns[i].columnConf.getAttribute( "name" ) ) .append( "= ?"); count++; } } queryData.queryString = queryBuffer.toString(); this.cachedQueryData.put( lookUpKey, queryData ); } } return queryData; } /** * Fetch all values for all key 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++ ){ if ( queryData.columns[i].isKey ) { columnValues[i] = this.getColumnValue( tableConf, queryData.columns[i], objectModel ); } else { // columnValues[i] = new Object[1]; // this should not be needed } } 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++; } } statement.execute(); // retrieve values ResultSet resultset = statement.getResultSet(); rowIndex = 0; while ( resultset.next() ){ for (int i = 0; i < queryData.columns.length; i++) { if ( !queryData.columns[i].isKey ) { Object value = JDBCTypeConversions.getColumn( resultset, queryData.columns[i].columnConf ); this.setOutput(objectModel, outputMode, results, table, queryData.columns[i].columnConf, rowIndex, value); } } rowIndex++; } if (rowIndex == 0) { results = EMPTY_MAP;} return rowIndex; } }