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

org.eclipse.persistence.descriptors.FetchGroupManager Maven / Gradle / Ivy

There is a newer version: 5.0.0-B03
Show newest version
/*
 * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
//     05/19/2010-2.1 ailitchev - Bug 244124 - Add Nested FetchGroup
package org.eclipse.persistence.descriptors;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;

import org.eclipse.persistence.descriptors.changetracking.ObjectChangePolicy;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.queries.AttributeItem;
import org.eclipse.persistence.internal.queries.EntityFetchGroup;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.internal.sessions.UnitOfWorkImpl;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.queries.AttributeGroup;
import org.eclipse.persistence.queries.FetchGroup;
import org.eclipse.persistence.queries.FetchGroupTracker;

/**
 * 

Purpose: The fetch group manager controls the named fetch groups defined at * the descriptor level. EclipseLink supports multiple, overlapped fetch groups, optionally with * one of them as the default fetch group. *

* The domain object must implement org.eclipse.persistence.queries.FetchGroupTracker interface, * in order to make use of the fetch group performance enhancement feature. *

* Please refer to FetchGroup class for the pros and cons of fetch group usage. * * @see org.eclipse.persistence.queries.FetchGroup * @see org.eclipse.persistence.queries.FetchGroupTracker * * @author King Wang, dclarke * @since TopLink 10.1.3. */ public class FetchGroupManager implements Cloneable, java.io.Serializable { //The group map is keyed by the group name, valued by the fetch group object. private Map fetchGroups = null; // EntityFetchGroups mapped by their AttributeNames Sets. private transient Map, EntityFetchGroup> entityFetchGroups = new ConcurrentHashMap(); //default fetch group private FetchGroup defaultFetchGroup; private EntityFetchGroup defaultEntityFetchGroup; // full fetch group - contains all attributes, none of them nested. private FetchGroup fullFetchGroup; // minimal fetch group - contains primary key attribute(s) and version. private FetchGroup minimalFetchGroup; // identity fetch group - contains primary key attribute(s) only. private EntityFetchGroup idEntityFetchGroup; // non relational fetch group - contains intersection of non-relational attributes // and defaultEntityFetchGroup. private EntityFetchGroup nonReferenceEntityFetchGroup; //ref to the descriptor private ClassDescriptor descriptor; // indicates whether defaultFetchGroup should be copied from the parent if not set. private boolean shouldUseInheritedDefaultFetchGroup = true; /** * Constructor */ public FetchGroupManager() { } /** * Add a named fetch group to the descriptor */ public void addFetchGroup(FetchGroup group) { //create a new fetch group and put it in the group map. getFetchGroups().put(group.getName(), group); } /** * Return the fetch group map: keyed by the group name, valued by the fetch group object. */ public Map getFetchGroups() { if (this.fetchGroups == null) { //lazy initialized this.fetchGroups = new HashMap<>(2); } return this.fetchGroups; } /** * Return the descriptor-level default fetch group. * All read object and read all queries would use the default fetch group if no fetch group * is explicitly defined for the query, unless setShouldUseDefaultFetchGroup(false); is also * called on the query. * * Default fetch group should be used carefully. It would be beneficial if most of the system queries * are for the subset of the object, so un-needed attributes data would not have to be read, and the * users do not have to setup every query for the given fetch group, as default one is always used. * However, if queries on object are mostly use case specific and not systematic, using default fetch group * could cause undesirable extra round-trip and performance degradation. * * @see org.eclipse.persistence.queries.ObjectLevelReadQuery#setShouldUseDefaultFetchGroup(boolean) */ public FetchGroup getDefaultFetchGroup() { return this.defaultFetchGroup; } /** * INTERNAL: * Returns EntityFetchGroup corresponding to default FetchGroup. */ public EntityFetchGroup getDefaultEntityFetchGroup() { return this.defaultEntityFetchGroup; } /** * PUBLIC: * Returns clone of the minimal fetch group. * Could be used as a starting point for a new user-defined fetch group. */ public FetchGroup createMinimalFetchGroup() { return this.minimalFetchGroup.clone(); } /** * PUBLIC: * Indicates whether the passed fetch group is minimal. */ public boolean isMinimalFetchGroup(FetchGroup fetchGroup) { return this.minimalFetchGroup.equals(fetchGroup); } /** * INTERNAL: * Returns EntityFetchGroup corresponding to primary key attribute(s). */ public EntityFetchGroup getIdEntityFetchGroup() { return this.idEntityFetchGroup; } /** * INTERNAL: * Returns EntityFetchGroup corresponding to non relational attributes * intersected with defaultFetchGroup. */ public EntityFetchGroup getNonReferenceEntityFetchGroup() { return this.nonReferenceEntityFetchGroup; } /** * INTERNAL: * Returns EntityFetchGroup corresponding to non relational attributes * intersected with defaultFetchGroup. */ public EntityFetchGroup getNonReferenceEntityFetchGroup(boolean addPk, boolean addVersion) { if(addPk && addVersion) { return getNonReferenceEntityFetchGroup(); } FetchGroup nonReferenceFetchGroup = new FetchGroup(); for (DatabaseMapping mapping : getDescriptor().getMappings()) { if(!mapping.isForeignReferenceMapping()) { String name = mapping.getAttributeName(); if(this.defaultEntityFetchGroup == null || this.defaultEntityFetchGroup.containsAttribute(name)) { nonReferenceFetchGroup.addAttribute(name); } } } if(addPk) { for(DatabaseMapping mapping : descriptor.getObjectBuilder().getPrimaryKeyMappings()) { String name = mapping.getAttributeName(); if(!nonReferenceFetchGroup.containsAttribute(name)) { nonReferenceFetchGroup.addAttribute(name); } } } else { for(DatabaseMapping mapping : descriptor.getObjectBuilder().getPrimaryKeyMappings()) { if(mapping.isForeignReferenceMapping()) { String name = mapping.getAttributeName(); if(!nonReferenceFetchGroup.containsAttribute(name)) { // always add foreign reference pk nonReferenceFetchGroup.addAttribute(name); } } } } if(addVersion) { String lockAttribute = descriptor.getObjectBuilder().getLockAttribute(); if(lockAttribute != null) { if(!nonReferenceFetchGroup.containsAttribute(lockAttribute)) { nonReferenceFetchGroup.addAttribute(lockAttribute); } } } return getEntityFetchGroup(nonReferenceFetchGroup); } /** * INTERNAL: * Add primary key and version attributes to the passed fetch group. */ public void addMinimalFetchGroup(FetchGroup fetchGroup) { if (this.minimalFetchGroup == null) { return; } Iterator it = this.minimalFetchGroup.getAttributeNames().iterator(); while(it.hasNext()) { String name = it.next(); if(!fetchGroup.containsAttribute(name)) { fetchGroup.addAttribute(name); } } } /** * PUBLIC: * Add primary key and version attributes to the passed fetch group * and all the fetch group it contains. * Also verifies that all the attributes have corresponding mappings. * Could be used for fetch group preparation and validation. * Called by ObjectLevelReadQuery prepareFetchgroup method. */ public void prepareAndVerify(FetchGroup fetchGroup) { prepareAndVerifyInternal(fetchGroup, ""); } /** * INTERNAL: * Add primary key and version attributes to the passed fetch group * and all the fetch group it contains. * Also verifies that all the attributes have corresponding mappings. */ protected void prepareAndVerifyInternal(FetchGroup fetchGroup, String attributePrefix) { addMinimalFetchGroup(fetchGroup); ObjectBuilder builder = this.descriptor.getObjectBuilder(); if (fetchGroup.isValidated()){ return; } Iterator> it = fetchGroup.getAllItems().entrySet().iterator(); while(it.hasNext()) { Map.Entry entry = it.next(); String name = entry.getKey(); DatabaseMapping mapping = builder.getMappingForAttributeName(name); if(mapping != null) { FetchGroup nestedFetchGroup = (FetchGroup)entry.getValue().getGroup(); if(nestedFetchGroup != null) { if(mapping.isForeignReferenceMapping()) { ClassDescriptor referenceDescriptor = mapping.getReferenceDescriptor(); if(referenceDescriptor != null) { FetchGroupManager nestedFetchGroupManager = referenceDescriptor.getFetchGroupManager(); if(nestedFetchGroupManager != null) { nestedFetchGroupManager.prepareAndVerifyInternal(nestedFetchGroup, attributePrefix + name + '.'); } else { // target descriptor does not support fetch groups throw ValidationException.fetchGroupHasWrongReferenceClass(fetchGroup, name); } } else { // no reference descriptor found throw ValidationException.fetchGroupHasWrongReferenceAttribute(fetchGroup, name); } } else if (mapping.isAggregateObjectMapping()){ ClassDescriptor referenceDescriptor = mapping.getReferenceDescriptor(); if(referenceDescriptor != null) { FetchGroupManager nestedFetchGroupManager = referenceDescriptor.getFetchGroupManager(); if(nestedFetchGroupManager != null) { nestedFetchGroupManager.prepareAndVerifyInternal(nestedFetchGroup, attributePrefix + name + '.'); } else { // target descriptor does not support fetch groups throw ValidationException.fetchGroupHasWrongReferenceClass(fetchGroup, name); } } else { // no reference descriptor found throw ValidationException.fetchGroupHasWrongReferenceAttribute(fetchGroup, name); } } else { // no reference mapping found throw ValidationException.fetchGroupHasWrongReferenceAttribute(fetchGroup, name); } } } else { // no mapping found throw ValidationException.fetchGroupHasUnmappedAttribute(fetchGroup, name); } } } /** * PUBLIC: * Returns clone of the default fetch group. * Could be used as a starting point for a new user-defined fetch group. */ public FetchGroup createDefaultFetchGroup() { return this.defaultFetchGroup.clone(); } /** * PUBLIC: * Returns clone of the full fetch group - contains all the attributes, no nesting. * Could be used as a starting point for a new user-defined fetch group. */ public FetchGroup createFullFetchGroup() { return this.fullFetchGroup.clone(); } /** * PUBLIC: * Indicates whether the passed fetch group contains all the attributes, no nesting. */ public boolean isFullFetchGroup(FetchGroup fetchGroup) { return this.fullFetchGroup.equals(fetchGroup); } /** * INTERNAL: * Returns entity fetch group corresponding to the passed set of attributes. */ public EntityFetchGroup getEntityFetchGroup(Set attributeNames) { if(attributeNames == null || attributeNames.isEmpty()) { return null; } EntityFetchGroup entityFetchGroup = this.entityFetchGroups.get(attributeNames); if(entityFetchGroup == null) { entityFetchGroup = new EntityFetchGroup(attributeNames); // EntityFetchGroup that contains all attributes is equivalent to no fetch group if(entityFetchGroup.equals(this.fullFetchGroup)) { return null; } this.entityFetchGroups.put(entityFetchGroup.getAttributeNames(), entityFetchGroup); } return entityFetchGroup; } /** * INTERNAL: * Returns entity fetch group corresponding to the passed fetch group. */ public EntityFetchGroup getEntityFetchGroup(FetchGroup fetchGroup) { if(fetchGroup == null) { return null; }else{ return fetchGroup.getEntityFetchGroup(this); } } /** * Return a pre-defined named fetch group. * * Lookup the FetchGroup to use given a name taking into * consideration descriptor inheritance to ensure parent descriptors are * searched for named FetchGroups. */ public FetchGroup getFetchGroup(String groupName) { FetchGroup fg = this.fetchGroups.get(groupName); if (fg == null){ AttributeGroup ag = this.descriptor.getAttributeGroup(groupName); if (ag != null){ fg = ag.toFetchGroup(); } } if (fg == null && getDescriptor().isChildDescriptor()) { ClassDescriptor current = this.descriptor; while (fg == null && current.isChildDescriptor()) { ClassDescriptor parent = current.getInheritancePolicy().getParentDescriptor(); if (parent.hasFetchGroupManager()) { fg = parent.getFetchGroupManager().getFetchGroup(groupName); } if (fg == null){ AttributeGroup ag = parent.getAttributeGroup(groupName); if (ag != null){ fg = ag.toFetchGroup(); } } current = parent; } } return fg; } /** * Lookup the FetchGroup to use given a name and a flag taking into * consideration descriptor inheritance to ensure parent descriptors are * searched for named and default FetchGroup. This is used to determine the * FetchGroup to use in a query's prepare. */ public FetchGroup getFetchGroup(String groupName, boolean useDefault) { FetchGroup fg = null; if (groupName != null) { fg = getFetchGroup(groupName); } // Process default using hierarchy if (fg == null && useDefault) { fg = getDefaultFetchGroup(); } return fg; } /** * Set the descriptor-level default fetch group. * All read object and read all queries would use the default fetch group if no fetch group is * explicitly defined for the query, unless setShouldUseDefaultFetchGroup(false); * is also called on the query. * * Default fetch group should be used carefully. It would be beneficial if most of the system queries * are for the subset of the object, so un-needed attributes data would not have to be read, and the * users do not have to setup every query for the given fetch group, as default one is always used. * However, if queries on object are mostly use case specific and not systematic, using default fetch group * could cause undesirable extra round-trip and performance degradation. * * @see org.eclipse.persistence.queries.ObjectLevelReadQuery#setShouldUseDefaultFetchGroup(boolean) */ public void setDefaultFetchGroup(FetchGroup newDefaultFetchGroup) { if(this.defaultFetchGroup != newDefaultFetchGroup) { if(this.descriptor.isFullyInitialized()) { // don't do that before descriptors are initialized. if(newDefaultFetchGroup != null) { prepareAndVerify(newDefaultFetchGroup); this.defaultEntityFetchGroup = this.getEntityFetchGroup(newDefaultFetchGroup); } else { this.defaultEntityFetchGroup = null; } } this.defaultFetchGroup = newDefaultFetchGroup; if(this.descriptor.isFullyInitialized()) { // don't do that before descriptors are initialized. initNonReferenceEntityFetchGroup(); } } } /** * INTERNAL: * Return true if the object is partially fetched and cached. * It applies to the query with fetch group. */ public boolean isPartialObject(Object domainObject) { if (domainObject != null) { FetchGroup fetchGroupInCache = ((FetchGroupTracker)domainObject)._persistence_getFetchGroup(); //if the fetch group reference is not null, it means the object is partial. return (fetchGroupInCache != null); } return false; } /** * INTERNAL: * Return if the cached object data is sufficiently valid against a fetch group */ public boolean isObjectValidForFetchGroup(Object object, FetchGroup fetchGroup) { FetchGroup groupInObject = ((FetchGroupTracker)object)._persistence_getFetchGroup(); return (groupInObject == null) || groupInObject.isSupersetOf(fetchGroup); } /** * INTERNAL: * Return true if the cached object data should be written in clone. * It is used in Fetch Group case when filling in the clone from the cached object. */ public boolean shouldWriteInto(Object cachedObject, Object clone) { FetchGroup fetchGroupInTarg = ((FetchGroupTracker)clone)._persistence_getFetchGroup(); if (fetchGroupInTarg != null) { FetchGroup fetchGroupInSrc = ((FetchGroupTracker)cachedObject)._persistence_getFetchGroup(); //should write if target's fetch group is not a superset of that of the source, //or if refresh is required, should always write (either refresh or revert) data from the cache to the clones. return !fetchGroupInTarg.isSupersetOf(fetchGroupInSrc) || ((FetchGroupTracker) cachedObject)._persistence_shouldRefreshFetchGroup(); } return false; } /** * INTERNAL: * Write data of the partially fetched object into the working and backup clones */ public void writePartialIntoClones(Object partialObject, Object workingClone, Object backupClone, UnitOfWorkImpl uow) { FetchGroup fetchGroupInClone = ((FetchGroupTracker)workingClone)._persistence_getFetchGroup(); FetchGroup fetchGroupInObject = ((FetchGroupTracker)partialObject)._persistence_getFetchGroup(); // Update fetch group in clone as the union of two, // do this first to avoid fetching during method access. EntityFetchGroup union = flatUnionFetchGroups(fetchGroupInObject, fetchGroupInClone, false);// this method is not called for aggregates // Finally, update clone's fetch group reference. setObjectFetchGroup(workingClone, union, uow); if (workingClone != backupClone) { setObjectFetchGroup(backupClone, union, uow); } ObjectChangePolicy policy = descriptor.getObjectChangePolicy(); // Turn it 'off' to prevent unwanted events. policy.dissableEventProcessing(workingClone); try { //if refresh is set, force to fill in fetch group data if (((FetchGroupTracker)partialObject)._persistence_shouldRefreshFetchGroup()) { //refresh and fill in the fetch group data refreshFetchGroupIntoClones(partialObject, workingClone, backupClone, fetchGroupInObject, fetchGroupInClone, uow); } else {//no refresh is enforced //revert the unfetched attributes of the clones. revertDataIntoUnfetchedAttributesOfClones(partialObject, workingClone, backupClone, fetchGroupInObject, fetchGroupInClone, uow); } } finally { policy.enableEventProcessing(workingClone); } } /** * INTERNAL: * Refresh the fetch group data into the working and backup clones. * This is called if refresh is enforced */ // TODO-244124-dclarke: Needs to be updated to reflect new FetchGroup behaviour private void refreshFetchGroupIntoClones(Object cachedObject, Object workingClone, Object backupClone, FetchGroup fetchGroupInObject, FetchGroup fetchGroupInClone, UnitOfWorkImpl uow) { Vector mappings = descriptor.getMappings(); boolean isObjectPartial = (fetchGroupInObject != null); Set fetchedAttributes = isObjectPartial ? fetchGroupInObject.getAttributeNames() : null; int size = mappings.size(); for (int index = 0; index < size; index++) { DatabaseMapping mapping = mappings.get(index); if ((!isObjectPartial) || ((fetchedAttributes != null) && fetchedAttributes.contains(mapping.getAttributeName()))) { // Only refresh the fetched attributes into clones. mapping.buildClone(cachedObject, null, workingClone, null, uow); if (workingClone != backupClone) { mapping.buildClone(workingClone, null, backupClone, null, uow); } } else if (mapping.isAggregateObjectMapping()){ Object attributeValue = mapping.getAttributeValueFromObject(cachedObject); Object cloneAttrbute = mapping.getAttributeValueFromObject(workingClone); Object backupAttribute = mapping.getAttributeValueFromObject(backupClone); if ((cloneAttrbute == null && attributeValue != null) || (cloneAttrbute != null && attributeValue == null)){ mapping.buildClone(cachedObject, null, workingClone, null, uow); }else if (attributeValue != null && mapping.getReferenceDescriptor().getFetchGroupManager().shouldWriteInto(attributeValue, cloneAttrbute)) { //there might be cases when reverting/refreshing clone is needed. mapping.getReferenceDescriptor().getFetchGroupManager().writePartialIntoClones(attributeValue, cloneAttrbute, backupAttribute, uow); } } } } /** * Revert the clones' unfetched attributes, and leave fetched ones intact. */ private void revertDataIntoUnfetchedAttributesOfClones(Object cachedObject, Object workingClone, Object backupClone, FetchGroup fetchGroupInObject, FetchGroup fetchGroupInClone, UnitOfWorkImpl uow) { // Fetched attributes set in working clone. Set fetchedAttributesClone = fetchGroupInClone.getAttributeNames(); // Fetched attributes set in cached object. Set fetchedAttributesCached = null; if (fetchGroupInObject != null) { fetchedAttributesCached = fetchGroupInObject.getAttributeNames(); } for (DatabaseMapping mapping : descriptor.getMappings()) { String attributeName = mapping.getAttributeName(); if ((fetchedAttributesCached == null || fetchedAttributesCached.contains(attributeName)) && !fetchedAttributesClone.contains(attributeName)) { mapping.buildClone(cachedObject, null, workingClone, null, uow); if (workingClone != backupClone) { mapping.buildClone(workingClone, null, backupClone, null, uow); } }else if (mapping.isAggregateObjectMapping()){ if (mapping.getReferenceDescriptor().hasFetchGroupManager()){ Object attributeValue = mapping.getAttributeValueFromObject(cachedObject); Object cloneAttrbute = mapping.getAttributeValueFromObject(workingClone); Object backupAttribute = mapping.getAttributeValueFromObject(backupClone); if ((cloneAttrbute == null && attributeValue != null) || (cloneAttrbute != null && attributeValue == null)){ mapping.buildClone(cachedObject, null, workingClone, null, uow); }else if (attributeValue != null && mapping.getReferenceDescriptor().getFetchGroupManager().shouldWriteInto(attributeValue, cloneAttrbute)) { //there might be cases when reverting/refreshing clone is needed. mapping.getReferenceDescriptor().getFetchGroupManager().writePartialIntoClones(attributeValue, cloneAttrbute, backupAttribute, uow); } } } // Only revert the attribute which is fetched by the cached object, but not fetched by the clone. } } /** * INTERNAL: * Copy fetch group reference from the source object to the target */ public void copyAggregateFetchGroupInto(Object source, Object target, Object rootEntity, AbstractSession session) { if (isPartialObject(source)) { FetchGroup newGroup = ((FetchGroupTracker)source)._persistence_getFetchGroup().clone(); // must clone because original is linked to orig root newGroup.setRootEntity((FetchGroupTracker) rootEntity); setObjectFetchGroup(target, newGroup, session); } } /** * INTERNAL: * Copy fetch group reference from the source object to the target */ public void copyFetchGroupInto(Object source, Object target, AbstractSession session) { if (isPartialObject(source)) { setObjectFetchGroup(target, ((FetchGroupTracker)source)._persistence_getFetchGroup(), session); } } /** * INTERNAL: * Union the fetch group of the domain object with the new fetch group. */ public void unionEntityFetchGroupIntoObject(Object source, EntityFetchGroup newEntityFetchGroup, AbstractSession session, boolean shouldClone) { //this order is important as we need to be merging into the target fetchgroup setObjectFetchGroup(source, flatUnionFetchGroups(newEntityFetchGroup, ((FetchGroupTracker)source)._persistence_getFetchGroup(), shouldClone), session); } /** * INTERNAL: * Union two fetch groups. */ public FetchGroup unionFetchGroups(FetchGroup first, FetchGroup second) { if ((first == null) || (second == null)) { return null; } //return the superset if applied if (first == second || first.isSupersetOf(second)) { return first; } else if (second.isSupersetOf(first)) { return second; } Set unionAttributeNames = new HashSet(); unionAttributeNames.addAll(first.getAttributeNames()); unionAttributeNames.addAll(second.getAttributeNames()); return getEntityFetchGroup(unionAttributeNames); } /** * INTERNAL: * Union two fetch groups as EntityFetchGroups. * Ignores all nested attributes. */ public EntityFetchGroup flatUnionFetchGroups(FetchGroup first, FetchGroup second, boolean shouldClone) { if ((first == null) || (second == null)) { return null; } //return the superset if applied if (first == second) { return getEntityFetchGroup(first); } Set unionAttributeNames = new HashSet(); unionAttributeNames.addAll(first.getAttributeNames()); unionAttributeNames.addAll(second.getAttributeNames()); EntityFetchGroup newGroup = getEntityFetchGroup(unionAttributeNames); if (shouldClone){ newGroup = (EntityFetchGroup) newGroup.clone(); newGroup.setRootEntity(second.getRootEntity()); } return newGroup; } /** * INTERNAL: * Reset object attributes to the default values. */ public void reset(Object source) { ((FetchGroupTracker)source)._persistence_resetFetchGroup(); } /** * INTERNAL: * Return FetchGroup held by the object. */ public FetchGroup getObjectFetchGroup(Object domainObject) { if (domainObject != null) { return ((FetchGroupTracker)domainObject)._persistence_getFetchGroup(); } return null; } /** * INTERNAL: * Return FetchGroup held by the object. */ public EntityFetchGroup getObjectEntityFetchGroup(Object domainObject) { if (domainObject != null) { FetchGroup fetchGroup = ((FetchGroupTracker)domainObject)._persistence_getFetchGroup(); if(fetchGroup != null) { if(fetchGroup.isEntityFetchGroup()) { return (EntityFetchGroup)fetchGroup; } return getEntityFetchGroup(fetchGroup); } } return null; } /** * INTERNAL: * Set fetch group into the object. */ public void setObjectFetchGroup(Object source, FetchGroup fetchGroup, AbstractSession session) { FetchGroupTracker tracker = (FetchGroupTracker)source; if(fetchGroup == null) { tracker._persistence_setFetchGroup(null); tracker._persistence_setSession(null); } else { if(fetchGroup.isEntityFetchGroup()) { // it's EntityFetchGroup - just set it tracker._persistence_setFetchGroup(fetchGroup); tracker._persistence_setSession(session); } else { EntityFetchGroup entityFetchGroup = this.getEntityFetchGroup(fetchGroup); if(entityFetchGroup != null) { tracker._persistence_setFetchGroup(entityFetchGroup); tracker._persistence_setSession(session); } else { tracker._persistence_setFetchGroup(null); tracker._persistence_setSession(null); } } } } /** * INTERNAL: * Set if the tracked object is fetched from executing a query with or without refresh. */ public void setRefreshOnFetchGroupToObject(Object source, boolean shouldRefreshOnFetchgroup) { ((FetchGroupTracker)source)._persistence_setShouldRefreshFetchGroup(shouldRefreshOnFetchgroup); } /** * Return true if the attribute of the object has already been fetched */ public boolean isAttributeFetched(Object entity, String attributeName) { FetchGroup fetchGroup = ((FetchGroupTracker) entity)._persistence_getFetchGroup(); if (fetchGroup == null) { return true; } return fetchGroup.containsAttributeInternal(attributeName); } /** * PUBLIC: * Return the referenced descriptor. */ public ClassDescriptor getDescriptor() { return descriptor; } /** * Set the referenced descriptor. */ public void setDescriptor(ClassDescriptor descriptor) { this.descriptor = descriptor; } /** * Return true if a fetch group exists for the given group name. */ public boolean hasFetchGroup(String groupName) { return getFetchGroups().containsKey(groupName); } /** * INTERNAL: Initialize the fetch groups. XXX-dclarke: added support for * reinit the query manager's queries if they exist */ public void initialize(AbstractSession session) throws DescriptorException { if (this.entityFetchGroups == null) { this.entityFetchGroups = new ConcurrentHashMap(); } if (!(Helper.classImplementsInterface(getDescriptor().getJavaClass(), ClassConstants.FetchGroupTracker_class))) { //to use fetch group, the domain class must implement FetchGroupTracker interface session.getIntegrityChecker().handleError(DescriptorException.needToImplementFetchGroupTracker(getDescriptor().getJavaClass(), getDescriptor())); } this.minimalFetchGroup = new FetchGroup(); this.fullFetchGroup = new FetchGroup(); for (DatabaseMapping mapping : getDescriptor().getMappings()) { String name = mapping.getAttributeName(); if(mapping.isPrimaryKeyMapping()) { this.minimalFetchGroup.addAttribute(name); } this.fullFetchGroup.addAttribute(name); } this.idEntityFetchGroup = null; if (this.descriptor.isChildDescriptor()) { FetchGroupManager parentManager = this.descriptor.getInheritancePolicy().getParentDescriptor().getFetchGroupManager(); if (parentManager != null) { // copy idEntityFetchGroup from the parent this.idEntityFetchGroup = parentManager.getIdEntityFetchGroup(); } } if (this.idEntityFetchGroup != null) { // insert the copied idEntityFetchGroup into the map this.entityFetchGroups.put(this.idEntityFetchGroup.getAttributeNames(), this.idEntityFetchGroup); } else { // currently minimalFetchGroup contains only PrimaryKey - that's what idEntityFetchGroup will consist of. this.idEntityFetchGroup = getEntityFetchGroup(this.minimalFetchGroup); } if(this.descriptor.usesOptimisticLocking()) { DatabaseField lockField = this.descriptor.getOptimisticLockingPolicy().getWriteLockField(); if (lockField != null) { DatabaseMapping lockMapping = getDescriptor().getObjectBuilder().getMappingForField(lockField); if (lockMapping != null) { String attributeName = lockMapping.getAttributeName(); minimalFetchGroup.addAttribute(attributeName); } } } // Now minimalFetchGroup contains PrimaryKey plus locking field - getEntityFetchGroup call ensures // that corresponding EntityFetchGroup is cached in entityFetchGroups map. // Note that the new EntityFetchGroup is not created if there is no locking field. getEntityFetchGroup(this.minimalFetchGroup); } /** * INTERNAL: * postInitialize called for inheritance children first. * That allows to copy defaultFetchGroup from the parent only in case * it has been set by user (not automatically generated). */ public void postInitialize(AbstractSession session) throws DescriptorException { if (!(Helper.classImplementsInterface(getDescriptor().getJavaClass(), ClassConstants.FetchGroupTracker_class))) { // initialize already threw exception here return; } // Create and cache EntityFetchGroups for named fetch groups. if(this.fetchGroups != null) { Iterator it = this.fetchGroups.values().iterator(); while(it.hasNext()) { FetchGroup fetchGroup = it.next(); prepareAndVerify(fetchGroup); getEntityFetchGroup(fetchGroup); } } if(this.defaultFetchGroup == null) { // Look up default fetch group set by user on parent descriptors if(this.descriptor.isChildDescriptor() && this.shouldUseInheritedDefaultFetchGroup) { ClassDescriptor current = this.descriptor; while(current.isChildDescriptor()) { ClassDescriptor parent = current.getInheritancePolicy().getParentDescriptor(); if (parent.hasFetchGroupManager()) { this.defaultFetchGroup = parent.getFetchGroupManager().getDefaultFetchGroup(); if(this.defaultFetchGroup != null) { return; } } current = parent; } } FetchGroup defaultCandidate = new FetchGroup(); boolean hasLazy = false; for (DatabaseMapping mapping : getDescriptor().getMappings()) { if (mapping.isForeignReferenceMapping() || (!mapping.isLazy())) { defaultCandidate.addAttribute(mapping.getAttributeName()); } else { hasLazy = true; } } if(hasLazy) { this.defaultFetchGroup = defaultCandidate; } } if(this.defaultFetchGroup != null) { prepareAndVerify(this.defaultFetchGroup); this.defaultEntityFetchGroup = getEntityFetchGroup(this.defaultFetchGroup); } initNonReferenceEntityFetchGroup(); } protected void initNonReferenceEntityFetchGroup() { FetchGroup nonReferenceFetchGroup = new FetchGroup(); for (DatabaseMapping mapping : getDescriptor().getMappings()) { if(!mapping.isForeignReferenceMapping()) { String name = mapping.getAttributeName(); if(this.defaultEntityFetchGroup == null || this.defaultEntityFetchGroup.containsAttribute(name)) { nonReferenceFetchGroup.addAttribute(name); } } } this.addMinimalFetchGroup(nonReferenceFetchGroup); this.nonReferenceEntityFetchGroup = getEntityFetchGroup(nonReferenceFetchGroup); } /** * INTERNAL: * Clone the fetch group manager. */ @Override public Object clone() { try { return super.clone(); } catch (Exception exception) { throw new InternalError(exception.toString()); } } /** * PUBLIC: * Set whether defaultFetchGroup should be copied from the parent if not set. */ public void setShouldUseInheritedDefaultFetchGroup(boolean shouldUseInheritedDefaultFetchGroup) { this.shouldUseInheritedDefaultFetchGroup = shouldUseInheritedDefaultFetchGroup; } /** * PUBLIC: * Indicates whether defaultFetchGroup should be copied from the parent if not set. */ public boolean shouldUseInheritedDefaultFetchGroup() { return this.shouldUseInheritedDefaultFetchGroup; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy