use of org.datanucleus.store.rdbms.query.StatementClassMapping in project datanucleus-rdbms by datanucleus.
the class JoinListStore method listIterator.
/**
* Accessor for an iterator through the list elements.
* @param ownerOP ObjectProvider for the owner
* @param startIdx The start point in the list (only for indexed lists).
* @param endIdx End index in the list (only for indexed lists).
* @return The List Iterator
*/
protected ListIterator<E> listIterator(ObjectProvider ownerOP, int startIdx, int endIdx) {
ExecutionContext ec = ownerOP.getExecutionContext();
Transaction tx = ec.getTransaction();
// Generate the statement. Note that this is not cached since depends on the current FetchPlan and other things
IteratorStatement iterStmt = getIteratorStatement(ownerOP.getExecutionContext(), ec.getFetchPlan(), true, startIdx, endIdx);
SelectStatement sqlStmt = iterStmt.getSelectStatement();
StatementClassMapping resultMapping = iterStmt.getStatementClassMapping();
// Input parameter(s) - the owner
int inputParamNum = 1;
StatementMappingIndex ownerIdx = new StatementMappingIndex(ownerMapping);
if (sqlStmt.getNumberOfUnions() > 0) {
// Add parameter occurrence for each union of statement
for (int j = 0; j < sqlStmt.getNumberOfUnions() + 1; j++) {
int[] paramPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
for (int k = 0; k < paramPositions.length; k++) {
paramPositions[k] = inputParamNum++;
}
ownerIdx.addParameterOccurrence(paramPositions);
}
} else {
int[] paramPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
for (int k = 0; k < paramPositions.length; k++) {
paramPositions[k] = inputParamNum++;
}
ownerIdx.addParameterOccurrence(paramPositions);
}
if (tx.getSerializeRead() != null && tx.getSerializeRead()) {
sqlStmt.addExtension(SQLStatement.EXTENSION_LOCK_FOR_UPDATE, true);
}
String stmt = sqlStmt.getSQLText().toSQL();
try {
ManagedConnection mconn = storeMgr.getConnectionManager().getConnection(ec);
SQLController sqlControl = storeMgr.getSQLController();
try {
// Create the statement
PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
// Set the owner
ObjectProvider stmtOwnerOP = BackingStoreHelper.getOwnerObjectProviderForBackingStore(ownerOP);
int numParams = ownerIdx.getNumberOfParameterOccurrences();
for (int paramInstance = 0; paramInstance < numParams; paramInstance++) {
ownerIdx.getMapping().setObject(ec, ps, ownerIdx.getParameterPositionsForOccurrence(paramInstance), stmtOwnerOP.getObject());
}
try {
ResultSet rs = sqlControl.executeStatementQuery(ec, mconn, stmt, ps);
try {
if (elementsAreEmbedded || elementsAreSerialised) {
// No ResultObjectFactory needed - handled by SetStoreIterator
return new ListStoreIterator(ownerOP, rs, null, this);
} else if (elementMapping instanceof ReferenceMapping) {
// No ResultObjectFactory needed - handled by SetStoreIterator
return new ListStoreIterator(ownerOP, rs, null, this);
} else {
ResultObjectFactory rof = new PersistentClassROF(ec, rs, false, resultMapping, elementCmd, clr.classForName(elementType));
return new ListStoreIterator(ownerOP, rs, rof, this);
}
} finally {
rs.close();
}
} finally {
sqlControl.closeStatement(mconn, ps);
}
} finally {
mconn.release();
}
} catch (SQLException | MappedDatastoreException e) {
throw new NucleusDataStoreException(Localiser.msg("056006", stmt), e);
}
}
use of org.datanucleus.store.rdbms.query.StatementClassMapping in project datanucleus-rdbms by datanucleus.
the class JoinListStore method getIteratorStatement.
/**
* Method to return the SQLStatement and mapping for an iterator for this backing store.
* Create a statement of the form
* <pre>
* SELECT ELEM_COLS
* FROM JOIN_TBL
* [JOIN ELEM_TBL ON ELEM_TBL.ID = JOIN_TBL.ELEM_ID]
* [WHERE]
* [JOIN_TBL.OWNER_ID = {value}] [AND]
* [JOIN_TBL.DISCRIM = {discrimValue}]
* [ORDER BY {orderClause}]
* </pre>
* @param ec ExecutionContext
* @param fp FetchPlan to use in determing which fields of element to select
* @param addRestrictionOnOwner Whether to restrict to a particular owner (otherwise functions as bulk fetch for many owners).
* @param startIdx Start index for the iterator (or -1)
* @param endIdx End index for the iterator (or -1)
* @return The SQLStatement and its associated StatementClassMapping
*/
public IteratorStatement getIteratorStatement(ExecutionContext ec, FetchPlan fp, boolean addRestrictionOnOwner, int startIdx, int endIdx) {
SelectStatement sqlStmt = null;
StatementClassMapping stmtClassMapping = new StatementClassMapping();
SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
if (elementsAreEmbedded || elementsAreSerialised) {
// Element = embedded, serialised (maybe Non-PC)
// Just select the join table since we're going to return the embedded/serialised columns from it
sqlStmt = new SelectStatement(storeMgr, containerTable, null, null);
sqlStmt.setClassLoaderResolver(clr);
// Select the element column - first select is assumed by ListStoreIterator
sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
// TODO If embedded element and it includes 1-1/N-1 in FetchPlan then select its fields also
} else if (elementMapping instanceof ReferenceMapping) {
// Element = Reference type (interface/Object)
// Just select the join table since we're going to return the implementation id columns only
sqlStmt = new SelectStatement(storeMgr, containerTable, null, null);
sqlStmt.setClassLoaderResolver(clr);
// Select the reference column(s) - first select is assumed by ListStoreIterator
sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
} else {
// Join to the element table(s)
if (elementInfo != null) {
for (int i = 0; i < elementInfo.length; i++) {
// TODO This will only work if all element types have a discriminator
final int elementNo = i;
final Class elementCls = clr.classForName(elementInfo[elementNo].getClassName());
SelectStatement elementStmt = null;
if (elementInfo[elementNo].getDiscriminatorStrategy() != null && elementInfo[elementNo].getDiscriminatorStrategy() != DiscriminatorStrategy.NONE) {
// The element uses a discriminator so just use that in the SELECT
String elementType = ownerMemberMetaData.getCollection().getElementType();
if (ClassUtils.isReferenceType(clr.classForName(elementType))) {
String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
Class[] cls = new Class[clsNames.length];
for (int j = 0; j < clsNames.length; j++) {
cls[j] = clr.classForName(clsNames[j]);
}
SelectStatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, cls, true, null, null, containerTable, null, elementMapping);
if (allowNulls) {
stmtGen.setOption(SelectStatementGenerator.OPTION_ALLOW_NULLS);
}
elementStmt = stmtGen.getStatement(ec);
} else {
SelectStatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, elementCls, true, null, null, containerTable, null, elementMapping);
if (allowNulls) {
stmtGen.setOption(SelectStatementGenerator.OPTION_ALLOW_NULLS);
}
elementStmt = stmtGen.getStatement(ec);
}
iterateUsingDiscriminator = true;
} else {
// No discriminator, but subclasses so use UNIONs
SelectStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, elementCls, true, null, null, containerTable, null, elementMapping);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
stmtClassMapping.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
elementStmt = stmtGen.getStatement(ec);
}
if (sqlStmt == null) {
sqlStmt = elementStmt;
} else {
sqlStmt.union(elementStmt);
}
}
if (sqlStmt == null) {
throw new NucleusException("Error in generation of SQL statement for iterator over (Join) list. Statement is null");
}
// Select the required fields
SQLTable elementSqlTbl = sqlStmt.getTable(elementInfo[0].getDatastoreClass(), sqlStmt.getPrimaryTable().getGroupName());
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, stmtClassMapping, fp, elementSqlTbl, elementCmd, fp.getMaxFetchDepth());
} else {
throw new NucleusException("Unable to create SQL statement to retrieve elements of List");
}
}
if (addRestrictionOnOwner) {
// Apply condition on join-table owner field to filter by owner
SQLTable ownerSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), ownerMapping);
SQLExpression ownerExpr = exprFactory.newExpression(sqlStmt, ownerSqlTbl, ownerMapping);
SQLExpression ownerVal = exprFactory.newLiteralParameter(sqlStmt, ownerMapping, null, "OWNER");
sqlStmt.whereAnd(ownerExpr.eq(ownerVal), true);
}
if (relationDiscriminatorMapping != null) {
// Apply condition on distinguisher field to filter by distinguisher (when present)
SQLTable distSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), relationDiscriminatorMapping);
SQLExpression distExpr = exprFactory.newExpression(sqlStmt, distSqlTbl, relationDiscriminatorMapping);
SQLExpression distVal = exprFactory.newLiteral(sqlStmt, relationDiscriminatorMapping, relationDiscriminatorValue);
sqlStmt.whereAnd(distExpr.eq(distVal), true);
}
if (indexedList) {
// "Indexed List" so allow restriction on returned indexes
boolean needsOrdering = true;
if (startIdx == -1 && endIdx == -1) {
// Just restrict to >= 0 so we don't get any disassociated elements
SQLExpression indexExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression indexVal = exprFactory.newLiteral(sqlStmt, orderMapping, 0);
sqlStmt.whereAnd(indexExpr.ge(indexVal), true);
} else if (startIdx >= 0 && endIdx == startIdx) {
// Particular index required so add restriction
needsOrdering = false;
SQLExpression indexExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression indexVal = exprFactory.newLiteral(sqlStmt, orderMapping, startIdx);
sqlStmt.whereAnd(indexExpr.eq(indexVal), true);
} else {
// Add restrictions on start/end indices as required
if (startIdx >= 0) {
SQLExpression indexExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression indexVal = exprFactory.newLiteral(sqlStmt, orderMapping, startIdx);
sqlStmt.whereAnd(indexExpr.ge(indexVal), true);
} else {
// Just restrict to >= 0 so we don't get any disassociated elements
SQLExpression indexExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression indexVal = exprFactory.newLiteral(sqlStmt, orderMapping, 0);
sqlStmt.whereAnd(indexExpr.ge(indexVal), true);
}
if (endIdx >= 0) {
SQLExpression indexExpr2 = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression indexVal2 = exprFactory.newLiteral(sqlStmt, orderMapping, endIdx);
sqlStmt.whereAnd(indexExpr2.lt(indexVal2), true);
}
}
if (needsOrdering) {
// Order by the ordering column, when present
SQLTable orderSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression[] orderExprs = new SQLExpression[orderMapping.getNumberOfDatastoreMappings()];
boolean[] descendingOrder = new boolean[orderMapping.getNumberOfDatastoreMappings()];
orderExprs[0] = exprFactory.newExpression(sqlStmt, orderSqlTbl, orderMapping);
sqlStmt.setOrdering(orderExprs, descendingOrder);
}
} else {
if (elementInfo != null) {
// Apply ordering defined by <order-by>
DatastoreClass elementTbl = elementInfo[0].getDatastoreClass();
FieldOrder[] orderComponents = ownerMemberMetaData.getOrderMetaData().getFieldOrders();
SQLExpression[] orderExprs = new SQLExpression[orderComponents.length];
boolean[] orderDirs = new boolean[orderComponents.length];
for (int i = 0; i < orderComponents.length; i++) {
String fieldName = orderComponents[i].getFieldName();
JavaTypeMapping fieldMapping = elementTbl.getMemberMapping(elementInfo[0].getAbstractClassMetaData().getMetaDataForMember(fieldName));
orderDirs[i] = !orderComponents[i].isForward();
SQLTable fieldSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), fieldMapping);
orderExprs[i] = exprFactory.newExpression(sqlStmt, fieldSqlTbl, fieldMapping);
}
sqlStmt.setOrdering(orderExprs, orderDirs);
}
}
return new IteratorStatement(this, sqlStmt, stmtClassMapping);
}
use of org.datanucleus.store.rdbms.query.StatementClassMapping in project datanucleus-rdbms by datanucleus.
the class BackingStoreHelper method populateEmbeddedElementFieldsInStatement.
/**
* Convenience method to populate the passed PreparedStatement with the field values from
* the embedded element starting at the specified jdbc position.
* @param op ObjectProvider of the owning container
* @param element The embedded element
* @param ps The PreparedStatement
* @param jdbcPosition JDBC position in the statement to start at
* @param ownerFieldMetaData The meta data for the owner field
* @param elementMapping mapping for the element
* @param emd Metadata for the element class
* @param bcs Container store
* @return The next JDBC position
*/
public static int populateEmbeddedElementFieldsInStatement(ObjectProvider op, Object element, PreparedStatement ps, int jdbcPosition, AbstractMemberMetaData ownerFieldMetaData, JavaTypeMapping elementMapping, AbstractClassMetaData emd, BaseContainerStore bcs) {
EmbeddedElementPCMapping embeddedMapping = (EmbeddedElementPCMapping) elementMapping;
StatementClassMapping mappingDefinition = new StatementClassMapping();
int[] elementFieldNumbers = new int[embeddedMapping.getNumberOfJavaTypeMappings()];
for (int i = 0; i < embeddedMapping.getNumberOfJavaTypeMappings(); i++) {
JavaTypeMapping fieldMapping = embeddedMapping.getJavaTypeMapping(i);
int absFieldNum = emd.getAbsolutePositionOfMember(fieldMapping.getMemberMetaData().getName());
elementFieldNumbers[i] = absFieldNum;
StatementMappingIndex stmtMapping = new StatementMappingIndex(fieldMapping);
int[] jdbcParamPositions = new int[fieldMapping.getNumberOfDatastoreMappings()];
for (int j = 0; j < fieldMapping.getNumberOfDatastoreMappings(); j++) {
jdbcParamPositions[j] = jdbcPosition++;
}
stmtMapping.addParameterOccurrence(jdbcParamPositions);
mappingDefinition.addMappingForMember(absFieldNum, stmtMapping);
}
ObjectProvider elementOP = bcs.getObjectProviderForEmbeddedPCObject(op, element, ownerFieldMetaData, ObjectProvider.EMBEDDED_COLLECTION_ELEMENT_PC);
elementOP.provideFields(elementFieldNumbers, new ParameterSetter(elementOP, ps, mappingDefinition));
return jdbcPosition;
}
use of org.datanucleus.store.rdbms.query.StatementClassMapping 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.store.rdbms.query.StatementClassMapping in project datanucleus-rdbms by datanucleus.
the class FKSetStore method getIteratorStatement.
/**
* Method to return the SQLStatement and mapping for an iterator for this backing store.
* Create a statement of the form
* <pre>
* SELECT ELEM_COLS
* FROM ELEM_TBL
* [WHERE]
* [ELEM_TBL.OWNER_ID = {value}] [AND]
* [ELEM_TBL.DISCRIM = {discrimValue}]
* [ORDER BY {orderClause}]
* </pre>
* @param ec ExecutionContext
* @param fp FetchPlan to use in determining which fields of element to select
* @param addRestrictionOnOwner Whether to restrict to a particular owner (otherwise functions as bulk fetch for many owners).
* @return The SQLStatement and its associated StatementClassMapping
*/
public IteratorStatement getIteratorStatement(ExecutionContext ec, FetchPlan fp, boolean addRestrictionOnOwner) {
SelectStatement sqlStmt = null;
SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
StatementClassMapping iteratorMappingClass = new StatementClassMapping();
if (elementInfo[0].getDatastoreClass().getDiscriminatorMetaData() != null && elementInfo[0].getDatastoreClass().getDiscriminatorMetaData().getStrategy() != DiscriminatorStrategy.NONE) {
// TODO Only caters for one elementInfo, but with subclass-table we can have multiple
String elementType = ownerMemberMetaData.getCollection().getElementType();
if (ClassUtils.isReferenceType(clr.classForName(elementType))) {
String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
Class[] cls = new Class[clsNames.length];
for (int i = 0; i < clsNames.length; i++) {
cls[i] = clr.classForName(clsNames[i]);
}
sqlStmt = new DiscriminatorStatementGenerator(storeMgr, clr, cls, true, null, null).getStatement(ec);
} else {
sqlStmt = new DiscriminatorStatementGenerator(storeMgr, clr, clr.classForName(elementInfo[0].getClassName()), true, null, null).getStatement(ec);
}
iterateUsingDiscriminator = true;
// TODO Cater for having all possible subclasses stored in the same table (so we can select their fields too)
// String[] elemSubclasses = op.getExecutionContext().getMetaDataManager().getSubclassesForClass(emd.getFullClassName(), false);
// NucleusLogger.GENERAL.info(">> FKSetStore.iter iterMapDef=" + iteratorMappingDef + " table=" + sqlStmt.getPrimaryTable() +
// " emd=" + emd.getFullClassName() + " elem.subclasses=" + StringUtils.objectArrayToString(elemSubclasses));
// Select the required fields (of the element class)
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, iteratorMappingClass, fp, sqlStmt.getPrimaryTable(), elementCmd, fp.getMaxFetchDepth());
} else {
boolean selectFetchPlan = true;
Class elementTypeCls = clr.classForName(elementType);
if (elementTypeCls.isInterface() && elementInfo.length > 1) {
// Multiple implementations of an interface, so assume the FetchPlan differs between implementation
selectFetchPlan = false;
}
// TODO This only works if the different elementInfos have the same number of PK fields (otherwise get SQL error in UNION)
for (int i = 0; i < elementInfo.length; i++) {
final Class elementCls = clr.classForName(this.elementInfo[i].getClassName());
UnionStatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, elementCls, true, null, null);
stmtGen.setOption(SelectStatementGenerator.OPTION_SELECT_DN_TYPE);
iteratorMappingClass.setNucleusTypeColumnName(UnionStatementGenerator.DN_TYPE_COLUMN);
SelectStatement subStmt = stmtGen.getStatement(ec);
if (selectFetchPlan) {
// Select the FetchPlan fields (of the element class)
if (sqlStmt == null) {
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(subStmt, iteratorMappingClass, fp, subStmt.getPrimaryTable(), elementInfo[i].getAbstractClassMetaData(), fp.getMaxFetchDepth());
} else {
SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(subStmt, null, fp, subStmt.getPrimaryTable(), elementInfo[i].getAbstractClassMetaData(), fp.getMaxFetchDepth());
}
} else {
// Select the candidate id of the element class only
if (sqlStmt == null) {
SQLStatementHelper.selectIdentityOfCandidateInStatement(subStmt, iteratorMappingClass, elementInfo[i].getAbstractClassMetaData());
} else {
SQLStatementHelper.selectIdentityOfCandidateInStatement(subStmt, null, elementInfo[i].getAbstractClassMetaData());
}
}
if (sqlStmt == null) {
sqlStmt = subStmt;
} else {
sqlStmt.union(subStmt);
}
}
if (sqlStmt == null) {
throw new NucleusException("Unable to generate iterator statement for field " + getOwnerMemberMetaData().getFullFieldName());
}
}
if (addRestrictionOnOwner) {
// Apply condition to filter by owner
// TODO If ownerMapping is not for containerTable then do JOIN to ownerTable in the FROM clause (or find if already done)
SQLTable ownerSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), ownerMapping);
SQLExpression ownerExpr = exprFactory.newExpression(sqlStmt, ownerSqlTbl, ownerMapping);
SQLExpression ownerVal = exprFactory.newLiteralParameter(sqlStmt, ownerMapping, null, "OWNER");
sqlStmt.whereAnd(ownerExpr.eq(ownerVal), true);
}
if (relationDiscriminatorMapping != null) {
// Apply condition on distinguisher field to filter by distinguisher (when present)
SQLTable distSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), relationDiscriminatorMapping);
SQLExpression distExpr = exprFactory.newExpression(sqlStmt, distSqlTbl, relationDiscriminatorMapping);
SQLExpression distVal = exprFactory.newLiteral(sqlStmt, relationDiscriminatorMapping, relationDiscriminatorValue);
sqlStmt.whereAnd(distExpr.eq(distVal), true);
}
if (orderMapping != null) {
// Order by the ordering column, when present
SQLTable orderSqlTbl = SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
SQLExpression[] orderExprs = new SQLExpression[orderMapping.getNumberOfDatastoreMappings()];
boolean[] descendingOrder = new boolean[orderMapping.getNumberOfDatastoreMappings()];
orderExprs[0] = exprFactory.newExpression(sqlStmt, orderSqlTbl, orderMapping);
sqlStmt.setOrdering(orderExprs, descendingOrder);
}
return new IteratorStatement(this, sqlStmt, iteratorMappingClass);
}
Aggregations