use of org.datanucleus.metadata.AbstractMemberMetaData in project datanucleus-rdbms by datanucleus.
the class JoinListStore method internalAdd.
/**
* Internal method to add element(s) to the List.
* Performs the add in 2 steps.
* <ol>
* <li>Shift all existing elements into their new positions so we can insert.</li>
* <li>Insert all new elements directly at their desired positions</li>
* </ol>
* Both steps can be batched (separately).
* @param op The ObjectProvider
* @param start The start location (if required)
* @param atEnd Whether to add the element at the end
* @param c The collection of objects to add.
* @param size Current size of list if known. -1 if not known
* @return Whether it was successful
*/
protected boolean internalAdd(ObjectProvider op, int start, boolean atEnd, Collection<E> c, int size) {
if (c == null || c.size() == 0) {
return true;
}
if (relationType == RelationType.MANY_TO_MANY_BI && ownerMemberMetaData.getMappedBy() != null) {
// M-N non-owner : don't add from this side to avoid duplicates
return true;
}
// Calculate the amount we need to shift any existing elements by
// This is used where inserting between existing elements and have to shift down all elements after the start point
int shift = c.size();
// check all elements are valid for persisting and exist (persistence-by-reachability)
ExecutionContext ec = op.getExecutionContext();
Iterator iter = c.iterator();
while (iter.hasNext()) {
Object element = iter.next();
validateElementForWriting(ec, element, null);
if (relationType == RelationType.ONE_TO_MANY_BI) {
// TODO This is ManagedRelations - move into RelationshipManager
ObjectProvider elementOP = ec.findObjectProvider(element);
if (elementOP != null) {
AbstractMemberMetaData[] relatedMmds = ownerMemberMetaData.getRelatedMemberMetaData(clr);
// TODO Cater for more than 1 related field
Object elementOwner = elementOP.provideField(relatedMmds[0].getAbsoluteFieldNumber());
if (elementOwner == null) {
// No owner, so correct it
NucleusLogger.PERSISTENCE.info(Localiser.msg("056037", op.getObjectAsPrintable(), ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(elementOP.getObject())));
elementOP.replaceField(relatedMmds[0].getAbsoluteFieldNumber(), op.getObject());
} else if (elementOwner != op.getObject() && op.getReferencedPC() == null) {
// Inconsistent owner, so throw exception
throw new NucleusUserException(Localiser.msg("056038", op.getObjectAsPrintable(), ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(elementOP.getObject()), StringUtils.toJVMIDString(elementOwner)));
}
}
}
}
// Check what we have persistent already
int currentListSize = 0;
if (size < 0) {
// Get the current size from the datastore
currentListSize = size(op);
} else {
currentListSize = size;
}
// Check for dynamic schema updates prior to addition
if (storeMgr.getBooleanObjectProperty(RDBMSPropertyNames.PROPERTY_RDBMS_DYNAMIC_SCHEMA_UPDATES).booleanValue()) {
DynamicSchemaFieldManager dynamicSchemaFM = new DynamicSchemaFieldManager(storeMgr, op);
dynamicSchemaFM.storeObjectField(getOwnerMemberMetaData().getAbsoluteFieldNumber(), c);
if (dynamicSchemaFM.hasPerformedSchemaUpdates()) {
invalidateAddStmt();
}
}
String addStmt = getAddStmtForJoinTable();
try {
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
// Shift any existing elements so that we can insert the new element(s) at their position
if (!atEnd && start != currentListSize) {
boolean batched = currentListSize - start > 0;
for (int i = currentListSize - 1; i >= start; i--) {
// Shift the index for this row by "shift"
internalShift(op, mconn, batched, i, shift, (i == start));
}
} else {
start = currentListSize;
}
// Insert the elements at their required location
int jdbcPosition = 1;
boolean batched = (c.size() > 1);
Iterator elemIter = c.iterator();
while (elemIter.hasNext()) {
Object element = elemIter.next();
PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, addStmt, batched);
try {
jdbcPosition = 1;
jdbcPosition = BackingStoreHelper.populateOwnerInStatement(op, ec, ps, jdbcPosition, this);
jdbcPosition = BackingStoreHelper.populateElementInStatement(ec, ps, element, jdbcPosition, elementMapping);
if (orderMapping != null) {
jdbcPosition = BackingStoreHelper.populateOrderInStatement(ec, ps, start, jdbcPosition, orderMapping);
}
if (relationDiscriminatorMapping != null) {
jdbcPosition = BackingStoreHelper.populateRelationDiscriminatorInStatement(ec, ps, jdbcPosition, this);
}
start++;
// Execute the statement
sqlControl.executeStatementUpdate(ec, mconn, addStmt, ps, !iter.hasNext());
} finally {
sqlControl.closeStatement(mconn, ps);
}
}
} finally {
mconn.release();
}
} catch (SQLException | MappedDatastoreException e) {
throw new NucleusDataStoreException(Localiser.msg("056009", addStmt), e);
}
return true;
}
use of org.datanucleus.metadata.AbstractMemberMetaData in project datanucleus-rdbms by datanucleus.
the class JoinSetStore method elementAlreadyContainsOwnerInMtoN.
/**
* Convenience method to check if an element already refers to the owner in an M-N relation (i.e added from other side).
* @param ownerOP ObjectProvider of the owner
* @param element The element
* @return Whether the element contains the owner
*/
private boolean elementAlreadyContainsOwnerInMtoN(ObjectProvider ownerOP, Object element) {
ExecutionContext ec = ownerOP.getExecutionContext();
if (ec.getOperationQueue() != null) {
// TODO This means we always do a "SELECT 1 FROM JOINTABLE" for every addition when optimistic. Should seek to avoid this by updates to operationQueue maybe
if (locate(ownerOP, element)) {
NucleusLogger.DATASTORE.info(Localiser.msg("056040", ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(ownerOP.getObject()), element));
return true;
}
return false;
}
ObjectProvider elementOP = ec.findObjectProvider(element);
if (elementOP != null) {
// Check the collection at the other side whether already added (to avoid the locate call)
AbstractMemberMetaData[] relatedMmds = ownerMemberMetaData.getRelatedMemberMetaData(ec.getClassLoaderResolver());
Object elementColl = elementOP.provideField(relatedMmds[0].getAbsoluteFieldNumber());
if (elementColl != null && elementColl instanceof Collection && elementColl instanceof SCO && ((Collection) elementColl).contains(ownerOP.getObject())) {
NucleusLogger.DATASTORE.info(Localiser.msg("056040", ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(ownerOP.getObject()), element));
return true;
}
} else {
// Element is still detached
if (locate(ownerOP, element)) {
NucleusLogger.DATASTORE.info(Localiser.msg("056040", ownerMemberMetaData.getFullFieldName(), StringUtils.toJVMIDString(ownerOP.getObject()), element));
return true;
}
}
return false;
}
use of org.datanucleus.metadata.AbstractMemberMetaData in project datanucleus-rdbms by datanucleus.
the class SQLStatementHelper method getValueForPrimaryKeyIndexOfObjectUsingReflection.
/**
* Convenience method to use reflection to extract the value of a PK field of the provided object.
* @param value The value of the overall object
* @param index Index of the PK field whose value we need to return
* @param cmd Metadata for the class
* @param storeMgr Store manager
* @param clr ClassLoader resolver
* @return Value of this index of the PK field
*/
public static Object getValueForPrimaryKeyIndexOfObjectUsingReflection(Object value, int index, AbstractClassMetaData cmd, RDBMSStoreManager storeMgr, ClassLoaderResolver clr) {
if (cmd.getIdentityType() == IdentityType.DATASTORE) {
throw new NucleusException("This method does not support datastore-identity");
}
int position = 0;
int[] pkPositions = cmd.getPKMemberPositions();
for (int pkPosition : pkPositions) {
AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(pkPosition);
Object memberValue = null;
if (mmd instanceof FieldMetaData) {
memberValue = ClassUtils.getValueOfFieldByReflection(value, mmd.getName());
} else {
memberValue = ClassUtils.getValueOfMethodByReflection(value, ClassUtils.getJavaBeanGetterName(mmd.getName(), false));
}
if (storeMgr.getApiAdapter().isPersistable(mmd.getType())) {
// Compound PK field, so recurse
// TODO Cater for cases without own table ("subclass-table")
AbstractClassMetaData subCmd = storeMgr.getMetaDataManager().getMetaDataForClass(mmd.getType(), clr);
DatastoreClass subTable = storeMgr.getDatastoreClass(mmd.getTypeName(), clr);
JavaTypeMapping subMapping = subTable.getIdMapping();
Object subValue = getValueForPrimaryKeyIndexOfObjectUsingReflection(memberValue, index - position, subCmd, storeMgr, clr);
if (index < position + subMapping.getNumberOfDatastoreMappings()) {
return subValue;
}
position = position + subMapping.getNumberOfDatastoreMappings();
} else {
// Normal PK field
if (position == index) {
if (mmd instanceof FieldMetaData) {
return ClassUtils.getValueOfFieldByReflection(value, mmd.getName());
}
return ClassUtils.getValueOfMethodByReflection(value, ClassUtils.getJavaBeanGetterName(mmd.getName(), false));
}
position++;
}
}
return null;
}
use of org.datanucleus.metadata.AbstractMemberMetaData in project datanucleus-rdbms by datanucleus.
the class UpdateRequest method execute.
/**
* Method performing the update of the record in the datastore.
* Takes the constructed update query and populates with the specific record information.
* @param op The ObjectProvider for the record to be updated
*/
public void execute(ObjectProvider op) {
// Choose the statement based on whether optimistic or not
String stmt = null;
ExecutionContext ec = op.getExecutionContext();
boolean optimisticChecks = (versionMetaData != null && ec.getTransaction().getOptimistic() && versionChecks);
stmt = optimisticChecks ? updateStmtOptimistic : updateStmt;
if (stmt != null) {
// TODO Support surrogate update user/timestamp
AbstractMemberMetaData[] mmds = cmd.getManagedMembers();
for (int i = 0; i < mmds.length; i++) {
if (// TODO Make this accessible from cmd
mmds[i].isUpdateTimestamp()) {
op.replaceField(mmds[i].getAbsoluteFieldNumber(), new Timestamp(ec.getTransaction().getIsActive() ? ec.getTransaction().getBeginTime() : System.currentTimeMillis()));
} else if (// TODO Make this accessible from cmd
mmds[i].isUpdateUser()) {
op.replaceField(mmds[i].getAbsoluteFieldNumber(), ec.getNucleusContext().getCurrentUser(ec));
}
}
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
// Debug info about fields being updated
StringBuilder fieldStr = new StringBuilder();
if (updateFieldNumbers != null) {
for (int i = 0; i < updateFieldNumbers.length; i++) {
if (fieldStr.length() > 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 (// TODO What if strategy is NONE?
versionMetaData != null) {
// 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 < idxs.length; i++) {
if (idxs[i] != null) {
mappingDefinition.addMappingForMember(i, idxs[i]);
}
}
op.provideFields(updateFieldNumbers, new ParameterSetter(op, ps, mappingDefinition));
}
if (versionMetaData != null && versionMetaData.getFieldName() == null) {
// SELECT clause - set the surrogate version column to the new version
StatementMappingIndex mapIdx = stmtMappingDefinition.getUpdateVersion();
for (int i = 0; i < mapIdx.getNumberOfParameterOccurrences(); i++) {
table.getSurrogateMapping(SurrogateColumnType.VERSION, false).setObject(ec, ps, mapIdx.getParameterPositionsForOccurrence(i), nextVersion);
}
}
// WHERE clause - primary key fields
if (table.getIdentityType() == IdentityType.DATASTORE) {
// a). datastore identity
StatementMappingIndex mapIdx = stmtMappingDefinition.getWhereDatastoreId();
for (int i = 0; i < mapIdx.getNumberOfParameterOccurrences(); i++) {
table.getSurrogateMapping(SurrogateColumnType.DATASTORE_ID, false).setObject(ec, ps, mapIdx.getParameterPositionsForOccurrence(i), op.getInternalObjectId());
}
} else {
// b). application/nondurable identity
StatementClassMapping mappingDefinition = new StatementClassMapping();
StatementMappingIndex[] idxs = stmtMappingDefinition.getWhereFields();
for (int i = 0; i < idxs.length; i++) {
if (idxs[i] != null) {
mappingDefinition.addMappingForMember(i, idxs[i]);
}
}
FieldManager fm = null;
if (cmd.getIdentityType() == IdentityType.NONDURABLE) {
fm = new OldValueParameterSetter(op, ps, mappingDefinition);
} else {
fm = new ParameterSetter(op, ps, mappingDefinition);
}
op.provideFields(whereFieldNumbers, fm);
}
if (optimisticChecks) {
if (currentVersion == null) {
// Somehow the version is not set on this object (not read in ?) so report the bug
String msg = Localiser.msg("052201", op.getInternalObjectId(), table);
NucleusLogger.PERSISTENCE.error(msg);
throw new NucleusException(msg);
}
// WHERE clause - current version discriminator
StatementMappingIndex mapIdx = stmtMappingDefinition.getWhereVersion();
for (int i = 0; i < mapIdx.getNumberOfParameterOccurrences(); i++) {
mapIdx.getMapping().setObject(ec, ps, mapIdx.getParameterPositionsForOccurrence(i), currentVersion);
}
}
int[] rcs = sqlControl.executeStatementUpdate(ec, mconn, stmt, ps, !batch);
if (rcs[0] == 0 && optimisticChecks) {
// TODO Batching : when we use batching here we need to process these somehow
throw new NucleusOptimisticException(Localiser.msg("052203", op.getObjectAsPrintable(), op.getInternalObjectId(), "" + currentVersion), op.getObject());
}
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException e) {
String msg = Localiser.msg("052215", op.getObjectAsPrintable(), stmt, StringUtils.getStringFromStackTrace(e));
NucleusLogger.DATASTORE_PERSIST.error(msg);
List exceptions = new ArrayList();
exceptions.add(e);
while ((e = e.getNextException()) != null) {
exceptions.add(e);
}
throw new NucleusDataStoreException(msg, (Throwable[]) exceptions.toArray(new Throwable[exceptions.size()]));
}
}
// Execute any mapping actions now that we have done the update
for (int i = 0; i < callbacks.length; ++i) {
try {
if (NucleusLogger.PERSISTENCE.isDebugEnabled()) {
NucleusLogger.PERSISTENCE.debug(Localiser.msg("052216", op.getObjectAsPrintable(), ((JavaTypeMapping) callbacks[i]).getMemberMetaData().getFullFieldName()));
}
callbacks[i].postUpdate(op);
} catch (NotYetFlushedException e) {
op.updateFieldAfterInsert(e.getPersistable(), ((JavaTypeMapping) callbacks[i]).getMemberMetaData().getAbsoluteFieldNumber());
}
}
}
use of org.datanucleus.metadata.AbstractMemberMetaData in project datanucleus-rdbms by datanucleus.
the class MapContainsKeyMethod method getExpression.
/* (non-Javadoc)
* @see org.datanucleus.store.rdbms.sql.method.SQLMethod#getExpression(org.datanucleus.store.rdbms.sql.expression.SQLExpression, java.util.List)
*/
public SQLExpression getExpression(SQLStatement stmt, SQLExpression expr, List<SQLExpression> args) {
if (args == null || args.size() == 0 || args.size() > 1) {
throw new NucleusException(Localiser.msg("060016", "containsKey", "MapExpression", 1));
}
MapExpression mapExpr = (MapExpression) expr;
SQLExpression keyExpr = args.get(0);
if (keyExpr.isParameter()) {
// Key is a parameter so make sure its type is set
AbstractMemberMetaData mmd = mapExpr.getJavaTypeMapping().getMemberMetaData();
if (mmd != null && mmd.getMap() != null) {
Class keyCls = stmt.getQueryGenerator().getClassLoaderResolver().classForName(mmd.getMap().getKeyType());
stmt.getQueryGenerator().bindParameter(keyExpr.getParameterName(), keyCls);
}
}
SQLExpressionFactory exprFactory = stmt.getSQLExpressionFactory();
ClassLoaderResolver clr = stmt.getQueryGenerator().getClassLoaderResolver();
if (expr instanceof MapLiteral) {
// Literal Map
MapLiteral lit = (MapLiteral) expr;
Map map = (Map) lit.getValue();
JavaTypeMapping m = exprFactory.getMappingForType(boolean.class, true);
if (map == null || map.size() == 0) {
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
}
boolean useInExpression = false;
List<SQLExpression> mapKeyExprs = lit.getKeyLiteral().getKeyExpressions();
if (mapKeyExprs != null && !mapKeyExprs.isEmpty()) {
// Make sure the the map key(s) are compatible with the keyExpr
boolean incompatible = true;
Class elemtype = clr.classForName(keyExpr.getJavaTypeMapping().getType());
Iterator<SQLExpression> mapKeyExprIter = mapKeyExprs.iterator();
while (mapKeyExprIter.hasNext()) {
SQLExpression mapKeyExpr = mapKeyExprIter.next();
Class mapKeyType = clr.classForName(mapKeyExpr.getJavaTypeMapping().getType());
if (keyTypeCompatible(elemtype, mapKeyType)) {
incompatible = false;
break;
}
}
if (incompatible) {
// The provided element type isn't assignable to any of the input collection elements!
return exprFactory.newLiteral(stmt, m, true).eq(exprFactory.newLiteral(stmt, m, false));
}
// Check if we should compare using an "IN (...)" expression
SQLExpression mapKeyExpr = mapKeyExprs.get(0);
if (mapKeyExpr instanceof StringExpression || mapKeyExpr instanceof NumericExpression || mapKeyExpr instanceof TemporalExpression || mapKeyExpr instanceof CharacterExpression || mapKeyExpr instanceof EnumExpression) {
useInExpression = true;
}
}
if (useInExpression) {
// Return "key IN (val1, val2, ...)"
SQLExpression[] exprs = (mapKeyExprs != null ? mapKeyExprs.toArray(new SQLExpression[mapKeyExprs.size()]) : null);
return new InExpression(keyExpr, exprs);
}
// TODO If keyExpr is a parameter and mapExpr is derived from a parameter ?
MapKeyLiteral mapKeyLiteral = lit.getKeyLiteral();
BooleanExpression bExpr = null;
List<SQLExpression> elementExprs = mapKeyLiteral.getKeyExpressions();
for (int i = 0; i < elementExprs.size(); i++) {
if (bExpr == null) {
bExpr = (elementExprs.get(i)).eq(keyExpr);
} else {
bExpr = bExpr.ior((elementExprs.get(i)).eq(keyExpr));
}
}
if (bExpr != null) {
bExpr.encloseInParentheses();
}
return bExpr;
}
if (stmt.getQueryGenerator().getCompilationComponent() == CompilationComponent.FILTER) {
boolean useSubquery = getNeedsSubquery(stmt);
JoinType joinType = JoinType.INNER_JOIN;
if (keyExpr instanceof UnboundExpression) {
// See if the user has defined what should be used
String varName = ((UnboundExpression) keyExpr).getVariableName();
String extensionName = "datanucleus.query.jdoql." + varName + ".join";
String extensionValue = (String) stmt.getQueryGenerator().getValueForExtension(extensionName);
if (extensionValue != null) {
if (extensionValue.equalsIgnoreCase("SUBQUERY")) {
useSubquery = true;
} else if (extensionValue.equalsIgnoreCase("INNERJOIN")) {
useSubquery = false;
} else if (extensionValue.equalsIgnoreCase("LEFTOUTERJOIN")) {
joinType = JoinType.LEFT_OUTER_JOIN;
}
}
}
// TODO Check if *this* "containsKey" is negated, not any of them (and remove above check)
if (useSubquery) {
return containsAsSubquery(stmt, mapExpr, keyExpr);
}
return containsAsJoin(stmt, mapExpr, keyExpr, joinType);
}
return containsAsSubquery(stmt, mapExpr, keyExpr);
}
Aggregations