All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.FetchPlan Maven / Gradle / Ivy

Go to download

DataNucleus Core provides the primary components of a heterogenous Java persistence solution. It supports persistence API's being layered on top of the core functionality.

There is a newer version: 6.0.9
Show newest version
/**********************************************************************
Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
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.

Contributors:
    ...
**********************************************************************/
package org.datanucleus;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.util.ConcurrentReferenceHashMap;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.StringUtils;
import org.datanucleus.util.ConcurrentReferenceHashMap.ReferenceType;

/**
 * FetchPlan for fields for use internally.
 * A FetchPlan has a series of FetchPlanForClass objects being the fetch plan for particular classes.
 * Each FetchPlanForClass defines a series of fields of that class that are part of the fetch plan.
 * There are two types of fetch groups under consideration here.
 * 
    *
  • Static fetch groups, defined in MetaData (XML/Annotations).
  • *
  • Dynamic fetch groups, defined via an API.
  • *
*/ public class FetchPlan implements Serializable { private static final long serialVersionUID = 6031608568703439025L; /** Constant defining the fields in the default fetch group. */ public static final String DEFAULT = "default"; /** Constant defining all fields */ public static final String ALL = "all"; /** Constant defing no fields. */ public static final String NONE = "none"; /** Members that are loaded but not in current fetch plan should be unloaded before detachment. */ public static final int DETACH_UNLOAD_FIELDS = 2; /** Members that are not loaded but are in the current fetch plan should be loaded before detachment. */ public static final int DETACH_LOAD_FIELDS = 1; /** Fetch size to load all possible. */ public static final int FETCH_SIZE_GREEDY = -1; /** Fetch size for the implementation to decide how many to load. */ public static final int FETCH_SIZE_OPTIMAL = 0; public static final int RECURSION_DEPTH_UNLIMITED = -1; public static final int RECURSION_DEPTH_FK_ONLY = 0; /** Execution Context that this FetchPlan relates to. */ transient final ExecutionContext ec; // Defined as transient to avoid Serializable problems /** ClassLoader resolver. */ transient final ClassLoaderResolver clr; // Defined as transient to avoid Serializable problems /** Names of the "defined" fetch groups in the current FetchPlan. */ final Set groupNames = new HashSet<>(); /** The "dynamic" fetch groups in the current FetchPlan. */ transient Set dynamicGroups = null; // Defined as transient to avoid Serializable problems /** The Fetch size. For use when using large result sets. */ int fetchSize = FETCH_SIZE_OPTIMAL; /** Options to be used during detachment. Spec 12.7 says that the default is DETACH_LOAD_FIELDS. */ int detachmentOptions = FetchPlan.DETACH_LOAD_FIELDS; /** FetchPlanForClass keyed by the class name. **/ final transient Map fetchPlansByClassName = new HashMap<>(); /** Maximum depth to fetch from the root object. */ int maxFetchDepth = 1; /** The classes used as the roots for detachment (DetachAllOnCommit). */ Class[] detachmentRootClasses = null; /** The instances used as the roots for detachment (DetachAllOnCommit). */ Collection detachmentRoots = null; /** * Constructor. Initially has the default fetch group. * @param ec execution context * @param clr ClassLoader Resolver */ public FetchPlan(ExecutionContext ec, ClassLoaderResolver clr) { this.ec = ec; this.clr = clr; groupNames.add(FetchPlan.DEFAULT); // Extension property to define the default detachmentOptions String flds = ec.getNucleusContext().getConfiguration().getStringProperty("datanucleus.detachmentFields"); if (flds != null) { flds = flds.toLowerCase(); if (flds.equals("load-unload-fields")) { detachmentOptions = FetchPlan.DETACH_LOAD_FIELDS | FetchPlan.DETACH_UNLOAD_FIELDS; } else if (flds.equals("unload-fields")) { detachmentOptions = FetchPlan.DETACH_UNLOAD_FIELDS; } else if (flds.equals("load-fields")) { detachmentOptions = FetchPlan.DETACH_LOAD_FIELDS; } } } /** * Mark all managed fetch plans to be dirty, so the active members need to be recomputed. */ private void markDirty() { for (FetchPlanForClass fpCls : fetchPlansByClassName.values()) { fpCls.markDirty(); } } /** * Access the fetch plan for the class. * @param cmd metadata for the class * @return the FetchPlanForClass */ public synchronized FetchPlanForClass getFetchPlanForClass(AbstractClassMetaData cmd) { FetchPlanForClass fpClass = fetchPlansByClassName.get(cmd.getFullClassName()); if (fpClass == null) { fpClass = new FetchPlanForClass(cmd, this); fetchPlansByClassName.put(cmd.getFullClassName(), fpClass); } return fpClass; } /** * Method to add a group to the fetch plan. * @param grpName The fetch group to add * @return Updated Fetch Plan */ public synchronized FetchPlan addGroup(String grpName) { if (grpName != null) { boolean changed = groupNames.add(grpName); boolean dynChanged = addDynamicGroup(grpName); if (changed || dynChanged) { markDirty(); } } return this; } /** * Method to remove a group from the fetch plan. * @param grpName The fetch group to remove * @return Updated Fetch Plan */ public synchronized FetchPlan removeGroup(String grpName) { if (grpName != null) { boolean changed = false; changed = groupNames.remove(grpName); if (dynamicGroups != null) { Iterator iter = dynamicGroups.iterator(); while (iter.hasNext()) { FetchGroup grp = iter.next(); if (grp.getName().equals(grpName)) { grp.deregisterListener(this); // Deregister us from this group changed = true; iter.remove(); } } } if (changed) { markDirty(); } } return this; } /** * Method to clear the current groups and activate the DFG. * @return The FetchPlan */ public synchronized FetchPlan clearGroups() { clearDynamicGroups(); groupNames.clear(); markDirty(); return this; } /** * Accessor for the static groups for this FetchPlan. * Doesn't return the dynamic groups. * @return The fetch plan groups (unmodifiable) */ public synchronized Set getGroups() { return Collections.unmodifiableSet(new HashSet(groupNames)); } /** * Method to set the groups of the fetch plan. * @param grpNames Names of the groups * @return Updated Fetch Plan */ public synchronized FetchPlan setGroups(Collection grpNames) { clearDynamicGroups(); groupNames.clear(); if (grpNames != null) { groupNames.addAll(grpNames); for (String grpName : grpNames) { addDynamicGroup(grpName); } } markDirty(); return this; } /** * Method to set the groups using an array. * @param grpNames Names of the groups * @return The Fetch Plan */ public synchronized FetchPlan setGroups(String[] grpNames) { clearDynamicGroups(); groupNames.clear(); if (grpNames != null) { for (int i=0;i ecGrpsWithName = ec.getFetchGroupsWithName(grpName); if (ecGrpsWithName != null) { if (dynamicGroups == null) { dynamicGroups = new HashSet<>(); } for (FetchGroup grp : ecGrpsWithName) { dynamicGroups.add(grp); grp.registerListener(this); // Register us with this group changed = true; } } if (!changed) { // Check for group registered with NucleusContext Set grpsWithName = ec.getNucleusContext().getFetchGroupsWithName(grpName); if (grpsWithName != null) { if (dynamicGroups == null) { dynamicGroups = new HashSet<>(); } for (FetchGroup grp : grpsWithName) { dynamicGroups.add(grp); grp.registerListener(this); // Register us with this group changed = true; } } } return changed; } /** * Method to notify this FetchPlan that the specified FetchGroup has been updated. * dynamic fetch groups extension * @param group The dynamic FetchGroup */ public void notifyFetchGroupChange(FetchGroup group) { Collection fpClasses = fetchPlansByClassName.values(); for (FetchPlanForClass fpClass : fpClasses) { Class cls = clr.classForName(fpClass.cmd.getFullClassName()); if (cls.isAssignableFrom(group.getType()) || group.getType().isAssignableFrom(cls)) { // Mark all potentially related fetch plans dirty so they recalculate fpClass.markDirty(); } } } /** * Method to notify this FetchPlan that the specified FetchGroup has been updated. * dynamic fetch groups extension * @param group The dynamic FetchGroup */ public void notifyFetchGroupRemove(FetchGroup group) { dynamicGroups.remove(group); // Remove the group notifyFetchGroupChange(group); // Recalculate all groups fields } /** * Set the roots for DetachAllOnCommit * @param roots The roots of the detachment graph. * @return The fetch plan with these roots */ public FetchPlan setDetachmentRoots(Collection roots) { if (detachmentRootClasses != null || detachmentRoots != null) { throw new NucleusUserException(Localiser.msg("006003")); } if (roots == null) { detachmentRoots = null; } detachmentRoots = new ArrayList<>(); detachmentRoots.addAll(roots); return this; } /** * Accessor for the roots of the detachment graph for DetachAllOnCommit. * @return The roots of the detachment graph. */ public Collection getDetachmentRoots() { if (detachmentRoots == null) { return Collections.EMPTY_LIST; } return Collections.unmodifiableCollection(detachmentRoots); } /** * Set the classes used for roots of the detachment graph for DetachAllOnCommit. * @param rootClasses Classes to be used as roots of the detachment graph * @return The fetch plan with these roots */ public FetchPlan setDetachmentRootClasses(Class[] rootClasses) { if (detachmentRootClasses != null || detachmentRoots != null) { throw new NucleusUserException(Localiser.msg("006003")); } if (rootClasses == null) { detachmentRootClasses = null; return this; } detachmentRootClasses = new Class[rootClasses.length]; for (int i=0;i(dynamicGroups); } for (Map.Entry entry : this.fetchPlansByClassName.entrySet()) { String className = entry.getKey(); FetchPlanForClass fpcls = entry.getValue(); fp.fetchPlansByClassName.put(className, fpcls.getCopy(fp)); } fp.fetchSize = this.fetchSize; return fp; } /** * Cache the result of FetchPlanImpl.isToCallPostLoadFetchPlan(): * for a given set of loaded members of a certain class. Must be * invalidated with any change of fields to load, e.g. adding a fetchgroup. */ private transient Map> isToCallPostLoadFetchPlanByCmd; Boolean getCachedIsToCallPostLoadFetchPlan(AbstractClassMetaData cmd, BitSet loadedFields) { if (isToCallPostLoadFetchPlanByCmd == null) { isToCallPostLoadFetchPlanByCmd = new ConcurrentReferenceHashMap<>(1, ReferenceType.STRONG, ReferenceType.SOFT); } Map cachedIsToCallPostLoadFetchPlan = isToCallPostLoadFetchPlanByCmd.get(cmd); if (cachedIsToCallPostLoadFetchPlan==null) { return null; } return (Boolean) cachedIsToCallPostLoadFetchPlan.get(loadedFields); } void cacheIsToCallPostLoadFetchPlan(AbstractClassMetaData cmd, BitSet loadedFields, Boolean itcplfp) { if (isToCallPostLoadFetchPlanByCmd == null) { isToCallPostLoadFetchPlanByCmd = new ConcurrentReferenceHashMap<>(1, ReferenceType.STRONG, ReferenceType.SOFT); } Map cachedIsToCallPostLoadFetchPlan = isToCallPostLoadFetchPlanByCmd.get(cmd); if (cachedIsToCallPostLoadFetchPlan == null) { cachedIsToCallPostLoadFetchPlan = new ConcurrentReferenceHashMap<>(1, ReferenceType.STRONG, ReferenceType.SOFT); isToCallPostLoadFetchPlanByCmd.put(cmd, cachedIsToCallPostLoadFetchPlan); } cachedIsToCallPostLoadFetchPlan.put(loadedFields, itcplfp); } void invalidateCachedIsToCallPostLoadFetchPlan(AbstractClassMetaData cmd) { if (isToCallPostLoadFetchPlanByCmd == null) { isToCallPostLoadFetchPlanByCmd = new ConcurrentReferenceHashMap<>(1, ReferenceType.STRONG, ReferenceType.SOFT); } Map cachedIsToCallPostLoadFetchPlan = isToCallPostLoadFetchPlanByCmd.get(cmd); if (cachedIsToCallPostLoadFetchPlan != null) { cachedIsToCallPostLoadFetchPlan.clear(); } } public String toStringWithClasses() { return "FetchPlan " + groupNames.toString() + " classes=" + StringUtils.collectionToString(Collections.unmodifiableCollection(fetchPlansByClassName.values())); } public String toString() { return "FetchPlan " + groupNames.toString(); } }