use of org.eclipse.persistence.sessions.DatabaseRecord in project eclipselink by eclipse-ee4j.
the class AggregateCollectionMapping method compareListsAndWrite_UpdatableListOrderField.
/**
* INTERNAL:
* Old and new lists are compared and only the changes are written to the database.
* Called only if listOrderField != null
*/
protected void compareListsAndWrite_UpdatableListOrderField(List previousList, List currentList, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
boolean shouldRepairOrder = false;
if (currentList instanceof IndirectList) {
shouldRepairOrder = ((IndirectList) currentList).isListOrderBrokenInDb();
}
// Object[] = {previousObject, currentObject, previousIndex, currentIndex}
HashMap<Object, Object[]> previousAndCurrentByKey = new HashMap<>();
// a SortedMap, current index mapped by previous index, both indexes must exist and be not equal.
TreeMap<Integer, Integer> currentIndexByPreviousIndex = new TreeMap<>();
// First index the current objects by their primary key.
for (int i = 0; i < currentList.size(); i++) {
Object currentObject = currentList.get(i);
try {
Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
Object[] previousAndCurrent = new Object[] { null, currentObject, null, i };
previousAndCurrentByKey.put(primaryKey, previousAndCurrent);
} catch (NullPointerException e) {
// ideally the customer should check for these themselves.
if (currentObject != null) {
throw e;
}
}
}
// Next index the previous objects (read from db or from backup in uow), also remove the objects to be removed.
for (int i = 0; i < previousList.size(); i++) {
Object previousObject = previousList.get(i);
Object primaryKey = getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
Object[] previousAndCurrent = previousAndCurrentByKey.get(primaryKey);
if (previousAndCurrent == null) {
// there's no current object - that means that previous object should be deleted
objectRemovedDuringUpdate(query, previousObject, null);
} else {
previousAndCurrent[0] = previousObject;
previousAndCurrent[2] = i;
int iCurrent = (Integer) previousAndCurrent[3];
if (i != iCurrent || shouldRepairOrder) {
currentIndexByPreviousIndex.put(i, iCurrent);
}
}
}
// some order indexes should be changed
if (!currentIndexByPreviousIndex.isEmpty()) {
boolean shouldUpdateOrderUsingPk = shouldRepairOrder;
if (!shouldUpdateOrderUsingPk) {
// search for cycles in order changes, such as, for instance:
// previous index 1, 2
// current index 2, 1
// or
// previous index 1, 3, 5
// current index 3, 5, 1
// those objects order index can't be updated using their previous order index value - should use pk in where clause instead.
// For now, if a cycle is found let's update all order indexes using pk.
// Ideally that should be refined in the future so that only indexes participating in cycles updated using pks - others still through bulk update.
boolean isCycleFound = false;
int iCurrentMax = -1;
Iterator<Integer> itCurrentIndexes = currentIndexByPreviousIndex.values().iterator();
while (itCurrentIndexes.hasNext() && !isCycleFound) {
int iCurrent = itCurrentIndexes.next();
if (iCurrent > iCurrentMax) {
iCurrentMax = iCurrent;
} else {
isCycleFound = true;
}
}
shouldUpdateOrderUsingPk = isCycleFound;
}
if (shouldUpdateOrderUsingPk) {
Iterator<Map.Entry<Object, Object[]>> it = previousAndCurrentByKey.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<Object, Object[]> entry = it.next();
Object key = entry.getKey();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject != null) {
Object currentObject = previousAndCurrent[1];
if (!this.isEntireObjectPK) {
objectUnchangedDuringUpdate(query, currentObject, previousObject);
}
int iPrevious = (Integer) previousAndCurrent[2];
int iCurrent = (Integer) previousAndCurrent[3];
if (iPrevious != iCurrent || shouldRepairOrder) {
objectChangedListOrderDuringUpdate(query, key, iCurrent);
}
}
}
} else {
// update the objects - but not their order values
if (!this.isEntireObjectPK) {
Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object[]> entry = iterator.next();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject != null) {
Object currentObject = previousAndCurrent[1];
objectUnchangedDuringUpdate(query, currentObject, previousObject);
}
}
}
// a bulk update query will be executed for each bunch of adjacent previous indexes from which current indexes could be obtained with a shift, for instance:
// previous index 1, 2, 3
// current index 5, 6, 7
// the sql will look like:
// UPDATE ... SET ListOrderField = ListOrderField + 4 WHERE 1 <= ListOrderField AND ListOrderField <= 3 AND FK = ...
int iMin = -1;
int iMax = -1;
int iShift = 0;
// each index corresponds to a bunch of objects to be shifted
ArrayList<Integer> iMinList = new ArrayList();
ArrayList<Integer> iMaxList = new ArrayList();
ArrayList<Integer> iShiftList = new ArrayList();
Iterator<Map.Entry<Integer, Integer>> itEntries = currentIndexByPreviousIndex.entrySet().iterator();
while (itEntries.hasNext()) {
Map.Entry<Integer, Integer> entry = itEntries.next();
int iPrevious = entry.getKey();
int iCurrent = entry.getValue();
if (iMin >= 0) {
// the shift should be the same for all indexes participating in bulk update
int iPreviousExpected = iMax + 1;
if (iPrevious == iPreviousExpected && iCurrent == iPreviousExpected + iShift) {
iMax++;
} else {
iMinList.add(iMin);
iMaxList.add(iMax);
iShiftList.add(iShift);
iMin = -1;
}
}
if (iMin == -1) {
// start defining a new bulk update - define iShift, iFirst, iLast
iMin = iPrevious;
iMax = iPrevious;
iShift = iCurrent - iPrevious;
}
}
if (iMin >= 0) {
iMinList.add(iMin);
iMaxList.add(iMax);
iShiftList.add(iShift);
}
// Look for the left-most and right-most bunches and update them first.
while (!iMinList.isEmpty()) {
int iMinLeft = previousList.size() + 1;
int iMinRight = -1;
int indexShiftLeft = -1;
int indexShiftRight = -1;
for (int i = 0; i < iMinList.size(); i++) {
iMin = iMinList.get(i);
iShift = iShiftList.get(i);
if (iShift < 0) {
if (iMin < iMinLeft) {
iMinLeft = iMin;
indexShiftLeft = i;
}
} else {
// iShift > 0
if (iMin > iMinRight) {
iMinRight = iMin;
indexShiftRight = i;
}
}
}
if (indexShiftLeft >= 0) {
objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftLeft), iMaxList.get(indexShiftLeft), iShiftList.get(indexShiftLeft));
}
if (indexShiftRight >= 0) {
objectChangedListOrderDuringUpdate(query, iMinList.get(indexShiftRight), iMaxList.get(indexShiftRight), iShiftList.get(indexShiftRight));
}
if (indexShiftLeft >= 0) {
iMinList.remove(indexShiftLeft);
iMaxList.remove(indexShiftLeft);
iShiftList.remove(indexShiftLeft);
}
if (indexShiftRight >= 0) {
iMinList.remove(indexShiftRight);
iMaxList.remove(indexShiftRight);
iShiftList.remove(indexShiftRight);
}
}
}
}
// Add the new objects
Iterator<Map.Entry<Object, Object[]>> iterator = previousAndCurrentByKey.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<Object, Object[]> entry = iterator.next();
Object[] previousAndCurrent = entry.getValue();
// previousObject may be null, meaning currentObject has been added to the list
Object previousObject = previousAndCurrent[0];
if (previousObject == null) {
// there's no previous object - that means that current object should be added.
// currentObject is not null
Object currentObject = previousAndCurrent[1];
// index of currentObject in currentList
int iCurrent = (Integer) previousAndCurrent[3];
DatabaseRecord extraData = new DatabaseRecord(1);
extraData.put(this.listOrderField, iCurrent);
objectAddedDuringUpdate(query, currentObject, null, extraData);
}
}
if (shouldRepairOrder) {
((IndirectList) currentList).setIsListOrderBrokenInDb(false);
}
}
use of org.eclipse.persistence.sessions.DatabaseRecord in project eclipselink by eclipse-ee4j.
the class ReadAllQuery method executeObjectLevelReadQuery.
/**
* INTERNAL:
* Execute the query.
* Get the rows and build the object from the rows.
* @exception DatabaseException - an error has occurred on the database
* @return java.lang.Object collection of objects resulting from execution of query.
*/
@Override
protected Object executeObjectLevelReadQuery() throws DatabaseException {
Object result = null;
if (this.containerPolicy.overridesRead()) {
this.executionTime = System.currentTimeMillis();
return this.containerPolicy.execute();
}
if (this.descriptor.isDescriptorForInterface()) {
Object returnValue = this.descriptor.getInterfacePolicy().selectAllObjectsUsingMultipleTableSubclassRead(this);
this.executionTime = System.currentTimeMillis();
return returnValue;
}
if (this.descriptor.hasTablePerClassPolicy() && this.descriptor.isAbstract()) {
result = this.containerPolicy.containerInstance();
if (this.shouldIncludeData) {
ComplexQueryResult complexResult = new ComplexQueryResult();
complexResult.setResult(result);
complexResult.setData(new ArrayList());
result = complexResult;
}
} else {
Object sopObject = getTranslationRow().getSopObject();
boolean useOptimization = false;
if (sopObject == null) {
useOptimization = usesResultSetAccessOptimization();
}
if (useOptimization) {
DatabaseCall call = ((DatasourceCallQueryMechanism) this.queryMechanism).selectResultSet();
this.executionTime = System.currentTimeMillis();
Statement statement = call.getStatement();
ResultSet resultSet = call.getResult();
DatabaseAccessor dbAccessor = (DatabaseAccessor) getAccessor();
boolean exceptionOccured = false;
try {
if (this.session.isUnitOfWork()) {
result = registerResultSetInUnitOfWork(resultSet, call.getFields(), call.getFieldsArray(), (UnitOfWorkImpl) this.session, this.translationRow);
} else {
result = this.containerPolicy.containerInstance();
this.descriptor.getObjectBuilder().buildObjectsFromResultSetInto(this, resultSet, call.getFields(), call.getFieldsArray(), result);
}
} catch (SQLException exception) {
exceptionOccured = true;
DatabaseException commException = dbAccessor.processExceptionForCommError(this.session, exception, call);
if (commException != null) {
throw commException;
}
throw DatabaseException.sqlException(exception, call, dbAccessor, this.session, false);
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (dbAccessor != null) {
if (statement != null) {
dbAccessor.releaseStatement(statement, call.getSQLString(), call, this.session);
}
}
if (call.hasAllocatedConnection()) {
getExecutionSession().releaseConnectionAfterCall(this);
}
} catch (RuntimeException cleanupException) {
if (!exceptionOccured) {
throw cleanupException;
}
} catch (SQLException cleanupSQLException) {
if (!exceptionOccured) {
throw DatabaseException.sqlException(cleanupSQLException, call, dbAccessor, this.session, false);
}
}
}
} else {
List<AbstractRecord> rows;
if (sopObject != null) {
Object valuesIterator = this.containerPolicy.iteratorFor(getTranslationRow().getSopObject());
int size = this.containerPolicy.sizeFor(sopObject);
rows = new ArrayList<>(size);
while (this.containerPolicy.hasNext(valuesIterator)) {
Object memberSopObject = this.containerPolicy.next(valuesIterator, this.session);
DatabaseRecord memberRow = new DatabaseRecord(0);
memberRow.setSopObject(memberSopObject);
rows.add(memberRow);
}
this.executionTime = System.currentTimeMillis();
} else {
rows = getQueryMechanism().selectAllRows();
this.executionTime = System.currentTimeMillis();
// If using 1-m joins, must set all rows.
if (hasJoining() && this.joinedAttributeManager.isToManyJoin()) {
this.joinedAttributeManager.setDataResults(rows, this.session);
}
// Batch fetching in IN requires access to the rows to build the id array.
if ((this.batchFetchPolicy != null) && this.batchFetchPolicy.isIN()) {
this.batchFetchPolicy.setDataResults(rows);
}
}
if (this.session.isUnitOfWork()) {
//
result = registerResultInUnitOfWork(rows, (UnitOfWorkImpl) this.session, this.translationRow, true);
} else {
if (rows instanceof ThreadCursoredList) {
result = this.containerPolicy.containerInstance();
} else {
result = this.containerPolicy.containerInstance(rows.size());
}
this.descriptor.getObjectBuilder().buildObjectsInto(this, rows, result);
}
if (sopObject != null) {
if (!this.descriptor.getObjectBuilder().isSimple()) {
// remove sopObject so it's not stuck in any value holder.
for (AbstractRecord row : rows) {
row.setSopObject(null);
}
}
} else {
if (this.shouldIncludeData) {
ComplexQueryResult complexResult = new ComplexQueryResult();
complexResult.setResult(result);
complexResult.setData(rows);
result = complexResult;
}
}
}
}
// Add the other (already registered) results and return them.
if (this.descriptor.hasTablePerClassPolicy()) {
result = this.containerPolicy.concatenateContainers(result, this.descriptor.getTablePerClassPolicy().selectAllObjectsUsingMultipleTableSubclassRead(this), this.session);
}
// If the results were empty, then ensure they get cached still.
if (shouldCacheQueryResults() && this.containerPolicy.isEmpty(result)) {
this.temporaryCachedQueryResults = InvalidObject.instance();
}
return result;
}
use of org.eclipse.persistence.sessions.DatabaseRecord in project eclipselink by eclipse-ee4j.
the class ReadObjectQuery method executeObjectLevelReadQuery.
/**
* INTERNAL:
* Execute the query.
* Do a cache lookup and build object from row if required.
* @exception DatabaseException - an error has occurred on the database
* @return object - the first object found or null if none.
*/
@Override
protected Object executeObjectLevelReadQuery() throws DatabaseException {
if (this.descriptor.isDescriptorForInterface() || this.descriptor.hasTablePerClassPolicy()) {
Object returnValue = this.descriptor.getInterfacePolicy().selectOneObjectUsingMultipleTableSubclassRead(this);
if (this.descriptor.hasTablePerClassPolicy() && (!this.descriptor.isAbstract()) && (returnValue == null)) {
// let it fall through to query the root.
} else {
this.executionTime = System.currentTimeMillis();
return returnValue;
}
}
boolean shouldSetRowsForJoins = hasJoining() && this.joinedAttributeManager.isToManyJoin();
AbstractSession session = getSession();
Object result = null;
AbstractRecord row = null;
Object sopObject = getTranslationRow().getSopObject();
boolean useOptimization = false;
if (sopObject == null) {
useOptimization = usesResultSetAccessOptimization();
}
if (useOptimization) {
DatabaseCall call = ((DatasourceCallQueryMechanism) this.queryMechanism).selectResultSet();
this.executionTime = System.currentTimeMillis();
boolean exceptionOccured = false;
ResultSet resultSet = call.getResult();
DatabaseAccessor dbAccessor = (DatabaseAccessor) getAccessor();
try {
if (resultSet.next()) {
ResultSetMetaData metaData = call.getResult().getMetaData();
boolean useSimple = this.descriptor.getObjectBuilder().isSimple();
DatabasePlatform platform = dbAccessor.getPlatform();
boolean optimizeData = platform.shouldOptimizeDataConversion();
if (useSimple) {
row = new SimpleResultSetRecord(call.getFields(), call.getFieldsArray(), resultSet, metaData, dbAccessor, getExecutionSession(), platform, optimizeData);
if (this.descriptor.isDescriptorTypeAggregate()) {
// Aggregate Collection may have an unmapped primary key referencing the owner, the corresponding field will not be used when the object is populated and therefore may not be cleared.
((SimpleResultSetRecord) row).setShouldKeepValues(true);
}
} else {
row = new ResultSetRecord(call.getFields(), call.getFieldsArray(), resultSet, metaData, dbAccessor, getExecutionSession(), platform, optimizeData);
}
if (session.isUnitOfWork()) {
result = registerResultInUnitOfWork(row, (UnitOfWorkImpl) session, this.translationRow, true);
} else {
result = buildObject(row);
}
if (!useSimple && this.descriptor.getObjectBuilder().shouldKeepRow()) {
if (((ResultSetRecord) row).hasResultSet()) {
// ResultSet has not been fully triggered - that means the cached object was used.
// Yet the row still may be cached in a value holder (see loadBatchReadAttributes and loadJoinedAttributes methods).
// Remove ResultSet to avoid attempt to trigger it (already closed) when pk or fk values (already extracted) accessed when the value holder is instantiated.
((ResultSetRecord) row).removeResultSet();
} else {
((ResultSetRecord) row).removeNonIndirectionValues();
}
}
}
} catch (SQLException exception) {
exceptionOccured = true;
DatabaseException commException = dbAccessor.processExceptionForCommError(session, exception, call);
if (commException != null) {
throw commException;
}
throw DatabaseException.sqlException(exception, call, getAccessor(), session, false);
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (dbAccessor != null) {
if (call.getStatement() != null) {
dbAccessor.releaseStatement(call.getStatement(), call.getSQLString(), call, session);
}
}
if (call.hasAllocatedConnection()) {
getExecutionSession().releaseConnectionAfterCall(this);
}
} catch (RuntimeException cleanupException) {
if (!exceptionOccured) {
throw cleanupException;
}
} catch (SQLException cleanupSQLException) {
if (!exceptionOccured) {
throw DatabaseException.sqlException(cleanupSQLException, call, dbAccessor, session, false);
}
}
}
} else {
if (sopObject != null) {
row = new DatabaseRecord(0);
row.setSopObject(sopObject);
} else {
// If using 1-m joins, must select all rows.
if (shouldSetRowsForJoins) {
List rows = getQueryMechanism().selectAllRows();
if (rows.size() > 0) {
row = (AbstractRecord) rows.get(0);
}
getJoinedAttributeManager().setDataResults(rows, session);
} else {
row = getQueryMechanism().selectOneRow();
}
}
this.executionTime = System.currentTimeMillis();
if (row != null) {
if (session.isUnitOfWork()) {
result = registerResultInUnitOfWork(row, (UnitOfWorkImpl) session, this.translationRow, true);
} else {
result = buildObject(row);
}
if (sopObject != null) {
// remove sopObject so it's not stuck in a value holder.
row.setSopObject(null);
}
}
}
if ((result == null) && shouldCacheQueryResults()) {
cacheResult(null);
}
if ((result == null) && this.shouldRefreshIdentityMapResult) {
// bug5955326, should invalidate the shared cached if refreshed object no longer exists.
if (this.selectionId != null) {
session.getParentIdentityMapSession(this.descriptor, true, true).getIdentityMapAccessor().invalidateObject(this.selectionId, this.referenceClass);
} else if (this.selectionObject != null) {
session.getParentIdentityMapSession(this.descriptor, true, true).getIdentityMapAccessor().invalidateObject(this.selectionObject);
}
}
if (this.shouldIncludeData && (sopObject == null)) {
ComplexQueryResult complexResult = new ComplexQueryResult();
complexResult.setResult(result);
complexResult.setData(row);
return complexResult;
}
return result;
}
use of org.eclipse.persistence.sessions.DatabaseRecord in project eclipselink by eclipse-ee4j.
the class ReadQuery method prepareForExecution.
/**
* INTERNAL:
* Prepare the receiver for execution in a session.
*/
@Override
public void prepareForExecution() throws QueryException {
super.prepareForExecution();
DatabaseCall databaseCall = this.getCall();
if (databaseCall != null && (databaseCall.shouldIgnoreFirstRowSetting() || databaseCall.shouldIgnoreMaxResultsSetting())) {
AbstractRecord parameters = this.getTranslationRow();
if (parameters.isEmpty()) {
parameters = new DatabaseRecord();
}
// We should check FirstRow and MaxResults separately.
if (databaseCall.shouldIgnoreFirstRowSetting()) {
parameters.add(DatabaseCall.FIRSTRESULT_FIELD, this.getFirstResult());
}
if (databaseCall.shouldIgnoreMaxResultsSetting()) {
// Bug #493771
parameters.add(DatabaseCall.MAXROW_FIELD, ((DatabasePlatform) session.getPlatform(databaseCall.getQuery().getReferenceClass())).computeMaxRowsForSQL(this.getFirstResult(), this.getMaxRows()));
}
this.setTranslationRow(parameters);
}
}
use of org.eclipse.persistence.sessions.DatabaseRecord in project eclipselink by eclipse-ee4j.
the class ResultSetMappingQuery method buildObjectsFromRecords.
/**
* INTERNAL:
* This method is used to build the results. Interpreting the SQLResultSetMapping.
*/
protected List buildObjectsFromRecords(List databaseRecords, SQLResultSetMapping mapping) {
int numberOfRecords = databaseRecords.size();
List results = new ArrayList(numberOfRecords);
if (mapping == null) {
if (shouldReturnNameValuePairs()) {
return databaseRecords;
}
for (Iterator iterator = databaseRecords.iterator(); iterator.hasNext(); ) {
DatabaseRecord record = (DatabaseRecord) iterator.next();
results.add(record.values().toArray());
}
} else {
for (Iterator iterator = databaseRecords.iterator(); iterator.hasNext(); ) {
if (mapping.getResults().size() > 1) {
Object[] resultElement = new Object[mapping.getResults().size()];
DatabaseRecord record = (DatabaseRecord) iterator.next();
for (int i = 0; i < mapping.getResults().size(); i++) {
resultElement[i] = mapping.getResults().get(i).getValueFromRecord(record, this);
}
results.add(resultElement);
} else if (mapping.getResults().size() == 1) {
DatabaseRecord record = (DatabaseRecord) iterator.next();
results.add(mapping.getResults().get(0).getValueFromRecord(record, this));
} else {
return results;
}
}
}
return results;
}
Aggregations