blob: 8d450ea13687c481e9be78e9c9f68ba69acd8f12 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed 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: DatabaseAddAction.java,v 1.5 2004/03/05 13:01:52 bdelacretaz Exp $
*/
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.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" );
}
} 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.includeAsValue()) {
Object value = autoincr.getPostValue( tableConf, key.columnConf, key.modeConf, conn, statement, objectModel );
this.setOutput(objectModel, outputMode, results, tableConf, key.columnConf, rowIndex, 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;
}
}