/* * 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.SQLException; import java.sql.Statement; 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.avalon.framework.service.ServiceSelector; import org.apache.cocoon.components.modules.database.AutoIncrementModule; /** * Adds record in a database. The action can update one or more * tables, and can add more than one row to a table at a time. See * {@link DatabaseAction} for details. * * @author <a href="mailto:haul@apache.org">Christian Haul</a> * @version CVS $Id$ */ public class DatabaseAddAction extends DatabaseAction { /** * 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; for (int i = 0; i < queryData.columns.length; i++) { Column col = queryData.columns[i]; if ( col.isAutoIncrement && col.isKey ) { currentIndex += setKeyAuto( table, col, currentIndex, rowIndex, conn, statement, objectModel, outputMode, results ); } else { this.setOutput( objectModel, outputMode, results, table, col.columnConf, rowIndex, columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ]); this.setColumn( statement, currentIndex, col.columnConf, columnValues[ i ][ ( col.isSet ? rowIndex : 0 ) ]); currentIndex++; } } int rowCount = statement.executeUpdate(); // get resulting ids for autoincrement columns for (int i = 0; i < queryData.columns.length; i++) { if ( queryData.columns[i].isAutoIncrement && queryData.columns[i].isKey ) { storeKeyValue( table, queryData.columns[i], rowIndex, conn, statement, objectModel, outputMode, results ); } } return rowCount; } /** * Sets the key value on the prepared statement for an autoincrement type. * * @param table the table's configuration object * @param column the key's configuration object * @param currentIndex the position of the key column * @param rowIndex the position in the current row set * @param conn the database connection * @param statement the insert statement * @param objectModel the objectModel object * @param outputMode name of the requested output module * @param results sitemap result object * @return the number of columns by which to increment the currentIndex */ protected int setKeyAuto(Configuration table, Column column, int currentIndex, int rowIndex, Connection conn, PreparedStatement statement, Map objectModel, String outputMode, Map results) throws ConfigurationException, SQLException, Exception { int columnCount = 0; ServiceSelector autoincrSelector = null; AutoIncrementModule autoincr = null; try { autoincrSelector = (ServiceSelector)this.manager.lookup(DATABASE_MODULE_SELECTOR); if (column.mode != null && autoincrSelector != null && autoincrSelector.isSelectable(column.mode)){ autoincr = (AutoIncrementModule)autoincrSelector.select(column.mode); } if (autoincr != null) { if (autoincr.includeInQuery()) { if (autoincr.includeAsValue()) { Object value = autoincr.getPreValue(table, column.columnConf, column.modeConf, conn, objectModel); this.setColumn(objectModel, outputMode, results, table, column.columnConf, rowIndex, value, statement, currentIndex); columnCount = 1; } } else { if (getLogger().isDebugEnabled()) { getLogger().debug("Automatically setting key"); } } } else if (getLogger().isWarnEnabled()) { getLogger().warn("Could not select autoincrement module" + outputMode + ". Defaulting to automatically setting key."); } } finally { if (autoincrSelector != null) { if (autoincr != null) { autoincrSelector.release(autoincr); } this.manager.release(autoincrSelector); } } return columnCount; } /** * Put key values into request attributes. Checks whether the * value needs to be retrieved from the database module first. * */ protected void storeKeyValue(Configuration tableConf, Column key, int rowIndex, Connection conn, Statement statement, Map objectModel, String outputMode, Map results) throws SQLException, ConfigurationException, ServiceException { ServiceSelector autoincrSelector = null; AutoIncrementModule autoincr = null; try { autoincrSelector = (ServiceSelector)this.manager.lookup(DATABASE_MODULE_SELECTOR); if (key.mode != null && autoincrSelector != null && autoincrSelector.isSelectable(key.mode)){ autoincr = (AutoIncrementModule)autoincrSelector.select(key.mode); } if (autoincr != null) { if (!autoincr.includeAsValue()) { Object value = autoincr.getPostValue(tableConf, key.columnConf, key.modeConf, conn, statement, objectModel); this.setOutput(objectModel, outputMode, results, tableConf, key.columnConf, rowIndex, value); } } else { throw new ConfigurationException("Could not obtain key value"); } } finally { if (autoincrSelector != null) { if (autoincr != null) { autoincrSelector.release(autoincr); } this.manager.release(autoincrSelector); } } } /** * determine which mode to use as default mode * here: INSERT * highly specific to operation INSERT / UPDATE / DELETE / SELECT */ protected String selectMode ( boolean isAutoIncrement, Map modes ) { if ( isAutoIncrement ) return (String) modes.get( MODE_AUTOINCR ); else 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 true; } /** * 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; } /** * 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[] values = table.getChild("values").getChildren("value"); Configuration[] keys = table.getChild("keys").getChildren("key"); 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("INSERT INTO "); StringBuffer valueBuffer = new StringBuffer(") VALUES ("); queryBuffer.append(table.getAttribute("name")); queryBuffer.append(" ("); int actualColumns = 0; for (int i = 0; i < queryData.columns.length; i++) { if ( actualColumns > 0 ) { queryBuffer.append( ", " ); valueBuffer.append( ", " ); } if ( queryData.columns[i].isKey && queryData.columns[i].isAutoIncrement ) { ServiceSelector autoincrSelector = null; AutoIncrementModule autoincr = null; try { autoincrSelector = (ServiceSelector) this.manager.lookup(DATABASE_MODULE_SELECTOR); if (queryData.columns[i].mode != null && autoincrSelector != null && autoincrSelector.isSelectable(queryData.columns[i].mode)){ autoincr = (AutoIncrementModule) autoincrSelector.select(queryData.columns[i].mode); if ( autoincr.includeInQuery() ) { actualColumns++; queryBuffer.append( queryData.columns[i].columnConf.getAttribute( "name" ) ); if ( autoincr.includeAsValue() ) { valueBuffer.append( "?" ); } else { valueBuffer.append( autoincr.getSubquery( table, queryData.columns[i].columnConf, queryData.columns[i].modeConf ) ); } } } else { if (getLogger().isErrorEnabled()) getLogger().error("Could not find mode description " + queryData.columns[i].mode + " for column #"+i); if (getLogger().isDebugEnabled()) { getLogger().debug("Column data "+queryData.columns[i]); } throw new ConfigurationException("Could not find mode description "+queryData.columns[i].mode+" for column "+i); } } finally { if (autoincrSelector != null) { if (autoincr != null) autoincrSelector.release(autoincr); this.manager.release(autoincrSelector); } } } else { actualColumns++; queryBuffer.append( queryData.columns[i].columnConf.getAttribute( "name" ) ); valueBuffer.append( "?" ); } } valueBuffer.append(")"); queryBuffer.append(valueBuffer); queryData.queryString = queryBuffer.toString(); this.cachedQueryData.put( lookUpKey, queryData ); } } return queryData; } }