org.datanucleus.store.rdbms.request.UpdateRequest Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-rdbms Show documentation
Show all versions of datanucleus-rdbms Show documentation
Plugin for DataNucleus providing persistence to RDBMS datastores.
/**********************************************************************
Copyright (c) 2002 Mike Martin (TJDO) and others. All rights reserved.
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.
Contributors:
2003 Erik Bengtson - optimistic transaction support
2004 Andy Jefferson - coding standards
2004 Andy Jefferson - conversion to use Logger
2005 Andy Jefferson - added handling for updating FK in related object
2006 Andy Jefferson - changed to extend VersionCheckRequest
...
**********************************************************************/
package org.datanucleus.store.rdbms.request;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.exceptions.NotYetFlushedException;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusOptimisticException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.MetaData;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.VersionStrategy;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.fieldmanager.FieldManager;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.SQLController;
import org.datanucleus.store.rdbms.fieldmanager.OldValueParameterSetter;
import org.datanucleus.store.rdbms.fieldmanager.ParameterSetter;
import org.datanucleus.store.rdbms.identifier.DatastoreIdentifier;
import org.datanucleus.store.rdbms.mapping.MappingCallbacks;
import org.datanucleus.store.rdbms.mapping.MappingConsumer;
import org.datanucleus.store.rdbms.mapping.MappingType;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.query.StatementClassMapping;
import org.datanucleus.store.rdbms.query.StatementMappingIndex;
import org.datanucleus.store.rdbms.table.Column;
import org.datanucleus.store.rdbms.table.DatastoreClass;
import org.datanucleus.store.schema.table.SurrogateColumnType;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* Class to provide a means of update of particular fields of a particular type in an RDBMS.
* Extends basic request class implementing the execute method to do a JDBC update operation.
* The SQL will be of the form
*
* UPDATE table-name SET param1=?, param2=?[, version=?] WHERE id1=? AND id2=?
*
* or (when also performing version checks)
*
* UPDATE table-name SET param1=?, param2=?, version={newvers} WHERE id1=? AND id2=? AND version={oldvers}
*
*/
public class UpdateRequest extends Request
{
/** SQL statement for the update. */
private final String updateStmt;
/** SQL statement for the update when using optimistic txns. */
private final String updateStmtOptimistic;
/** callback mappings will have their postUpdate method called after the update */
private final MappingCallbacks[] callbacks;
/** the index for the expression in the update sql statement. */
private StatementMappingDefinition stmtMappingDefinition;
/** Numbers of all fields to be updated (except PK). */
private final int[] updateFieldNumbers;
/** Numbers of WHERE clause fields. */
private final int[] whereFieldNumbers;
/** MetaData for the class. */
protected AbstractClassMetaData cmd = null;
/** MetaData for the version handling. */
protected VersionMetaData versionMetaData = null;
/** Whether we should make checks on optimistic version before updating. */
protected boolean versionChecks = false;
/**
* Constructor, taking the table. Uses the structure of the datastore table to build a basic query.
* @param table The Class Table representing the datastore table to update
* @param reqFieldMetaData MetaData of the fields to update
* @param cmd ClassMetaData of objects being updated
* @param clr ClassLoader resolver
*/
public UpdateRequest(DatastoreClass table, AbstractMemberMetaData[] reqFieldMetaData, AbstractClassMetaData cmd, ClassLoaderResolver clr)
{
super(table);
this.cmd = cmd;
versionMetaData = table.getVersionMetaData();
if (versionMetaData != null && versionMetaData.getVersionStrategy() != VersionStrategy.NONE)
{
// Only apply a version check if we have a strategy defined
versionChecks = true;
}
// Set up the basic mapping information
stmtMappingDefinition = new StatementMappingDefinition(); // Populated using the subsequent lines
UpdateMappingConsumer consumer = new UpdateMappingConsumer(cmd);
// Fields to update
if (versionMetaData != null)
{
if (versionMetaData.getFieldName() != null)
{
// Version field
// TODO If the passed fields arent included in the statement (e.g SCO collection) update version?
int numUpdateFields = reqFieldMetaData.length;
boolean includesVersion = false;
for (int i=0;i 0)
{
fieldStr.append(",");
}
fieldStr.append(cmd.getMetaDataForManagedMemberAtAbsolutePosition(updateFieldNumbers[i]).getName());
}
}
if (versionMetaData != null && versionMetaData.getFieldName() == null)
{
if (fieldStr.length() > 0)
{
fieldStr.append(",");
}
fieldStr.append("[VERSION]");
}
// Debug information about what we are updating
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052214", op.getObjectAsPrintable(), fieldStr.toString(), table));
}
RDBMSStoreManager storeMgr = table.getStoreManager();
boolean batch = false;
// TODO Set the batch flag based on whether we have no other SQL being invoked in here just our UPDATE
try
{
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try
{
// Perform the update
PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, batch);
try
{
Object currentVersion = op.getTransactionalVersion();
Object nextVersion = null;
if (versionMetaData != null) // TODO What if strategy is NONE?
{
// Set the next version in the object
if (versionMetaData.getFieldName() != null)
{
// Version field
AbstractMemberMetaData verfmd = cmd.getMetaDataForMember(table.getVersionMetaData().getFieldName());
if (currentVersion instanceof Number)
{
// Cater for Integer-based versions
currentVersion = Long.valueOf(((Number)currentVersion).longValue());
}
nextVersion = ec.getLockManager().getNextVersion(versionMetaData, currentVersion);
if (verfmd.getType() == Integer.class || verfmd.getType() == int.class)
{
// Cater for Integer-based versions
nextVersion = Integer.valueOf(((Number)nextVersion).intValue());
}
op.replaceField(verfmd.getAbsoluteFieldNumber(), nextVersion);
}
else
{
// Surrogate version column
nextVersion = ec.getLockManager().getNextVersion(versionMetaData, currentVersion);
}
op.setTransactionalVersion(nextVersion);
}
// SELECT clause - set the required fields to be updated
if (updateFieldNumbers != null)
{
StatementClassMapping mappingDefinition = new StatementClassMapping();
StatementMappingIndex[] idxs = stmtMappingDefinition.getUpdateFields();
for (int i=0;i exceptions = new ArrayList<>();
exceptions.add(e);
while((e = e.getNextException())!=null)
{
exceptions.add(e);
}
throw new NucleusDataStoreException(msg, exceptions.toArray(new Throwable[exceptions.size()]));
}
}
// Execute any mapping actions now that we have done the update
for (int i=0; i
* UPDATE table-name SET param1=?,param2=? WHERE id1=? AND id2=?
*
* or (when also performing version checks)
* * UPDATE table-name SET param1=?,param2=?,version={newvers} WHERE id1=? AND id2=? AND version={oldvers} **/ private class UpdateMappingConsumer implements MappingConsumer { /** Flag for initialisation state of the consumer. */ boolean initialized = false; /** Current parameter index. */ int paramIndex = 1; /** Numbers of all fields to be updated. */ List updateFields = new ArrayList(); /** Numbers of all WHERE clause fields. */ List whereFields = new ArrayList(); List mc = new ArrayList(); /** for UPDATE statement **/ StringBuilder columnAssignments = new StringBuilder(); Map assignedColumns = new HashMap(); /** Where clause for the statement. Built during the consumption process. */ StringBuilder where = new StringBuilder(); /** MetaData for the class of the object */ private final AbstractClassMetaData cmd; private boolean whereClauseConsumption = false; /** * Constructor * @param cmd metadata for the class */ public UpdateMappingConsumer(AbstractClassMetaData cmd) { super(); this.cmd = cmd; } public void setWhereClauseConsumption(boolean whereClause) { this.whereClauseConsumption = whereClause; } public void preConsumeMapping(int highest) { if (!initialized) { stmtMappingDefinition.setWhereFields(new StatementMappingIndex[highest]); stmtMappingDefinition.setUpdateFields(new StatementMappingIndex[highest]); initialized = true; } } /** * Consumes a mapping for a field. * @param m The mapping. * @param mmd MetaData for the field */ public void consumeMapping(JavaTypeMapping m, AbstractMemberMetaData mmd) { if (!mmd.getAbstractClassMetaData().isSameOrAncestorOf(cmd)) { return; } if (m.includeInUpdateStatement()) { // Check if the field is "updatable" (either using JPA column, or JDO extension) if (mmd.hasExtension(MetaData.EXTENSION_MEMBER_UPDATEABLE) && mmd.getValueForExtension(MetaData.EXTENSION_MEMBER_UPDATEABLE).equalsIgnoreCase("false")) { return; } ColumnMetaData[] colmds = mmd.getColumnMetaData(); if (colmds != null && colmds.length > 0) { for (int i=0;i
* UPDATE TABLE SET COL1 = ?, COL2 = ? WHERE COL3 = ? AND COL4 = ? ** @return The update SQL statement */ public String getStatement() { if (columnAssignments.length() < 1) { return null; } return "UPDATE " + table.toString() + " SET " + columnAssignments + " WHERE " + where; } } }
© 2015 - 2024 Weber Informatics LLC | Privacy Policy