use of org.datanucleus.store.rdbms.table.AbstractClassTable in project datanucleus-rdbms by datanucleus.
the class InsertRequest method execute.
/**
* Method performing the insertion of the record from the datastore.
* Takes the constructed insert query and populates with the specific record information.
* @param sm StateManager for the record to be inserted
*/
public void execute(DNStateManager sm) {
ExecutionContext ec = sm.getExecutionContext();
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
// Debug information about what we are inserting
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052207", sm.getObjectAsPrintable(), table));
}
try {
VersionMetaData vermd = table.getVersionMetaData();
RDBMSStoreManager storeMgr = table.getStoreManager();
if (vermd != null && vermd.getMemberName() != null) {
// Version field - Update the version in the object
AbstractMemberMetaData verfmd = ((AbstractClassMetaData) vermd.getParent()).getMetaDataForMember(vermd.getMemberName());
Object currentVersion = sm.getVersion();
if (currentVersion instanceof Number) {
// Cater for Integer based versions
currentVersion = Long.valueOf(((Number) currentVersion).longValue());
}
Object nextOptimisticVersion = ec.getLockManager().getNextVersion(vermd, currentVersion);
if (verfmd.getType() == Integer.class || verfmd.getType() == int.class) {
// Cater for Integer based versions
nextOptimisticVersion = Integer.valueOf(((Number) nextOptimisticVersion).intValue());
}
sm.replaceField(verfmd.getAbsoluteFieldNumber(), nextOptimisticVersion);
}
// Set the state to "inserting" (may already be at this state if multiple inheritance level INSERT)
sm.setInserting();
// sm.changeActivityState(ActivityState.INSERTING);
SQLController sqlControl = storeMgr.getSQLController();
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
try {
List<String> pkColumnNames = new ArrayList<>();
if (table.getIdentityType() == IdentityType.DATASTORE) {
JavaTypeMapping mapping = table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, true);
ColumnMapping[] columnMappings = mapping.getColumnMappings();
pkColumnNames = Stream.of(columnMappings).map(cm -> cm.getColumn().getIdentifier().getName()).collect(toList());
} else if (table.getIdentityType() == IdentityType.APPLICATION) {
List<Column> pkColumns = ((AbstractClassTable) table).getPrimaryKey().getColumns();
if (!pkColumns.isEmpty()) {
pkColumnNames = pkColumns.stream().map(cm -> cm.getName()).collect(toList());
}
}
PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, insertStmt, batch, hasIdentityColumn && storeMgr.getDatastoreAdapter().supportsOption(DatastoreAdapter.GET_GENERATED_KEYS_STATEMENT), pkColumnNames);
try {
StatementClassMapping mappingDefinition = new StatementClassMapping();
for (int i = 0; i < stmtMappings.length; i++) {
if (stmtMappings[i] != null) {
mappingDefinition.addMappingForMember(i, stmtMappings[i]);
}
}
// Provide the primary key field(s)
if (table.getIdentityType() == IdentityType.DATASTORE) {
if (!table.isObjectIdDatastoreAttributed() || !table.isBaseDatastoreClass()) {
int[] paramNumber = { IDPARAMNUMBER };
table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false).setObject(ec, ps, paramNumber, sm.getInternalObjectId());
}
} else if (table.getIdentityType() == IdentityType.APPLICATION) {
if (pkFieldNumbers != null && pkFieldNumbers.length > 0) {
sm.provideFields(pkFieldNumbers, new ParameterSetter(sm, ps, mappingDefinition));
}
}
// Provide all non-key fields needed for the insert - provides "persistence-by-reachability" for these fields
if (insertFieldNumbers.length > 0) {
AbstractClassMetaData cmd = sm.getClassMetaData();
if (createTimestampStmtMapping != null) {
// Set create timestamp to time for the start of this transaction
int createTimestampMemberPos = cmd.getCreateTimestampMemberPosition();
AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(createTimestampMemberPos);
if (mmd.getType().isAssignableFrom(java.time.Instant.class)) {
sm.replaceField(createTimestampMemberPos, ec.getTransaction().getIsActive() ? java.time.Instant.ofEpochMilli(ec.getTransaction().getBeginTime()) : java.time.Instant.now());
} else {
sm.replaceField(createTimestampMemberPos, ec.getTransaction().getIsActive() ? new Timestamp(ec.getTransaction().getBeginTime()) : new Timestamp(System.currentTimeMillis()));
}
}
if (createUserStmtMapping != null) {
// Set create user to current user
sm.replaceField(cmd.getCreateUserMemberPosition(), ec.getCurrentUser());
}
int numberOfFieldsToProvide = 0;
int numMembers = cmd.getMemberCount();
for (int i = 0; i < insertFieldNumbers.length; i++) {
if (insertFieldNumbers[i] < numMembers) {
numberOfFieldsToProvide++;
}
}
int j = 0;
int[] fieldNums = new int[numberOfFieldsToProvide];
for (int i = 0; i < insertFieldNumbers.length; i++) {
if (insertFieldNumbers[i] < numMembers) {
fieldNums[j++] = insertFieldNumbers[i];
}
}
sm.provideFields(fieldNums, new ParameterSetter(sm, ps, mappingDefinition));
}
JavaTypeMapping versionMapping = table.getSurrogateMapping(SurrogateColumnType.VERSION, false);
if (versionMapping != null) {
// Surrogate version - set the new version for the object
Object currentVersion = sm.getVersion();
Object nextOptimisticVersion = ec.getLockManager().getNextVersion(vermd, currentVersion);
for (int k = 0; k < versionStmtMapping.getNumberOfParameterOccurrences(); k++) {
versionMapping.setObject(ec, ps, versionStmtMapping.getParameterPositionsForOccurrence(k), nextOptimisticVersion);
}
sm.setTransactionalVersion(nextOptimisticVersion);
} else if (vermd != null && vermd.getMemberName() != null) {
// Version field - set the new version for the object
Object currentVersion = sm.getVersion();
Object nextOptimisticVersion = ec.getLockManager().getNextVersion(vermd, currentVersion);
sm.setTransactionalVersion(nextOptimisticVersion);
}
if (multitenancyStmtMapping != null) {
// Multitenancy mapping
String tenantId = ec.getTenantId();
if (tenantId == null) {
NucleusLogger.PERSISTENCE.warn("Insert of object with multitenancy column but tenantId not set! Suggest that you set it.");
}
table.getSurrogateMapping(SurrogateColumnType.MULTITENANCY, false).setObject(ec, ps, multitenancyStmtMapping.getParameterPositionsForOccurrence(0), tenantId);
}
if (softDeleteStmtMapping != null) {
// Soft-Delete mapping
table.getSurrogateMapping(SurrogateColumnType.SOFTDELETE, false).setObject(ec, ps, softDeleteStmtMapping.getParameterPositionsForOccurrence(0), Boolean.FALSE);
}
if (createUserStmtMapping != null) {
table.getSurrogateMapping(SurrogateColumnType.CREATE_USER, false).setObject(ec, ps, createUserStmtMapping.getParameterPositionsForOccurrence(0), ec.getCurrentUser());
}
if (createTimestampStmtMapping != null) {
table.getSurrogateMapping(SurrogateColumnType.CREATE_TIMESTAMP, false).setObject(ec, ps, createTimestampStmtMapping.getParameterPositionsForOccurrence(0), new Timestamp(ec.getTransaction().getIsActive() ? ec.getTransaction().getBeginTime() : System.currentTimeMillis()));
}
if (updateUserStmtMapping != null) {
// TODO Do we need to specify this on INSERT? can they be nullable?
table.getSurrogateMapping(SurrogateColumnType.UPDATE_USER, false).setObject(ec, ps, updateUserStmtMapping.getParameterPositionsForOccurrence(0), "");
}
if (updateTimestampStmtMapping != null) {
// TODO Do we need to specify this on INSERT? can they be nullable?
table.getSurrogateMapping(SurrogateColumnType.UPDATE_TIMESTAMP, false).setObject(ec, ps, updateTimestampStmtMapping.getParameterPositionsForOccurrence(0), null);
}
JavaTypeMapping discrimMapping = table.getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
if (discrimMapping != null) {
// Discriminator mapping
Object discVal = sm.getClassMetaData().getDiscriminatorValue();
for (int k = 0; k < discriminatorStmtMapping.getNumberOfParameterOccurrences(); k++) {
discrimMapping.setObject(ec, ps, discriminatorStmtMapping.getParameterPositionsForOccurrence(k), discVal);
}
}
// External FK columns (optional)
if (externalFKStmtMappings != null) {
for (int i = 0; i < externalFKStmtMappings.length; i++) {
Object fkValue = sm.getAssociatedValue(externalFKStmtMappings[i].getMapping());
if (fkValue != null) {
// Need to provide the owner field number so PCMapping can work out if it is inserted yet
AbstractMemberMetaData ownerFmd = table.getMetaDataForExternalMapping(externalFKStmtMappings[i].getMapping(), MappingType.EXTERNAL_FK);
for (int k = 0; k < externalFKStmtMappings[i].getNumberOfParameterOccurrences(); k++) {
externalFKStmtMappings[i].getMapping().setObject(ec, ps, externalFKStmtMappings[i].getParameterPositionsForOccurrence(k), fkValue, null, ownerFmd.getAbsoluteFieldNumber());
}
} else {
// We're inserting a null so don't need the owner field
for (int k = 0; k < externalFKStmtMappings[i].getNumberOfParameterOccurrences(); k++) {
externalFKStmtMappings[i].getMapping().setObject(ec, ps, externalFKStmtMappings[i].getParameterPositionsForOccurrence(k), null);
}
}
}
}
// External FK discriminator columns (optional)
if (externalFKDiscrimStmtMappings != null) {
for (int i = 0; i < externalFKDiscrimStmtMappings.length; i++) {
Object discrimValue = sm.getAssociatedValue(externalFKDiscrimStmtMappings[i].getMapping());
for (int k = 0; k < externalFKDiscrimStmtMappings[i].getNumberOfParameterOccurrences(); k++) {
externalFKDiscrimStmtMappings[i].getMapping().setObject(ec, ps, externalFKDiscrimStmtMappings[i].getParameterPositionsForOccurrence(k), discrimValue);
}
}
}
// External order columns (optional)
if (externalOrderStmtMappings != null) {
for (int i = 0; i < externalOrderStmtMappings.length; i++) {
Object orderValue = sm.getAssociatedValue(externalOrderStmtMappings[i].getMapping());
if (orderValue == null) {
// No order value so use -1
orderValue = Integer.valueOf(-1);
}
for (int k = 0; k < externalOrderStmtMappings[i].getNumberOfParameterOccurrences(); k++) {
externalOrderStmtMappings[i].getMapping().setObject(ec, ps, externalOrderStmtMappings[i].getParameterPositionsForOccurrence(k), orderValue);
}
}
}
sqlControl.executeStatementUpdate(ec, mconn, insertStmt, ps, !batch);
if (hasIdentityColumn) {
// Identity column was set in the datastore using auto-increment/identity/serial etc
Object newId = getInsertedIdentityValue(ec, sqlControl, sm, mconn, ps);
sm.setPostStoreNewObjectId(newId);
if (NucleusLogger.DATASTORE_PERSIST.isDebugEnabled()) {
NucleusLogger.DATASTORE_PERSIST.debug(Localiser.msg("052206", sm.getObjectAsPrintable(), IdentityUtils.getPersistableIdentityForId(sm.getInternalObjectId())));
}
}
// Execute any mapping actions on the insert of the fields (e.g Oracle CLOBs/BLOBs)
if (postSetMappings != null) {
for (JavaTypeMapping m : postSetMappings) {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052222", sm.getObjectAsPrintable(), m.getMemberMetaData().getFullFieldName()));
}
m.performSetPostProcessing(sm);
}
}
// Update the insert status for this table via the StoreManager
storeMgr.setObjectIsInsertedToLevel(sm, table);
// (if we did it the other way around we would get a NotYetFlushedException thrown above).
for (int i = 0; i < relationFieldNumbers.length; i++) {
Object value = sm.provideField(relationFieldNumbers[i]);
if (value != null && ec.getApiAdapter().isDetached(value)) {
Object valueAttached = ec.persistObjectInternal(value, null, -1, PersistableObjectType.PC);
sm.replaceField(relationFieldNumbers[i], valueAttached);
}
}
// Perform reachability on all fields that have no datastore column (1-1 bi non-owner, N-1 bi join)
if (reachableFieldNumbers.length > 0) {
int numberOfReachableFields = 0;
for (int i = 0; i < reachableFieldNumbers.length; i++) {
if (reachableFieldNumbers[i] < sm.getClassMetaData().getMemberCount()) {
numberOfReachableFields++;
}
}
int[] fieldNums = new int[numberOfReachableFields];
int j = 0;
for (int i = 0; i < reachableFieldNumbers.length; i++) {
if (reachableFieldNumbers[i] < sm.getClassMetaData().getMemberCount()) {
fieldNums[j++] = reachableFieldNumbers[i];
}
}
mappingDefinition = new StatementClassMapping();
for (int i = 0; i < retrievedStmtMappings.length; i++) {
if (retrievedStmtMappings[i] != null) {
mappingDefinition.addMappingForMember(i, retrievedStmtMappings[i]);
}
}
NucleusLogger.PERSISTENCE.debug("Performing reachability on fields " + StringUtils.intArrayToString(fieldNums));
sm.provideFields(fieldNums, new ParameterSetter(sm, ps, mappingDefinition));
}
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException e) {
String msg = Localiser.msg("052208", sm.getObjectAsPrintable(), insertStmt, e.getMessage());
NucleusLogger.DATASTORE_PERSIST.warn(msg);
List<Exception> 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 inserted the element (things like inserting any association parent-child).
if (mappingCallbacks != null) {
for (MappingCallbacks m : mappingCallbacks) {
try {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052209", IdentityUtils.getPersistableIdentityForId(sm.getInternalObjectId()), ((JavaTypeMapping) m).getMemberMetaData().getFullFieldName()));
}
m.postInsert(sm);
} catch (NotYetFlushedException e) {
sm.updateFieldAfterInsert(e.getPersistable(), ((JavaTypeMapping) m).getMemberMetaData().getAbsoluteFieldNumber());
}
}
}
}
Aggregations