![JAR search and dependency download from the Maven repository](/logo.png)
com.gs.fw.common.mithra.finder.DeepFetchNode Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of reladomo Show documentation
Show all versions of reladomo Show documentation
Reladomo is an object-relational mapping framework.
/*
Copyright 2016 Goldman Sachs.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package com.gs.fw.common.mithra.finder;
import com.gs.collections.impl.list.mutable.FastList;
import com.gs.collections.impl.set.mutable.UnifiedSet;
import com.gs.fw.common.mithra.DeepFetchTree;
import com.gs.fw.common.mithra.MithraManagerProvider;
import com.gs.fw.common.mithra.MithraObjectPortal;
import com.gs.fw.common.mithra.attribute.Attribute;
import com.gs.fw.common.mithra.cache.FullUniqueIndex;
import com.gs.fw.common.mithra.extractor.Extractor;
import com.gs.fw.common.mithra.extractor.IdentityExtractor;
import com.gs.fw.common.mithra.notification.MithraDatabaseIdentifierExtractor;
import com.gs.fw.common.mithra.querycache.CachedQuery;
import com.gs.fw.common.mithra.tempobject.TupleTempContext;
import com.gs.fw.common.mithra.util.*;
import com.gs.fw.finder.Navigation;
import java.io.Serializable;
import java.util.*;
import java.util.concurrent.*;
public class DeepFetchNode implements Serializable, DeepFetchTree
{
private static final Comparator super Attribute> ADHOC_ATTRIBUTE_COMPARATOR = new Comparator()
{
@Override
public int compare(Attribute o1, Attribute o2)
{
if (o1.equals(o2))
{
return 0;
}
int leftRank = o1.isAsOfAttribute() ? 100 : 0;
int rightRank = o2.isAsOfAttribute() ? 100 : 0;
if (leftRank == rightRank)
{
return o1.getAttributeName().compareTo(o2.getAttributeName());
}
else
{
return leftRank - rightRank;
}
}
};
private long queryTime;
private DeepFetchNode parent;
private FastList children;
private transient List cachedQueryList;
private AbstractRelatedFinder relatedFinder;
private Operation originalOperation;
private Operation simplifiedOperation;
private boolean resolved = false;
private boolean fullyResolved = false;
private transient List resolvedList;
private transient List[] chainedResults;
private static final int PERCENT_COMPLETE_TO_IGNORE = 80;
private static final ExecutorWithFinish CURRENT_THREAD_EXECUTOR = new CurrentThreadExecutorService();
public DeepFetchNode(DeepFetchNode parent, AbstractRelatedFinder relatedFinder)
{
this.parent = parent;
this.relatedFinder = relatedFinder;
}
public DeepFetchNode copyForAdHoc()
{
return copyForAdHoc(null);
}
protected DeepFetchNode copyForAdHoc(DeepFetchNode parent)
{
DeepFetchNode result = new DeepFetchNode(parent, this.relatedFinder);
result.resolved = this.resolved;
result.fullyResolved = this.fullyResolved;
if (cachedQueryList != null)
{
result.cachedQueryList = FastList.newList(this.cachedQueryList);
}
result.resolvedList = this.resolvedList;
result.chainedResults = this.chainedResults;
if (children != null)
{
result.children = FastList.newList(children.size());
for(int i=0;i getChildren()
{
if (children == null) return ListFactory.EMPTY_LIST;
return (List) (List) this.children.toImmutable();
}
public void setResolvedList(List resolvedList, int chainPosition)
{
if (chainedResults == null || chainPosition == chainedResults.length - 1)
{
this.resolvedList = resolvedList;
}
else
{
this.chainedResults[chainPosition] = resolvedList;
}
}
public boolean add(Navigation nav)
{
AbstractRelatedFinder arf = (AbstractRelatedFinder) nav;
DeepRelationshipAttribute parentAttribute = arf.getParentDeepRelationshipAttribute();
InternalList fullList = new InternalList(parentAttribute == null ? 1 : 6);
fullList.add(arf);
while(parentAttribute != null)
{
fullList.add(parentAttribute);
parentAttribute = parentAttribute.getParentDeepRelationshipAttribute();
}
boolean added = this.addAll(fullList);
if (added)
{
this.fullyResolved = false;
}
return added;
}
private boolean addAll(InternalList fullList)
{
int end = fullList.size() - 1;
DeepFetchNode cur = this;
boolean added = false;
while(end >= 0)
{
AbstractRelatedFinder toAdd = (AbstractRelatedFinder) fullList.get(end);
DeepFetchNode found = null;
if (cur.children != null)
{
for(int i=0;i childrenToProcess = FastList.newList(this.children.size());
if (!bypassCache)
{
for(int i=0;i childrenToProcess)
{
List originalResolvedList = this.resolvedList;
List segregatedBySource = this.segregateBySource(originalResolvedList);
try
{
for (int s = 0; s < segregatedBySource.size(); s++)
{
Map> attributesToNodesMap = new HashMap>(this.children.size());
List resolvedBySourceList = segregatedBySource.get(s);
this.resolvedList = resolvedBySourceList;
UnifiedSet nonConstantSet = new UnifiedSet();
UnifiedSet constantSet = new UnifiedSet();
for (int i = 0; i < childrenToProcess.size(); i++)
{
DeepFetchNode child = childrenToProcess.get(i);
Set leftAttributeSet = child.relatedFinder.zGetMapper().getAllLeftAttributes();
Extractor sourceAttribute = computeSourceAttribute(child.relatedFinder, leftAttributeSet);
removeConstants(leftAttributeSet, resolvedBySourceList, constantSet, nonConstantSet);
Extractor[] leftAttributesWithoutFilters = child.relatedFinder.zGetMapper().getLeftAttributesWithoutFilters();
UnifiedSet leftAttributesWithoutFiltersSet = UnifiedSet.newSet(leftAttributesWithoutFilters.length);
for(Extractor e: leftAttributesWithoutFilters)
{
leftAttributesWithoutFiltersSet.add((Attribute) e);
}
UnifiedSet localConstants = UnifiedSet.newSet(leftAttributesWithoutFiltersSet);
localConstants.removeAll(nonConstantSet);
leftAttributesWithoutFiltersSet.removeAll(constantSet);
if (!isNullConstant(localConstants, resolvedBySourceList))
{
if (leftAttributeSet.isEmpty() || child.isMappableForTempJoin(leftAttributeSet))
{
DeepFetchKey key = new DeepFetchKey(leftAttributeSet, leftAttributesWithoutFiltersSet, sourceAttribute, child.getLeftPersisterId());
List nodeList = attributesToNodesMap.get(key);
if (nodeList == null)
{
nodeList = new FastList(2);
attributesToNodesMap.put(key, nodeList);
}
nodeList.add(child);
}
else
{
child.deepFetchAdhocOneAtATime();
}
}
else
{
child.setEmptyResult();
}
}
for (Map.Entry> entry : attributesToNodesMap.entrySet())
{
List children = entry.getValue();
DeepFetchKey deepFetchKey = entry.getKey();
if (deepFetchKey.leftAttributesWithFilters.isEmpty())
{
//all the join values were constant
for (int i = 0; i < children.size(); i++)
{
children.get(i).setResolvedFromOne();
}
}
else
{
Attribute singleAttribute = deepFetchKey.leftAttributesWithFilters.iterator().next();
if (deepFetchKey.leftAttributesWithFilters.size() == 1 && !singleAttribute.isAsOfAttribute() && resolvedList.size() < DeepRelationshipUtility.MAX_SIMPLIFIED_IN)
{
FastList chainedChildren = FastList.newList(children.size());
for (int i = 0; i < children.size(); i++)
{
DeepFetchNode child = children.get(i);
boolean add = true;
if (child.relatedFinder.isSimple())
{
add = !child.deepFetchSelfWithInClause(singleAttribute, resolvedBySourceList);
}
if (add)
{
chainedChildren.add(child);
}
}
if (!chainedChildren.isEmpty())
{
deepFetchWithTempContext(resolvedBySourceList, chainedChildren, deepFetchKey);
}
}
else
{
deepFetchWithTempContext(resolvedBySourceList, children, deepFetchKey);
}
}
}
}
}
finally
{
this.resolvedList = originalResolvedList;
}
}
private boolean isNullConstant(UnifiedSet constantSet, List resolvedBySourceList)
{
if (constantSet.isEmpty()) return false;
Object one = resolvedBySourceList.get(0);
for(Attribute a: constantSet)
{
if (a.isAttributeNull(one))
{
return true;
}
}
return false;
}
private Extractor computeSourceAttribute(AbstractRelatedFinder relatedFinder, Set leftAttributeSet)
{
Mapper mapper = relatedFinder.zGetMapper();
Attribute sourceAttribute = mapper.getAnyRightAttribute().getSourceAttribute();
if (sourceAttribute != null)
{
//either we can rely on the mapping to the parent to have an equality to the source attribute, or
//the source is in the mapper.
MithraDatabaseIdentifierExtractor extractor = new MithraDatabaseIdentifierExtractor();
MappedOperation mappedOperation = new MappedOperation(mapper, new All(mapper.getAnyLeftAttribute()));
mappedOperation.registerOperation(extractor, true);
if (extractor.hasEqualitySourceOperations())
{
sourceAttribute = leftAttributeSet.iterator().next().getSourceAttribute();
}
else
{
mapper.pushMappers(extractor);
SourceOperation sourceOperation = extractor.getSourceOperation(extractor.getCurrentMapperList());
if (sourceOperation instanceof OperationWithParameterExtractor)
{
return ((OperationWithParameterExtractor) sourceOperation).getParameterExtractor();
}
}
}
return sourceAttribute;
}
private void deepFetchWithTempContext(List resolvedBySourceList, List children, DeepFetchKey deepFetchKey)
{
TupleTempContext tempContext = null;
try
{
tempContext = createTempTableForDeepFetch(deepFetchKey.leftAttributesWithoutFilters, children.get(0).relatedFinder, resolvedBySourceList, deepFetchKey.sourceAttribute);
if (tempContext != null)
{
for (int i = 0; i < children.size(); i++)
{
children.get(i).deepFetchSelfWithTempContext(tempContext, null);
}
}
else
{
for (int i = 0; i < children.size(); i++)
{
children.get(i).setEmptyResult();
}
}
}
finally
{
if (tempContext != null) tempContext.destroy();
}
}
private void deepFetchAdhocOneAtATime()
{
FullUniqueIndex resolvedIndex = new FullUniqueIndex("", IdentityExtractor.getArrayInstance());
for(int i=0;i leftAttributeSet)
{
return this.relatedFinder.zGetMapper().isMappableForTempJoin(leftAttributeSet);
}
private void setResolvedFromOne()
{
if (this.relatedFinder.isToOne())
{
Object related = this.relatedFinder.plainValueOf(parent.resolvedList.get(0));
if (related == null)
{
this.resolvedList = ListFactory.EMPTY_LIST;
}
else
{
this.resolvedList = ListFactory.create(related);
}
}
else
{
this.resolvedList = (List) this.relatedFinder.plainValueOf(parent.resolvedList.get(0));
}
this.resolved = true;
}
private List segregateBySource(List resolvedList)
{
Attribute sourceAttribute = this.relatedFinder.getSourceAttribute();
if (sourceAttribute == null || resolvedList.size() == 1) return ListFactory.create(resolvedList);
MultiHashMap map = null;
Object first = resolvedList.get(0);
for(int i=0;i < resolvedList.size(); i++)
{
Object current = resolvedList.get(i);
if (map != null)
{
map.put(sourceAttribute.valueOf(current), current);
}
else if (!sourceAttribute.valueEquals(first, current))
{
map = new MultiHashMap();
Object firstSource = sourceAttribute.valueOf(first);
for(int j=0;j leftAttributeSet, List resolvedBySourceList, Set constantSet, Set nonConstantSet)
{
if (resolvedBySourceList.size() == 1)
{
leftAttributeSet.clear(); // everything is constant anyway
return;
}
for(Iterator attributeIterator = leftAttributeSet.iterator(); attributeIterator.hasNext(); )
{
Attribute a = attributeIterator.next();
if (constantSet.contains(a))
{
attributeIterator.remove();
}
else if (!nonConstantSet.contains(a))
{
boolean constant = true;
Object first = resolvedBySourceList.get(0);
for(int i=1;i allLeftAttributes, DeepFetchNode child, List parentList)
{
TupleTempContext tempContext = null;
try
{
tempContext = createTempTableForDeepFetch(allLeftAttributes, child.relatedFinder, parentList, computeSourceAttribute(child.relatedFinder, allLeftAttributes));
if (tempContext != null)
{
child.deepFetchSelfWithTempContext(tempContext, parentList);
}
else
{
child.setEmptyResult();
}
}
finally
{
if (tempContext != null) tempContext.destroy();
}
}
private void deepFetchSelfWithTempContext(TupleTempContext tempContext, List immediateParentList)
{
addToCachedQueryList(this.relatedFinder.zDeepFetchWithTempContext(this, tempContext, parent.resolvedList.get(0), immediateParentList));
}
private boolean deepFetchSelfWithInClause(Attribute singleAttribute, List parentList)
{
List list = this.relatedFinder.zDeepFetchWithInClause(this, singleAttribute, parentList);
if (list == null)
{
return false;
}
addToCachedQueryList(list);
return true;
}
private TupleTempContext createTempTableForDeepFetch(Set allLeftAttributes, AbstractRelatedFinder relatedFinder, List parentList, Extractor sourceAttribute)
{
Attribute[] arrayAttributes = new Attribute[allLeftAttributes.size()];
allLeftAttributes.toArray(arrayAttributes);
Arrays.sort(arrayAttributes, ADHOC_ATTRIBUTE_COMPARATOR);
parentList = filterParentWithNullsOrFilters(arrayAttributes, relatedFinder.zGetMapper().filterLeftObjectList(parentList));
TupleTempContext result = null;
if (!parentList.isEmpty())
{
result = new TupleTempContext(arrayAttributes, sourceAttribute, null, true);
result.enableRetryHook();
result.insert(parentList, relatedFinder.zGetMapper().getFromPortal(), 100, false); //todo: set isParallel to true after implementing parallel deep fetch.
}
return result;
}
private List filterParentWithNullsOrFilters(Attribute[] arrayAttributes, List parentList)
{
List result = null;
for(int i=0;i set = new UnifiedSet();
mapper.addDepenedentPortalsToSet(set);
Iterator portals = set.iterator();
PersisterId id = portals.next().getPersisterId();
while(portals.hasNext())
{
if (!id.equals(portals.next().getPersisterId())) return true;
}
return false;
}
public void addToResolvedList(List all, int chainPosition)
{
if (chainedResults == null || chainPosition == chainedResults.length - 1)
{
if (this.resolvedList == null || this.resolvedList.isEmpty())
{
this.resolvedList = all;
}
else
{
List temp = new MithraFastList(this.resolvedList.size() + all.size());
temp.addAll(this.resolvedList);
temp.addAll(all);
this.resolvedList = temp;
}
}
else
{
if (this.chainedResults[chainPosition] == null || this.chainedResults[chainPosition].isEmpty())
{
this.chainedResults[chainPosition] = all;
}
else
{
List temp = new MithraFastList(this.chainedResults[chainPosition].size() + all.size());
temp.addAll(this.chainedResults[chainPosition]);
temp.addAll(all);
this.chainedResults[chainPosition] = temp;
}
}
}
private static class DeepFetchKey
{
private Set leftAttributesWithFilters;
private Set leftAttributesWithoutFilters;
private Extractor sourceAttribute;
private PersisterId persisterId;
private int hashCode = 0;
private DeepFetchKey(Set leftAttributesWithFilters, Set leftAttributesWithoutFilters, Extractor sourceAttribute, PersisterId persisterId)
{
this.leftAttributesWithFilters = leftAttributesWithFilters;
this.leftAttributesWithoutFilters = leftAttributesWithoutFilters;
this.sourceAttribute = sourceAttribute;
this.persisterId = persisterId;
}
private static boolean nullSafeEquals(Object value, Object other)
{
if (value == null)
{
if (other == null)
{
return true;
}
}
else if (other == value || value.equals(other))
{
return true;
}
return false;
}
@Override
public boolean equals(Object o)
{
if (this == o) return true;
DeepFetchKey that = (DeepFetchKey) o;
return leftAttributesWithFilters.equals(that.leftAttributesWithFilters) && persisterId.equals(that.persisterId) && nullSafeEquals(this.sourceAttribute, that.sourceAttribute);
}
@Override
public int hashCode()
{
if (hashCode == 0) hashCode = computeHashCode();
return hashCode;
}
public int computeHashCode()
{
int h = HashUtil.combineHashes(leftAttributesWithFilters.hashCode(), persisterId.hashCode());
if (this.sourceAttribute != null)
{
h = HashUtil.combineHashes(h, this.sourceAttribute.hashCode());
}
return h;
}
}
private static class CurrentThreadExecutorService implements ExecutorWithFinish
{
public void execute(Runnable command)
{
command.run();
}
public void finish()
{
//nothing to do
}
}
private class DeepFetchRunnable implements Runnable
{
private DeepFetchRunnable parentRunnable;
private boolean bypassCache;
private Executor executor;
private int childCount;
private boolean forceImplicitJoin;
private DeepFetchRunnable(DeepFetchRunnable parentRunnable, boolean bypassCache, Executor executor, boolean forceImplicitJoin)
{
this.parentRunnable = parentRunnable;
this.bypassCache = bypassCache;
this.executor = executor;
this.forceImplicitJoin = forceImplicitJoin;
}
public void run()
{
if (!bypassCache)
{
if (relatedFinder.getMithraObjectPortal().isCacheDisabled())
{
bypassCache = true;
}
DeepFetchNode parent = DeepFetchNode.this.parent;
while (!bypassCache && parent != null)
{
if (parent.relatedFinder.getMithraObjectPortal().isCacheDisabled())
{
bypassCache = true;
}
parent = parent.parent;
}
}
if (bypassCache || isRecursivelyPartiallyCached())
{
List cachedQueryList = relatedFinder.zDeepFetch(DeepFetchNode.this, bypassCache, forceImplicitJoin);
addToCachedQueryList(cachedQueryList);
}
DeepFetchNode.this.resolved = true;
int childCount = deepFetchChildren(bypassCache, executor, this, forceImplicitJoin);
if (childCount == 0)
{
decrementParent();
}
}
private void decrementParent()
{
if (parentRunnable != null&& parentRunnable.decrementChildCount())
{
parentRunnable.runAfterChildren();
}
}
private void runAfterChildren()
{
fullyResolved = true;
decrementParent();
}
private synchronized void waitForChildren()
{
while(childCount > 0)
{
try
{
this.wait();
}
catch (InterruptedException e)
{
//ignore
}
}
}
private synchronized boolean decrementChildCount()
{
childCount--;
if (childCount == 0)
{
notifyAll();
return true;
}
return false;
}
public synchronized void setChildCount(int childCount)
{
this.childCount = childCount;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy