use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class AggregateCollectionMapping method compareListsAndWrite_NonUpdatableListOrderField.
/**
* INTERNAL:
* Old and new lists are compared and only the changes are written to the database.
* Called only if listOrderField != null
*/
protected void compareListsAndWrite_NonUpdatableListOrderField(List previousList, List currentList, WriteObjectQuery query) throws DatabaseException, OptimisticLockException {
boolean shouldRepairOrder = false;
if (currentList instanceof IndirectList) {
shouldRepairOrder = ((IndirectList) currentList).isListOrderBrokenInDb();
}
HashMap<Object, Object[]> previousAndCurrentByKey = new HashMap<>();
int pkSize = getReferenceDescriptor().getPrimaryKeyFields().size();
// First index the current objects by their primary key.
for (int i = 0; i < currentList.size(); i++) {
Object currentObject = currentList.get(i);
try {
CacheId primaryKey = (CacheId) getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(currentObject, query.getSession());
primaryKey.add(i);
Object[] previousAndCurrent = new Object[] { null, currentObject };
previousAndCurrentByKey.put(primaryKey, previousAndCurrent);
} catch (NullPointerException e) {
// ideally the customer should check for these themselves.
if (currentObject != null) {
throw e;
}
}
}
if (shouldRepairOrder) {
DeleteAllQuery deleteAllQuery = (DeleteAllQuery) this.deleteAllQuery;
if (this.isCascadeOnDeleteSetOnDatabase) {
deleteAllQuery = (DeleteAllQuery) deleteAllQuery.clone();
deleteAllQuery.setIsInMemoryOnly(false);
}
deleteAllQuery.executeDeleteAll(query.getSession().getSessionForClass(getReferenceClass()), query.getTranslationRow(), new Vector(previousList));
} else {
// Next index the previous objects (read from db or from backup in uow)
for (int i = 0; i < previousList.size(); i++) {
Object previousObject = previousList.get(i);
CacheId primaryKey = (CacheId) getReferenceDescriptor().getObjectBuilder().extractPrimaryKeyFromObject(previousObject, query.getSession());
primaryKey.add(i);
Object[] previousAndCurrent = previousAndCurrentByKey.get(primaryKey);
if (previousAndCurrent == null) {
// there's no current object - that means that previous object should be deleted
DatabaseRecord extraData = new DatabaseRecord(1);
extraData.put(this.listOrderField, i);
objectRemovedDuringUpdate(query, previousObject, extraData);
} else {
previousAndCurrent[0] = previousObject;
}
}
}
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];
// currentObject is not null
Object currentObject = previousAndCurrent[1];
if (previousObject == null) {
// there's no previous object - that means that current object should be added.
// index of currentObject in currentList
int iCurrent = (Integer) ((CacheId) key).getPrimaryKey()[pkSize];
DatabaseRecord extraData = new DatabaseRecord(1);
extraData.put(this.listOrderField, iCurrent);
objectAddedDuringUpdate(query, currentObject, null, extraData);
} else {
if (!this.isEntireObjectPK) {
objectUnchangedDuringUpdate(query, currentObject, previousObject);
}
}
}
if (shouldRepairOrder) {
((IndirectList) currentList).setIsListOrderBrokenInDb(false);
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class OrderedListContainerPolicy method mergeChanges.
/**
* INTERNAL:
* Merge changes from the source to the target object. Because this is a
* collection mapping, values are added to or removed from the collection
* based on the change set.
*/
@Override
protected void mergeChanges(CollectionChangeRecord changeRecord, Object valueOfTarget, boolean shouldMergeCascadeParts, MergeManager mergeManager, AbstractSession targetSession) {
ObjectChangeSet objectChanges;
if (changeRecord.orderHasBeenRepaired() && valueOfTarget instanceof IndirectList) {
((IndirectList) valueOfTarget).setIsListOrderBrokenInDb(false);
}
if (!changeRecord.getOrderedChangeObjectList().isEmpty()) {
Iterator<OrderedChangeObject> objects = changeRecord.getOrderedChangeObjectList().iterator();
while (objects.hasNext()) {
OrderedChangeObject changeObject = objects.next();
objectChanges = changeObject.getChangeSet();
if (changeObject.getChangeType() == CollectionChangeEvent.REMOVE) {
boolean objectRemoved = changeRecord.getRemoveObjectList().containsKey(objectChanges);
Object objectToRemove = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
// This should not happen unless identity is lost.
if (objectToRemove != null) {
Integer index = changeObject.getIndex();
if (index != null) {
if (objectToRemove.equals(get(index, valueOfTarget, mergeManager.getSession()))) {
removeFromAtIndex(index, valueOfTarget);
} else {
// Object is in the cache, but the collection doesn't have it at the location we expect
// Collection is invalid with respect to these changes, so invalidate the parent and abort
Object key = changeRecord.getOwner().getId();
targetSession.getIdentityMapAccessor().invalidateObject(key, changeRecord.getOwner().getClassType(targetSession));
return;
}
} else {
removeFrom(objectToRemove, valueOfTarget, targetSession);
}
if ((!mergeManager.shouldMergeChangesIntoDistributedCache()) && changeRecord.getMapping().isPrivateOwned()) {
// Check that the object was actually removed and not moved.
if (objectRemoved) {
mergeManager.registerRemovedNewObjectIfRequired(objectChanges.getUnitOfWorkClone());
}
}
}
} else {
// getChangeType == add
boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
Object object = null;
// The object was actually added and not moved.
if (objectAdded && shouldMergeCascadeParts) {
object = mergeCascadeParts(objectChanges, mergeManager, targetSession);
}
if (object == null) {
// Retrieve the object to be added to the collection.
object = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
}
// not moved.
if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
// during merge into distributed cache
if (!contains(object, valueOfTarget, mergeManager.getSession())) {
addIntoAtIndex(changeObject.getIndex(), object, valueOfTarget, mergeManager.getSession());
}
} else {
addIntoAtIndex(changeObject.getIndex(), object, valueOfTarget, targetSession);
}
}
}
} else {
// Deferred change tracking merge behavior
// Step 1 - iterate over the removed changes and remove them from the container.
List<Integer> removedIndices = changeRecord.getOrderedRemoveObjectIndices();
if (removedIndices.isEmpty()) {
// Check if we have removed objects via a
// simpleRemoveFromCollectionChangeRecord API call.
Iterator<ObjectChangeSet> removedObjects = changeRecord.getRemoveObjectList().keySet().iterator();
while (removedObjects.hasNext()) {
objectChanges = removedObjects.next();
removeFrom(objectChanges.getOldKey(), objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession), valueOfTarget, targetSession);
registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
}
} else {
for (int i = removedIndices.size() - 1; i >= 0; i--) {
Integer index = removedIndices.get(i);
objectChanges = (ObjectChangeSet) changeRecord.getOrderedRemoveObject(index);
Object objectToRemove = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
if ((objectToRemove != null) && (objectToRemove.equals(get(index, valueOfTarget, mergeManager.getSession())))) {
removeFromAtIndex(index, valueOfTarget);
// The object was actually removed and not moved.
if (changeRecord.getRemoveObjectList().containsKey(objectChanges)) {
registerRemoveNewObjectIfRequired(objectChanges, mergeManager);
}
} else {
// Object is either not in the cache, or not at the location we expect
// Collection is invalid with respect to these changes, so invalidate the parent and abort
Object key = changeRecord.getOwner().getId();
targetSession.getIdentityMapAccessor().invalidateObject(key, changeRecord.getOwner().getClassType(targetSession));
return;
}
}
}
// Step 2 - iterate over the added changes and add them to the container.
for (ObjectChangeSet addChangeSet : changeRecord.getOrderedAddObjects()) {
objectChanges = addChangeSet;
boolean objectAdded = changeRecord.getAddObjectList().containsKey(objectChanges);
Object object = null;
// The object was actually added and not moved.
if (objectAdded && shouldMergeCascadeParts) {
object = mergeCascadeParts(objectChanges, mergeManager, targetSession);
}
if (object == null) {
// Retrieve the object to be added to the collection.
object = objectChanges.getTargetVersionOfSourceObject(mergeManager, targetSession);
}
// not moved.
if (objectAdded && mergeManager.shouldMergeChangesIntoDistributedCache()) {
// during merge into distributed cache
if (!contains(object, valueOfTarget, mergeManager.getSession())) {
addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, mergeManager.getSession());
}
} else {
addIntoAtIndex(changeRecord.getOrderedAddObjectIndex(objectChanges), object, valueOfTarget, targetSession);
}
}
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class OrderedListContainerPolicy method addAll.
protected boolean addAll(List elements, Object container, AbstractSession session, List<AbstractRecord> dbRows, ReadQuery query, CacheKey parentCacheKey) {
int size = dbRows.size();
if (this.elementDescriptor != null && this.elementDescriptor.getObjectBuilder().hasWrapperPolicy()) {
ObjectBuilder objectBuilder = this.elementDescriptor.getObjectBuilder();
List wrappedElements = new ArrayList(size);
for (int i = 0; i < size; i++) {
wrappedElements.add(objectBuilder.wrapObject(elements.get(i), session));
}
elements = wrappedElements;
}
ConversionManager conversionManager = session.getDatasourcePlatform().getConversionManager();
// populate container with a dummy so the container.set(i, obj) could be used later.
for (int i = 0; i < size; i++) {
((List) container).add(NOT_SET);
}
// insert the elements into container
boolean failed = false;
for (int i = 0; i < size; i++) {
AbstractRecord row = dbRows.get(i);
Object orderValue = row.get(this.listOrderField);
// order value is null
if (orderValue == null) {
failed = true;
break;
}
int intOrderValue = conversionManager.convertObject(orderValue, Integer.class);
try {
// one or more elements have the same order value
if (NOT_SET != ((List) container).set(intOrderValue, elements.get(i))) {
failed = true;
break;
}
} catch (IndexOutOfBoundsException indexException) {
// order value negative or greater/equal to size
failed = true;
break;
}
}
if (failed) {
((List) container).clear();
// extract order list - it will be set into exception or used by order correction.
List<Integer> orderList = new ArrayList(size);
for (int i = 0; i < size; i++) {
AbstractRecord row = dbRows.get(i);
Object orderValue = row.get(this.listOrderField);
if (orderValue == null) {
orderList.add(null);
} else {
orderList.add(conversionManager.convertObject(orderValue, Integer.class));
}
}
if (this.orderCorrectionType == OrderCorrectionType.READ || this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
// pair each element with its order index
List<IndexedObject> indexedElements = new ArrayList(size);
for (int i = 0; i < size; i++) {
indexedElements.add(new IndexedObject(orderList.get(i), elements.get(i)));
}
// put elements in order and add to container
((List) container).addAll(correctOrderList(indexedElements));
if (this.orderCorrectionType == OrderCorrectionType.READ_WRITE) {
// mark IndirectList to have broken order
((IndirectList) container).setIsListOrderBrokenInDb(true);
}
} else {
// this.orderCorrectionType == OrderCorrectionType.EXCEPTION
throw QueryException.listOrderFieldWrongValue(query, this.listOrderField, orderList);
}
}
return size > 0;
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class SimpleFetchGroupTests method verifyUnfetchedAttributes.
@Test
public void verifyUnfetchedAttributes() throws Exception {
EntityManager em = createEntityManager("fieldaccess");
try {
beginTransaction(em);
TypedQuery<Employee> q = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(p.owner.id) FROM PhoneNumber p)", Employee.class);
FetchGroup fg = new FetchGroup("Employee.empty");
q.setHint(QueryHints.FETCH_GROUP, fg);
Employee emp = q.getSingleResult();
assertNotNull(emp);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// This check using the mapping returns a default (empty) IndirectList
/*OneToManyMapping phoneMapping = (OneToManyMapping) employeeDescriptor.getMappingForAttributeName("phoneNumbers");
IndirectList phones = (IndirectList) phoneMapping.getAttributeValueFromObject(emp);
assertNotNull(phones);
assertTrue(phones.isInstantiated());
assertEquals(0, phones.size());
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());*/
IndirectList phonesIL = (IndirectList) emp.getPhoneNumbers();
assertFalse(phonesIL.isInstantiated());
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertTrue(emp.getPhoneNumbers().size() > 0);
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
use of org.eclipse.persistence.indirection.IndirectList in project eclipselink by eclipse-ee4j.
the class SimpleFetchGroupTests method verifyUnfetchedAttributes.
@Test
public void verifyUnfetchedAttributes() throws Exception {
EntityManager em = createEntityManager();
try {
beginTransaction(em);
TypedQuery<Employee> q = em.createQuery("SELECT e FROM Employee e WHERE e.id IN (SELECT MIN(p.id) FROM PhoneNumber p)", Employee.class);
FetchGroup fg = new FetchGroup("Employee.empty");
q.setHint(QueryHints.FETCH_GROUP, fg);
Employee emp = q.getSingleResult();
assertNotNull(emp);
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
// This check using the mapping returns a default (empty) IndirectList
/*OneToManyMapping phoneMapping = (OneToManyMapping) employeeDescriptor.getMappingForAttributeName("phoneNumbers");
IndirectList phones = (IndirectList) phoneMapping.getAttributeValueFromObject(emp);
assertNotNull(phones);
assertTrue(phones.isInstantiated());
assertEquals(0, phones.size());
assertEquals(1, getQuerySQLTracker(em).getTotalSQLSELECTCalls());*/
IndirectList phonesIL = (IndirectList) emp.getPhoneNumbers();
assertFalse(phonesIL.isInstantiated());
assertEquals(2, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
assertTrue(emp.getPhoneNumbers().size() > 0);
assertEquals(3, getQuerySQLTracker(em).getTotalSQLSELECTCalls());
} finally {
if (isTransactionActive(em)) {
rollbackTransaction(em);
}
closeEntityManager(em);
}
}
Aggregations