| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 1998, 2021 IBM Corporation. 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 |
| // stardif - updates for Cascaded locking and inheritance |
| // 02/20/2009-1.1 Guy Pelletier |
| // - 259829: TABLE_PER_CLASS with abstract classes does not work |
| // 10/15/2010-2.2 Guy Pelletier |
| // - 322008: Improve usability of additional criteria applied to queries at the session/EM |
| // 04/01/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 2) |
| // 04/05/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 3) |
| // 04/21/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 5) |
| // 09/09/2011-2.3.1 Guy Pelletier |
| // - 356197: Add new VPD type to MultitenantType |
| // 11/10/2011-2.4 Guy Pelletier |
| // - 357474: Address primaryKey option from tenant discriminator column |
| // 14/05/2012-2.4 Guy Pelletier |
| // - 376603: Provide for table per tenant support for multitenant applications |
| // 30/05/2012-2.4 Guy Pelletier |
| // - 354678: Temp classloader is still being used during metadata processing |
| // 09 Jan 2013-2.5 Gordon Yorke |
| // - 397772: JPA 2.1 Entity Graph Support |
| // 06/25/2014-2.5.2 Rick Curtis |
| // - 438177: Support M2M map with jointable |
| // 08/12/2015-2.6 Mythily Parthasarathy |
| // - 474752: Address NPE for Embeddable with 1-M association |
| // 07/09/2018-2.6 Jody Grassel |
| // - 536853: MapsID processing sets up to fail validation |
| package org.eclipse.persistence.descriptors; |
| |
| import java.io.Serializable; |
| import java.lang.reflect.Method; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.annotations.CacheKeyType; |
| import org.eclipse.persistence.annotations.IdValidation; |
| import org.eclipse.persistence.config.CacheIsolationType; |
| import org.eclipse.persistence.core.descriptors.CoreDescriptor; |
| import org.eclipse.persistence.descriptors.changetracking.AttributeChangeTrackingPolicy; |
| import org.eclipse.persistence.descriptors.changetracking.ChangeTracker; |
| import org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy; |
| import org.eclipse.persistence.descriptors.changetracking.ObjectChangePolicy; |
| import org.eclipse.persistence.descriptors.copying.CloneCopyPolicy; |
| import org.eclipse.persistence.descriptors.copying.CopyPolicy; |
| import org.eclipse.persistence.descriptors.copying.InstantiationCopyPolicy; |
| import org.eclipse.persistence.descriptors.copying.PersistenceEntityCopyPolicy; |
| import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy; |
| import org.eclipse.persistence.descriptors.invalidation.NoExpiryCacheInvalidationPolicy; |
| import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy; |
| import org.eclipse.persistence.exceptions.DatabaseException; |
| import org.eclipse.persistence.exceptions.DescriptorException; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.expressions.Expression; |
| import org.eclipse.persistence.expressions.ExpressionBuilder; |
| import org.eclipse.persistence.history.HistoryPolicy; |
| import org.eclipse.persistence.internal.databaseaccess.DatabaseCall; |
| import org.eclipse.persistence.internal.databaseaccess.DatasourceCall; |
| import org.eclipse.persistence.internal.databaseaccess.Platform; |
| import org.eclipse.persistence.internal.descriptors.CascadeLockingPolicy; |
| import org.eclipse.persistence.internal.descriptors.InstantiationPolicy; |
| import org.eclipse.persistence.internal.descriptors.ObjectBuilder; |
| import org.eclipse.persistence.internal.descriptors.OptimisticLockingPolicy; |
| import org.eclipse.persistence.internal.descriptors.PersistenceObject; |
| import org.eclipse.persistence.internal.descriptors.PersistenceObjectAttributeAccessor; |
| import org.eclipse.persistence.internal.descriptors.PersistenceObjectInstantiationPolicy; |
| import org.eclipse.persistence.internal.descriptors.SerializedObjectPolicyWrapper; |
| import org.eclipse.persistence.internal.descriptors.VirtualAttributeMethodInfo; |
| import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl; |
| import org.eclipse.persistence.internal.expressions.SQLSelectStatement; |
| import org.eclipse.persistence.internal.expressions.SQLStatement; |
| import org.eclipse.persistence.internal.helper.ClassConstants; |
| import org.eclipse.persistence.internal.helper.ConversionManager; |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.helper.DatabaseTable; |
| import org.eclipse.persistence.internal.helper.Helper; |
| import org.eclipse.persistence.internal.helper.MappingCompare; |
| import org.eclipse.persistence.internal.helper.NonSynchronizedVector; |
| import org.eclipse.persistence.internal.identitymaps.IdentityMap; |
| import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.security.PrivilegedMethodInvoker; |
| import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.weaving.PersistenceWeavedChangeTracking; |
| import org.eclipse.persistence.mappings.AggregateCollectionMapping; |
| import org.eclipse.persistence.mappings.AggregateMapping; |
| import org.eclipse.persistence.mappings.AggregateObjectMapping; |
| import org.eclipse.persistence.mappings.Association; |
| import org.eclipse.persistence.mappings.AttributeAccessor; |
| import org.eclipse.persistence.mappings.CollectionMapping; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.DirectCollectionMapping; |
| import org.eclipse.persistence.mappings.DirectToFieldMapping; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| import org.eclipse.persistence.mappings.ManyToManyMapping; |
| import org.eclipse.persistence.mappings.ManyToOneMapping; |
| import org.eclipse.persistence.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.mappings.OneToManyMapping; |
| import org.eclipse.persistence.mappings.OneToOneMapping; |
| import org.eclipse.persistence.mappings.UnidirectionalOneToManyMapping; |
| import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping; |
| import org.eclipse.persistence.mappings.querykeys.DirectQueryKey; |
| import org.eclipse.persistence.mappings.querykeys.QueryKey; |
| import org.eclipse.persistence.queries.AttributeGroup; |
| import org.eclipse.persistence.queries.DatabaseQuery; |
| import org.eclipse.persistence.queries.DeleteObjectQuery; |
| import org.eclipse.persistence.queries.FetchGroup; |
| import org.eclipse.persistence.queries.FetchGroupTracker; |
| import org.eclipse.persistence.queries.InsertObjectQuery; |
| import org.eclipse.persistence.queries.ObjectLevelReadQuery; |
| import org.eclipse.persistence.queries.QueryRedirector; |
| import org.eclipse.persistence.queries.ReadObjectQuery; |
| import org.eclipse.persistence.sequencing.Sequence; |
| import org.eclipse.persistence.sessions.DatabaseRecord; |
| import org.eclipse.persistence.sessions.Project; |
| import org.eclipse.persistence.sessions.interceptors.CacheInterceptor; |
| import org.eclipse.persistence.sessions.remote.DistributedSession; |
| |
| /** |
| * <p><b>Purpose</b>: |
| * Abstract descriptor class for defining persistence information on a class. |
| * This class provides the data independent behavior and is subclassed, |
| * for relational, object-relational, EIS, XML, etc. |
| * |
| * @see RelationalDescriptor |
| * @see org.eclipse.persistence.mappings.structures.ObjectRelationalDataTypeDescriptor |
| * @see org.eclipse.persistence.eis.EISDescriptor |
| * @see org.eclipse.persistence.oxm.XMLDescriptor |
| */ |
| public class ClassDescriptor extends CoreDescriptor<AttributeGroup, DescriptorEventManager, DatabaseField, InheritancePolicy, InstantiationPolicy, Vector, ObjectBuilder> implements Cloneable, Serializable { |
| protected Class javaClass; |
| protected String javaClassName; |
| protected Vector<DatabaseTable> tables; |
| protected transient DatabaseTable defaultTable; |
| protected List<DatabaseField> primaryKeyFields; |
| protected Map<DatabaseTable, Map<DatabaseField, DatabaseField>> additionalTablePrimaryKeyFields; |
| protected transient List<DatabaseTable> multipleTableInsertOrder; |
| protected Map<DatabaseTable, Set<DatabaseTable>> multipleTableForeignKeys; |
| /** Support delete cascading on the database for multiple and inheritance tables. */ |
| protected boolean isCascadeOnDeleteSetOnDatabaseOnSecondaryTables; |
| |
| protected transient Vector<DatabaseField> fields; |
| protected transient Vector<DatabaseField> allFields; |
| protected transient List<DatabaseField> selectionFields; |
| protected transient List<DatabaseField> allSelectionFields; |
| protected transient Vector<DatabaseField> returnFieldsToGenerateInsert; |
| protected transient Vector<DatabaseField> returnFieldsToGenerateUpdate; |
| protected transient List<DatabaseField> returnFieldsToMergeInsert; |
| protected transient List<DatabaseField> returnFieldsToMergeUpdate; |
| |
| protected Vector<DatabaseMapping> mappings; |
| |
| //Used to track which other classes reference this class in cases where |
| // the referencing classes need to be notified of something. |
| protected Set<ClassDescriptor> referencingClasses; |
| |
| //used by the lock on clone process. This will contain the foreign reference |
| //mapping without indirection |
| protected List<DatabaseMapping> lockableMappings; |
| protected Map<String, QueryKey> queryKeys; |
| |
| // Additional properties. |
| protected String sequenceNumberName; |
| protected DatabaseField sequenceNumberField; |
| protected transient String sessionName; |
| protected transient Vector constraintDependencies; |
| protected transient String amendmentMethodName; |
| protected transient Class amendmentClass; |
| protected String amendmentClassName; |
| protected String alias; |
| protected boolean shouldBeReadOnly; |
| protected boolean shouldAlwaysConformResultsInUnitOfWork; |
| |
| // for bug 2612601 allow ability not to register results in UOW. |
| protected boolean shouldRegisterResultsInUnitOfWork = true; |
| |
| // Delegation objects, these perform most of the behavior. |
| protected DescriptorQueryManager queryManager; |
| protected CopyPolicy copyPolicy; |
| protected String copyPolicyClassName; |
| protected InterfacePolicy interfacePolicy; |
| protected OptimisticLockingPolicy optimisticLockingPolicy; |
| protected transient List<CascadeLockingPolicy> cascadeLockingPolicies; |
| protected WrapperPolicy wrapperPolicy; |
| protected ObjectChangePolicy changePolicy; |
| protected ReturningPolicy returningPolicy; |
| protected List<ReturningPolicy> returningPolicies; |
| protected HistoryPolicy historyPolicy; |
| protected String partitioningPolicyName; |
| protected PartitioningPolicy partitioningPolicy; |
| protected CMPPolicy cmpPolicy; |
| protected CachePolicy cachePolicy; |
| protected MultitenantPolicy multitenantPolicy; |
| protected SerializedObjectPolicy serializedObjectPolicy; |
| |
| //manage fetch group behaviors and operations |
| protected FetchGroupManager fetchGroupManager; |
| |
| /** Additional properties may be added. */ |
| protected Map properties; |
| /** Allow the user to defined un-converted properties which will be initialized at runtime. */ |
| protected Map<String, List<String>> unconvertedProperties; |
| |
| protected transient int initializationStage; |
| protected transient int interfaceInitializationStage; |
| /** The following are the [initializationStage] states the descriptor passes through during the initialization. */ |
| protected static final int UNINITIALIZED = 0; |
| protected static final int PREINITIALIZED = 1; |
| protected static final int INITIALIZED = 2; // this state represents a fully initialized descriptor |
| protected static final int POST_INITIALIZED = 3; // however this value is used by the public function isFullyInitialized() |
| protected static final int ERROR = -1; |
| |
| protected int descriptorType; |
| /** Define valid descriptor types. */ |
| protected static final int NORMAL = 0; |
| protected static final int INTERFACE = 1; |
| protected static final int AGGREGATE = 2; |
| protected static final int AGGREGATE_COLLECTION = 3; |
| |
| protected boolean shouldOrderMappings; |
| protected CacheInvalidationPolicy cacheInvalidationPolicy = null; |
| |
| /** PERF: Used to optimize cache locking to only acquire deferred locks when required (no-indirection). */ |
| protected boolean shouldAcquireCascadedLocks = false; |
| /** INTERNAL: flag to indicate the initialization state of cascade locking for this descriptor */ |
| protected boolean cascadedLockingInitialized = false; |
| |
| /** PERF: Compute and store if the primary key is simple (direct-mapped) to allow fast extraction. */ |
| protected boolean hasSimplePrimaryKey = false; |
| |
| /** |
| * Defines if any mapping reference a field in a secondary table. |
| * This is used to disable deferring multiple table writes. |
| */ |
| protected boolean hasMultipleTableConstraintDependecy = false; |
| |
| public static final int UNDEFINED_OBJECT_CHANGE_BEHAVIOR = CachePolicy.UNDEFINED_OBJECT_CHANGE_BEHAVIOR; |
| public static final int SEND_OBJECT_CHANGES = CachePolicy.SEND_OBJECT_CHANGES; |
| public static final int INVALIDATE_CHANGED_OBJECTS = CachePolicy.INVALIDATE_CHANGED_OBJECTS; |
| public static final int SEND_NEW_OBJECTS_WITH_CHANGES = CachePolicy.SEND_NEW_OBJECTS_WITH_CHANGES; |
| public static final int DO_NOT_SEND_CHANGES = CachePolicy.DO_NOT_SEND_CHANGES; |
| |
| public static final int UNDEFINED_ISOLATATION = CachePolicy.UNDEFINED_ISOLATATION; |
| public static final int USE_SESSION_CACHE_AFTER_TRANSACTION = CachePolicy.USE_SESSION_CACHE_AFTER_TRANSACTION; |
| public static final int ISOLATE_NEW_DATA_AFTER_TRANSACTION = CachePolicy.ISOLATE_NEW_DATA_AFTER_TRANSACTION; // this is the default behaviour even when undefined. |
| public static final int ISOLATE_CACHE_AFTER_TRANSACTION = CachePolicy.ISOLATE_CACHE_AFTER_TRANSACTION; |
| public static final int ISOLATE_FROM_CLIENT_SESSION = CachePolicy.ISOLATE_FROM_CLIENT_SESSION; // Entity Instances only exist in UOW and shared cache. |
| public static final int ISOLATE_CACHE_ALWAYS = CachePolicy.ISOLATE_CACHE_ALWAYS; |
| |
| /** INTERNAL: Backdoor for using changes sets for new objects. */ |
| public static boolean shouldUseFullChangeSetsForNewObjects = false; |
| |
| /** Allow connection unwrapping to be configured. */ |
| protected boolean isNativeConnectionRequired; |
| |
| /** Allow zero primary key validation to be configured. */ |
| protected IdValidation idValidation; |
| |
| /** Allow zero primary key validation to be configured per field. */ |
| protected List<IdValidation> primaryKeyIdValidations; |
| |
| // JPA 2.0 Derived identities - map of mappings that act as derived ids |
| protected Map<String, DatabaseMapping> derivesIdMappings; |
| |
| //Added for default Redirectors |
| protected QueryRedirector defaultQueryRedirector; |
| protected QueryRedirector defaultReadAllQueryRedirector; |
| protected QueryRedirector defaultReadObjectQueryRedirector; |
| protected QueryRedirector defaultReportQueryRedirector; |
| protected QueryRedirector defaultUpdateObjectQueryRedirector; |
| protected QueryRedirector defaultInsertObjectQueryRedirector; |
| protected QueryRedirector defaultDeleteObjectQueryRedirector; |
| |
| //Added for default Redirectors |
| protected String defaultQueryRedirectorClassName; |
| protected String defaultReadAllQueryRedirectorClassName; |
| protected String defaultReadObjectQueryRedirectorClassName; |
| protected String defaultReportQueryRedirectorClassName; |
| protected String defaultUpdateObjectQueryRedirectorClassName; |
| protected String defaultInsertObjectQueryRedirectorClassName; |
| protected String defaultDeleteObjectQueryRedirectorClassName; |
| |
| /** Store the Sequence used for the descriptor. */ |
| protected Sequence sequence; |
| |
| /** Mappings that require postCalculateChanges method to be called */ |
| protected List<DatabaseMapping> mappingsPostCalculateChanges; |
| /** Mappings that require postCalculateChangesOnDeleted method to be called */ |
| protected List<DatabaseMapping> mappingsPostCalculateChangesOnDeleted; |
| |
| /** used by aggregate descriptors to hold additional fields needed when they are stored in an AggregatateCollection |
| * These fields are generally foreign key fields that are required in addition to the fields in the descriptor's |
| * mappings to uniquely identify the Aggregate*/ |
| protected transient List<DatabaseField> additionalAggregateCollectionKeyFields; |
| |
| /** stores a list of mappings that require preDelete as a group prior to the delete individually */ |
| protected List<DatabaseMapping> preDeleteMappings; |
| |
| /** stores fields that are written by Map key mappings so they can be checked for multiple writable mappings */ |
| protected transient List<DatabaseField> additionalWritableMapKeyFields; |
| |
| /** whether this descriptor has any relationships through its mappings, through inheritance, or through aggregates */ |
| protected boolean hasRelationships = false; |
| |
| /** Stores a set of FK fields that will be cached to later retrieve noncacheable mappings */ |
| protected Set<DatabaseField> foreignKeyValuesForCaching; |
| |
| /** caches if this descriptor has any non cacheable mappings */ |
| protected boolean hasNoncacheableMappings = false; |
| |
| /** This flag stores whether this descriptor is using Property access based on JPA semantics. It is used to modify |
| * the behavior of our weaving functionality as it pertains to adding annotations to fields |
| */ |
| protected boolean weavingUsesPropertyAccess = false; |
| |
| /** A list of methods that are used by virtual mappings. This list is used to control weaving of methods |
| * used for virtual access*/ |
| protected List<VirtualAttributeMethodInfo> virtualAttributeMethods = null; |
| |
| /** |
| * A list of AttributeAccessors in order of access from root to leaf to arrive at current AggregateDescriptor. |
| * Only application for Aggregate Descriptors. |
| */ |
| protected List<AttributeAccessor> accessorTree; |
| |
| /** |
| * JPA DescriptorCustomizer list stored here to preserve it when caching the project |
| */ |
| protected String descriptorCustomizerClassName; |
| |
| /** |
| * This flag controls if a UOW should acquire locks for clone or simple clone the instance passed to registerExistingObject. If the IdentityMap type does not |
| * have concurrent access this can save a return to the identity map for cloning. |
| */ |
| protected boolean shouldLockForClone = true; |
| |
| /** |
| * PUBLIC: |
| * Return a new descriptor. |
| */ |
| public ClassDescriptor() { |
| // Properties |
| this.tables = NonSynchronizedVector.newInstance(3); |
| this.mappings = NonSynchronizedVector.newInstance(); |
| this.primaryKeyFields = new ArrayList(2); |
| this.fields = NonSynchronizedVector.newInstance(); |
| this.allFields = NonSynchronizedVector.newInstance(); |
| this.constraintDependencies = NonSynchronizedVector.newInstance(2); |
| this.multipleTableForeignKeys = new HashMap(5); |
| this.queryKeys = new HashMap(5); |
| this.initializationStage = UNINITIALIZED; |
| this.interfaceInitializationStage = UNINITIALIZED; |
| this.descriptorType = NORMAL; |
| this.shouldOrderMappings = true; |
| this.shouldBeReadOnly = false; |
| this.shouldAlwaysConformResultsInUnitOfWork = false; |
| this.shouldAcquireCascadedLocks = false; |
| this.hasSimplePrimaryKey = false; |
| this.derivesIdMappings = new HashMap(5); |
| |
| this.referencingClasses = new HashSet<>(); |
| |
| // Policies |
| this.objectBuilder = new ObjectBuilder(this); |
| this.cachePolicy = new CachePolicy(); |
| |
| this.additionalWritableMapKeyFields = new ArrayList(2); |
| this.foreignKeyValuesForCaching = new HashSet<>(); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method should only be used for interface descriptors. It |
| * adds an abstract query key to the interface descriptor. Any |
| * implementors of that interface must define the query key |
| * defined by this abstract query key. |
| */ |
| public void addAbstractQueryKey(String queryKeyName) { |
| QueryKey queryKey = new QueryKey(); |
| queryKey.setName(queryKeyName); |
| addQueryKey(queryKey); |
| } |
| |
| /** |
| * INTERNAL: |
| * Add the cascade locking policy to all children that have a relationship to this descriptor |
| * either by inheritance or by encapsulation/aggregation. |
| * @param policy - the CascadeLockingPolicy |
| */ |
| public void addCascadeLockingPolicy(CascadeLockingPolicy policy) { |
| getCascadeLockingPolicies().add(policy); |
| // 232608: propagate later version changes up to the locking policy on a parent branch by setting the policy on all children here |
| if (hasInheritance()) { |
| // InOrder traverse the entire [deep] tree, not just the next level |
| for (ClassDescriptor parent : getInheritancePolicy().getAllChildDescriptors()) { |
| // Set the same cascade locking policy on all descriptors that inherit from this descriptor. |
| parent.addCascadeLockingPolicy(policy); |
| } |
| } |
| |
| // do not propagate an extra locking policy to other mappings, if this descriptor already |
| // has a cascaded optimistic locking policy that will be cascaded |
| if (!this.cascadedLockingInitialized) { |
| // never cascade locking until descriptor is initialized |
| if (isInitialized(INITIALIZED)) { |
| // Set cascade locking policies on privately owned children mappings. |
| for (DatabaseMapping mapping : getMappings()) { |
| prepareCascadeLockingPolicy(mapping); |
| } |
| this.cascadedLockingInitialized = true; |
| } |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * EclipseLink automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings. |
| * In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint, |
| * this defines that this descriptor has a foreign key constraint to another class and must be inserted after |
| * instances of the other class. |
| */ |
| public void addConstraintDependencies(Class dependencies) { |
| addConstraintDependency(dependencies); |
| } |
| |
| /** |
| * ADVANCED: |
| * EclipseLink automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings. |
| * In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint, |
| * this defines that this descriptor has a foreign key constraint to another class and must be inserted after |
| * instances of the other class. |
| */ |
| public void addConstraintDependency(Class dependencies) { |
| getConstraintDependencies().add(dependencies); |
| } |
| |
| /** |
| * Return a new direct/basic mapping for this type of descriptor. |
| */ |
| public AbstractDirectMapping newDirectMapping() { |
| return new DirectToFieldMapping(); |
| } |
| |
| /** |
| * Return a new aggregate/embedded mapping for this type of descriptor. |
| */ |
| public AggregateMapping newAggregateMapping() { |
| return new AggregateObjectMapping(); |
| } |
| |
| /** |
| * Return a new aggregate collection/element collection mapping for this type of descriptor. |
| */ |
| public DatabaseMapping newAggregateCollectionMapping() { |
| return new AggregateCollectionMapping(); |
| } |
| |
| /** |
| * Return a new direct collection/element collection mapping for this type of descriptor. |
| */ |
| public DatabaseMapping newDirectCollectionMapping() { |
| return new DirectCollectionMapping(); |
| } |
| |
| /** |
| * Return a new one to one mapping for this type of descriptor. |
| */ |
| public ObjectReferenceMapping newOneToOneMapping() { |
| OneToOneMapping mapping = new OneToOneMapping(); |
| mapping.setIsOneToOneRelationship(true); |
| return mapping; |
| } |
| |
| /** |
| * Return a new many to one mapping for this type of descriptor. |
| */ |
| public ObjectReferenceMapping newManyToOneMapping() { |
| return new ManyToOneMapping(); |
| } |
| |
| /** |
| * Return a new one to many mapping for this type of descriptor. |
| */ |
| public CollectionMapping newOneToManyMapping() { |
| return new OneToManyMapping(); |
| } |
| |
| /** |
| * Return a new one to many mapping for this type of descriptor. |
| */ |
| public CollectionMapping newUnidirectionalOneToManyMapping() { |
| return new UnidirectionalOneToManyMapping(); |
| } |
| |
| /** |
| * Return a new one to many mapping for this type of descriptor. |
| */ |
| public CollectionMapping newManyToManyMapping() { |
| return new ManyToManyMapping(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a direct to field mapping to the receiver. The new mapping specifies that |
| * an instance variable of the class of objects which the receiver describes maps in |
| * the default manner for its type to the indicated database field. |
| * |
| * @param attributeName is the name of an instance variable of the |
| * class which the receiver describes. |
| * @param fieldName is the name of the database column which corresponds |
| * with the designated instance variable. |
| * @return The newly created DatabaseMapping is returned. |
| */ |
| public DatabaseMapping addDirectMapping(String attributeName, String fieldName) { |
| AbstractDirectMapping mapping = newDirectMapping(); |
| |
| mapping.setAttributeName(attributeName); |
| mapping.setField(new DatabaseField(fieldName)); |
| |
| return addMapping(mapping); |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a direct to field mapping to the receiver. The new mapping specifies that |
| * a variable accessed by the get and set methods of the class of objects which |
| * the receiver describes maps in the default manner for its type to the indicated |
| * database field. |
| */ |
| public DatabaseMapping addDirectMapping(String attributeName, String getMethodName, String setMethodName, String fieldName) { |
| AbstractDirectMapping mapping = (AbstractDirectMapping)addDirectMapping(attributeName, fieldName); |
| |
| mapping.setSetMethodName(setMethodName); |
| mapping.setGetMethodName(getMethodName); |
| |
| return mapping; |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a query key to the descriptor. Query keys define Java aliases to database fields. |
| */ |
| public void addDirectQueryKey(String queryKeyName, String fieldName) { |
| DirectQueryKey queryKey = new DirectQueryKey(); |
| DatabaseField field = new DatabaseField(fieldName); |
| |
| queryKey.setName(queryKeyName); |
| queryKey.setField(field); |
| getQueryKeys().put(queryKeyName, queryKey); |
| } |
| |
| /** |
| * PUBLIC: |
| * This protocol can be used to associate multiple tables with foreign key |
| * information. Use this method to associate secondary tables to a |
| * primary table. Specify the source foreign key field to the target |
| * primary key field. The join criteria will be generated based on the |
| * fields provided. Unless the customary insert order is specified by the user |
| * (using setMultipleTableInsertOrder method) |
| * the (automatically generated) table insert order will ensure that |
| * insert into target table happens before insert into the source table |
| * (there may be a foreign key constraint in the database that requires |
| * target table to be inserted before the source table). |
| */ |
| public void addForeignKeyFieldNameForMultipleTable(String sourceForeignKeyFieldName, String targetPrimaryKeyFieldName) throws DescriptorException { |
| addForeignKeyFieldForMultipleTable(new DatabaseField(sourceForeignKeyFieldName), new DatabaseField(targetPrimaryKeyFieldName)); |
| } |
| |
| /** |
| * PUBLIC: |
| * This protocol can be used to associate multiple tables with foreign key |
| * information. Use this method to associate secondary tables to a |
| * primary table. Specify the source foreign key field to the target |
| * primary key field. The join criteria will be generated based on the |
| * fields provided. |
| */ |
| public void addForeignKeyFieldForMultipleTable(DatabaseField sourceForeignKeyField, DatabaseField targetPrimaryKeyField) throws DescriptorException { |
| // Make sure that the table is fully qualified. |
| if ((!sourceForeignKeyField.hasTableName()) || (!targetPrimaryKeyField.hasTableName())) { |
| throw DescriptorException.multipleTablePrimaryKeyMustBeFullyQualified(this); |
| } |
| |
| setAdditionalTablePrimaryKeyFields(sourceForeignKeyField.getTable(), targetPrimaryKeyField, sourceForeignKeyField); |
| Set<DatabaseTable> sourceTables = getMultipleTableForeignKeys().get(targetPrimaryKeyField.getTable()); |
| if(sourceTables == null) { |
| sourceTables = new HashSet<>(3); |
| getMultipleTableForeignKeys().put(targetPrimaryKeyField.getTable(), sourceTables); |
| } |
| sourceTables.add(sourceForeignKeyField.getTable()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a database mapping to the receiver. Perform any required |
| * initialization of both the mapping and the receiving descriptor |
| * as a result of adding the new mapping. |
| */ |
| public DatabaseMapping addMapping(DatabaseMapping mapping) { |
| // For CR#2646, if the mapping already points to the parent descriptor then leave it. |
| if (mapping.getDescriptor() == null) { |
| mapping.setDescriptor(this); |
| } |
| getMappings().add(mapping); |
| return mapping; |
| } |
| |
| protected void validateMappingType(DatabaseMapping mapping) { |
| if (!(mapping.isRelationalMapping())) { |
| throw DescriptorException.invalidMappingType(mapping); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the primary key field of the descriptors table. |
| * This should be called for each field that makes up the primary key of the table. |
| * If the descriptor has many tables, this must be the primary key in the first table, |
| * if the other tables have the same primary key nothing else is required, otherwise |
| * a primary key/foreign key field mapping must be provided for each of the other tables. |
| * @see #addForeignKeyFieldNameForMultipleTable(String, String) |
| */ |
| public void addPrimaryKeyFieldName(String fieldName) { |
| addPrimaryKeyField(new DatabaseField(fieldName)); |
| } |
| |
| /** |
| * ADVANCED: |
| * Specify the primary key field of the descriptors table. |
| * This should be called for each field that makes up the primary key of the table. |
| * This can be used for advanced field types, such as XML nodes, or to set the field type. |
| */ |
| public void addPrimaryKeyField(DatabaseField field) { |
| // Check if the pkFields List already contains a DatabaseField that is equal to the |
| // field we want to add, in order to avoid duplicates which will fail validation later. |
| List<DatabaseField> pkFields = getPrimaryKeyFields(); |
| if (!pkFields.contains(field)) { |
| pkFields.add(field); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a query key to the descriptor. Query keys define Java aliases to database fields. |
| */ |
| public void addQueryKey(QueryKey queryKey) { |
| getQueryKeys().put(queryKey.getName(), queryKey); |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the table for the class of objects the receiver describes. |
| * This method is used if there is more than one table. |
| */ |
| public void addTable(DatabaseTable table) { |
| getTables().add(table); |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the table name for the class of objects the receiver describes. |
| * If the table has a qualifier it should be specified using the dot notation, |
| * (i.e. "userid.employee"). This method is used if there is more than one table. |
| */ |
| public void addTableName(String tableName) { |
| addTable(new DatabaseTable(tableName)); |
| } |
| |
| /** |
| * PUBLIC: |
| * Add an unconverted property (to be initialiazed at runtime) |
| */ |
| public void addUnconvertedProperty(String propertyName, String propertyValue, String propertyType) { |
| List<String> valuePair = new ArrayList<>(2); |
| valuePair.add(propertyValue); |
| valuePair.add(propertyType); |
| getUnconvertedProperties().put(propertyName, valuePair); |
| } |
| |
| /** |
| * INTERNAL: |
| * Adjust the order of the tables in the multipleTableInsertOrder Vector according to the FK |
| * relationship if one (or more) were previously specified. I.e. target of FK relationship should be inserted |
| * before source. |
| * If the multipleTableInsertOrder has been specified (presumably by the user) then do not change it. |
| */ |
| public void adjustMultipleTableInsertOrder() { |
| // Check if a user defined insert order was given. |
| if ((getMultipleTableInsertOrder() == null) || getMultipleTableInsertOrder().isEmpty()) { |
| createMultipleTableInsertOrder(); |
| } else { |
| verifyMultipleTableInsertOrder(); |
| } |
| toggleAdditionalTablePrimaryKeyFields(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Used to set the descriptor to always conform in any unit of work query. |
| * |
| */ |
| public void alwaysConformResultsInUnitOfWork() { |
| setShouldAlwaysConformResultsInUnitOfWork(true); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldAlwaysRefreshCache} with an argument of <CODE>true</CODE>: |
| * it configures a <CODE>ClassDescriptor</CODE> to always refresh the cache if data is received from the database by any query.<P> |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. For example, by |
| * default, when a query for a single object based on its primary key is executed, OracleAS TopLink will first look in the |
| * cache for the object. If the object is in the cache, the cached object is returned and data is not refreshed. To avoid |
| * cache hits, use the {@link #disableCacheHits} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.<P> |
| * |
| * Use this property with caution because it can lead to poor performance and may refresh on queries when it is not desired. Normally, |
| * if you require fresh data, it is better to configure a query with {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}. |
| * To ensure that refreshes are only done when required, use this method in conjunction with {@link #onlyRefreshCacheIfNewerVersion}. |
| * |
| * @see #dontAlwaysRefreshCache |
| */ |
| public void alwaysRefreshCache() { |
| setShouldAlwaysRefreshCache(true); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldAlwaysRefreshCacheOnRemote} with an argument of <CODE>true</CODE>: |
| * it configures a <CODE>ClassDescriptor</CODE> to always remotely refresh the cache if data is received from the database by any |
| * query in a {@link org.eclipse.persistence.sessions.remote.RemoteSession}.<P> |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. For example, by |
| * default, when a query for a single object based on its primary key is executed, OracleAS TopLink will first look in the |
| * cache for the object. If the object is in the cache, the cached object is returned and data is not refreshed. To avoid |
| * cache hits, use the {@link #disableCacheHitsOnRemote} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.<P> |
| * |
| * Use this property with caution because it can lead to poor performance and may refresh on queries when it is not desired. |
| * Normally, if you require fresh data, it is better to configure a query with {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}. |
| * To ensure that refreshes are only done when required, use this method in conjunction with {@link #onlyRefreshCacheIfNewerVersion}. |
| * |
| * @see #dontAlwaysRefreshCacheOnRemote |
| */ |
| public void alwaysRefreshCacheOnRemote() { |
| setShouldAlwaysRefreshCacheOnRemote(true); |
| } |
| |
| /** |
| * ADVANCED: |
| * Call the descriptor amendment method. |
| * This is called while loading or creating a descriptor that has an amendment method defined. |
| */ |
| public void applyAmendmentMethod() { |
| applyAmendmentMethod(null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Call the descriptor amendment method. |
| * This is called while loading or creating a descriptor that has an amendment method defined. |
| */ |
| public void applyAmendmentMethod(DescriptorEvent event) { |
| if ((getAmendmentClass() == null) || (getAmendmentMethodName() == null)) { |
| return; |
| } |
| |
| Method method = null; |
| Class[] argTypes = new Class[1]; |
| |
| // BUG#2669585 |
| // Class argument type must be consistent, descriptor, i.e. instance may be a subclass. |
| argTypes[0] = ClassDescriptor.class; |
| try { |
| method = Helper.getDeclaredMethod(getAmendmentClass(), getAmendmentMethodName(), argTypes); |
| } catch (Exception ignore) { |
| // Return type should now be ClassDescriptor. |
| argTypes[0] = ClassDescriptor.class; |
| try { |
| method = Helper.getDeclaredMethod(getAmendmentClass(), getAmendmentMethodName(), argTypes); |
| } catch (Exception exception) { |
| throw DescriptorException.invalidAmendmentMethod(getAmendmentClass(), getAmendmentMethodName(), exception, this); |
| } |
| } |
| |
| Object[] args = new Object[1]; |
| args[0] = this; |
| |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| AccessController.doPrivileged(new PrivilegedMethodInvoker(method, null, args)); |
| } else { |
| PrivilegedAccessHelper.invokeMethod(method, null, args); |
| } |
| } catch (Exception exception) { |
| throw DescriptorException.errorOccuredInAmendmentMethod(getAmendmentClass(), getAmendmentMethodName(), exception, this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to determine if a foreign key references the primary key. |
| */ |
| public boolean arePrimaryKeyFields(Vector fields) { |
| if (!(fields.size() == (getPrimaryKeyFields().size()))) { |
| return false; |
| } |
| |
| for (Enumeration enumFields = fields.elements(); enumFields.hasMoreElements();) { |
| DatabaseField field = (DatabaseField)enumFields.nextElement(); |
| |
| if (!getPrimaryKeyFields().contains(field)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Some attributes have default values defined in Project. |
| * If such the value for the attribute hasn't been set then the default value is assigned. |
| */ |
| protected void assignDefaultValues(AbstractSession session) { |
| if (this.idValidation == null) { |
| this.idValidation = session.getProject().getDefaultIdValidation(); |
| } |
| getCachePolicy().assignDefaultValues(session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the selection criteria used to IN batch fetching. |
| */ |
| public Expression buildBatchCriteriaByPK(ExpressionBuilder builder, ObjectLevelReadQuery query) { |
| int size = getPrimaryKeyFields().size(); |
| if (size > 1) { |
| // Support composite keys using nested IN. |
| List<Expression> fields = new ArrayList<>(size); |
| for (DatabaseField targetForeignKeyField : primaryKeyFields) { |
| fields.add(builder.getField(targetForeignKeyField)); |
| } |
| return query.getSession().getPlatform().buildBatchCriteriaForComplexId(builder, fields); |
| } else { |
| return query.getSession().getPlatform().buildBatchCriteria(builder, builder.getField(primaryKeyFields.get(0))); |
| } |
| |
| } |
| /** |
| * INTERNAL: |
| * Return a call built from a statement. Subclasses may throw an exception |
| * if the statement is not appropriate. |
| */ |
| public DatasourceCall buildCallFromStatement(SQLStatement statement, DatabaseQuery query, AbstractSession session) { |
| DatabaseCall call = statement.buildCall(session); |
| if (isNativeConnectionRequired()) { |
| call.setIsNativeConnectionRequired(true); |
| } |
| return call; |
| } |
| |
| /** |
| * INTERNAL: |
| * Extract the direct values from the specified field value. |
| * Return them in a vector. |
| */ |
| public Vector buildDirectValuesFromFieldValue(Object fieldValue) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * A DatabaseField is built from the given field name. |
| */ |
| |
| // * added 9/7/00 by Les Davis |
| // * bug fix for null pointer in initialization of mappings in remote session |
| public DatabaseField buildField(String fieldName) { |
| DatabaseField field = new DatabaseField(fieldName); |
| DatabaseTable table; |
| |
| if (field.hasTableName()) { |
| table = getTable(field.getTableName()); |
| } else if (getDefaultTable() != null) { |
| table = getDefaultTable(); |
| } else { |
| table = getTable(getTableName()); |
| } |
| |
| field.setTable(table); |
| return field; |
| } |
| |
| /** |
| * INTERNAL: |
| * The table of the field is ensured to be unique from the descriptor's tables. |
| * If the field has no table the default table is assigned. |
| * This is used only in initialization. |
| * Fields are ensured to be unique so if the field has already been built it is returned. |
| */ |
| public DatabaseField buildField(DatabaseField field) { |
| return buildField(field, null); |
| } |
| |
| public DatabaseField buildField(DatabaseField field, DatabaseTable relationTable) { |
| DatabaseField builtField = getObjectBuilder().getFieldsMap().get(field); |
| if (builtField == null) { |
| builtField = field; |
| DatabaseTable table; |
| if (relationTable != null && field.hasTableName() && field.getTableName().equals(relationTable.getName())){ |
| table = relationTable; |
| } else if (relationTable != null && !field.hasTableName()) { |
| table = relationTable; |
| } else if (field.hasTableName()) { |
| table = getTable(field.getTableName()); |
| } else { |
| table = getDefaultTable(); |
| } |
| builtField.setTable(table); |
| getObjectBuilder().getFieldsMap().put(builtField, builtField); |
| } |
| return builtField; |
| } |
| |
| /** |
| * INTERNAL: |
| * Build the appropriate field value for the specified |
| * set of direct values. |
| */ |
| public Object buildFieldValueFromDirectValues(Vector directValues, String elementDataTypeName, AbstractSession session) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Build and return the appropriate field value for the specified |
| * set of foreign keys (i.e. each row has the fields that |
| * make up a foreign key). |
| */ |
| public Object buildFieldValueFromForeignKeys(Vector foreignKeys, String referenceDataTypeName, AbstractSession session) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Build and return the field value from the specified nested database row. |
| */ |
| public Object buildFieldValueFromNestedRow(AbstractRecord nestedRow, AbstractSession session) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Build and return the appropriate field value for the specified |
| * set of nested rows. |
| */ |
| public Object buildFieldValueFromNestedRows(Vector nestedRows, String structureName, AbstractSession session) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Build and return the nested database row from the specified field value. |
| */ |
| public AbstractRecord buildNestedRowFromFieldValue(Object fieldValue) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Build and return the nested rows from the specified field value. |
| */ |
| public Vector buildNestedRowsFromFieldValue(Object fieldValue, AbstractSession session) throws DatabaseException { |
| throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this); |
| } |
| |
| /** |
| * To check that tables and fields are present in database |
| */ |
| protected void checkDatabase(AbstractSession session) { |
| if (session.getIntegrityChecker().shouldCheckDatabase()) { |
| for (Iterator<DatabaseTable> iterator = getTables().iterator(); iterator.hasNext();) { |
| DatabaseTable table = iterator.next(); |
| if (session.getIntegrityChecker().checkTable(table, session)) { |
| // To load the fields of database into a vector |
| List databaseFields = new ArrayList(); |
| List result = session.getAccessor().getColumnInfo(null, null, table.getName(), null, session); |
| // Table name may need to be lowercase. |
| if (result.isEmpty() && session.getPlatform().shouldForceFieldNamesToUpperCase()) { |
| result = session.getAccessor().getColumnInfo(null, null, table.getName().toLowerCase(), null, session); |
| } |
| for (Iterator resultIterator = result.iterator(); resultIterator.hasNext();) { |
| AbstractRecord row = (AbstractRecord)resultIterator.next(); |
| if (session.getPlatform().shouldForceFieldNamesToUpperCase()) { |
| databaseFields.add(((String)row.get("COLUMN_NAME")).toUpperCase()); |
| } else { |
| databaseFields.add(row.get("COLUMN_NAME")); |
| } |
| } |
| |
| // To check that the fields of descriptor are present in the database. |
| for (DatabaseField field : getFields()) { |
| if (field.getTable().equals(table) && (!databaseFields.contains(field.getName()))) { |
| session.getIntegrityChecker().handleError(DescriptorException.fieldIsNotPresentInDatabase(this, table.getName(), field.getName())); |
| } |
| } |
| } else { |
| session.getIntegrityChecker().handleError(DescriptorException.tableIsNotPresentInDatabase(this)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that an aggregate descriptor's inheritance tree |
| * is full of aggregate descriptors. |
| */ |
| public void checkInheritanceTreeAggregateSettings(AbstractSession session, AggregateMapping mapping) throws DescriptorException { |
| if (!this.hasInheritance()) { |
| return; |
| } |
| |
| if (this.isChildDescriptor()) { |
| Class parentClass = this.getInheritancePolicy().getParentClass(); |
| if (parentClass == this.getJavaClass()) { |
| throw DescriptorException.parentClassIsSelf(this); |
| } |
| |
| // recurse up the inheritance tree to the root descriptor |
| session.getDescriptor(parentClass).checkInheritanceTreeAggregateSettings(session, mapping); |
| } else { |
| // we have a root descriptor, now verify it and all its children, grandchildren, etc. |
| this.checkInheritanceTreeAggregateSettingsForChildren(session, mapping); |
| } |
| } |
| |
| /** |
| * Verify that an aggregate descriptor's inheritance tree |
| * is full of aggregate descriptors, cont. |
| */ |
| private void checkInheritanceTreeAggregateSettingsForChildren(AbstractSession session, AggregateMapping mapping) throws DescriptorException { |
| if (!this.isAggregateDescriptor()) { |
| session.getIntegrityChecker().handleError(DescriptorException.referenceDescriptorIsNotAggregate(this.getJavaClass().getName(), mapping)); |
| } |
| for (ClassDescriptor childDescriptor : this.getInheritancePolicy().getChildDescriptors()) { |
| // recurse down the inheritance tree to its leaves |
| childDescriptor.checkInheritanceTreeAggregateSettingsForChildren(session, mapping); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Create multiple table insert order. |
| * If its a child descriptor then insert order starts |
| * with the same insert order as in the parent. |
| * Non-inherited tables ordered to adhere to |
| * multipleTableForeignKeys: |
| * the target table (the key in multipleTableForeignKeys map) |
| * should stand in insert order before any of the source tables |
| * (members of the corresponding value in multipleTableForeignKeys). |
| */ |
| protected void createMultipleTableInsertOrder() { |
| int nParentTables = 0; |
| if (isChildDescriptor()) { |
| nParentTables = getInheritancePolicy().getParentDescriptor().getTables().size(); |
| setMultipleTableInsertOrder(new ArrayList(getInheritancePolicy().getParentDescriptor().getMultipleTableInsertOrder())); |
| |
| if(nParentTables == getTables().size()) { |
| // all the tables mapped by the parent - nothing to do. |
| return; |
| } |
| } |
| |
| if(getMultipleTableForeignKeys().isEmpty()) { |
| if(nParentTables == 0) { |
| // no multipleTableForeignKeys specified - keep getTables() order. |
| setMultipleTableInsertOrder((Vector)getTables().clone()); |
| } else { |
| // insert order for parent-defined tables has been already copied from parent descriptor, |
| // add the remaining tables keeping the same order as in getTables() |
| for(int k = nParentTables; k < getTables().size(); k++) { |
| getMultipleTableInsertOrder().add(getTables().get(k)); |
| } |
| } |
| return; |
| } |
| |
| verifyMultipleTablesForeignKeysTables(); |
| |
| // tableComparison[i][j] indicates the order between i and j tables: |
| // -1 i table before j table; |
| // +1 i table after j table; |
| // 0 - not defined (could be either before or after) |
| int[][] tableComparison = createTableComparison(getTables(), nParentTables); |
| |
| // Now create insert order of the tables: |
| // getTables.get(i) table should be |
| // before getTable.get(j) in insert order if tableComparison[i][j]==-1; |
| // after getTable.get(j) in insert order if tableComparison[i][j]== 1; |
| // doesn't matter if tableComparison[i][j]== 0. |
| createMultipleTableInsertOrderFromComparison(tableComparison, nParentTables); |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify multiple table insert order provided by the user. |
| * If its a child descriptor then insert order starts |
| * with the same insert order as in the parent. |
| * Non-inherited tables ordered to adhere to |
| * multipleTableForeignKeys: |
| * the target table (the key in multipleTableForeignKeys map) |
| * should stand in insert order before any of the source tables |
| * (members of the corresponding value in multipleTableForeignKeys). |
| */ |
| protected void verifyMultipleTableInsertOrder() { |
| int nParentTables = 0; |
| if (isChildDescriptor()) { |
| nParentTables = getInheritancePolicy().getParentDescriptor().getTables().size(); |
| |
| if(nParentTables + getMultipleTableInsertOrder().size() == getTables().size()) { |
| // the user specified insert order only for the tables directly mapped by the descriptor, |
| // the inherited tables order must be the same as in parent descriptor |
| List<DatabaseTable> childMultipleTableInsertOrder = getMultipleTableInsertOrder(); |
| setMultipleTableInsertOrder(new ArrayList(getInheritancePolicy().getParentDescriptor().getMultipleTableInsertOrder())); |
| getMultipleTableInsertOrder().addAll(childMultipleTableInsertOrder); |
| } |
| |
| } |
| |
| if (getMultipleTableInsertOrder().size() != getTables().size()) { |
| throw DescriptorException.multipleTableInsertOrderMismatch(this); |
| } |
| |
| if(nParentTables == getTables().size()) { |
| // all the tables mapped by the parent - nothing to do. |
| return; |
| } |
| |
| if(getMultipleTableForeignKeys().isEmpty()) { |
| // nothing to do |
| return; |
| } |
| |
| verifyMultipleTablesForeignKeysTables(); |
| |
| // tableComparison[i][j] indicates the order between i and j tables: |
| // -1 i table before j table; |
| // +1 i table after j table; |
| // 0 - not defined (could be either before or after) |
| int[][] tableComparison = createTableComparison(getMultipleTableInsertOrder(), nParentTables); |
| |
| for(int i = nParentTables; i < getMultipleTableInsertOrder().size(); i++) { |
| for(int j = i + 1; j < getTables().size(); j++) { |
| if(tableComparison[i - nParentTables][j - nParentTables] > 0) { |
| throw DescriptorException.insertOrderConflictsWithMultipleTableForeignKeys(this, getMultipleTableInsertOrder().get(i), getMultipleTableInsertOrder().get(j)); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Verify that the tables specified in multipleTablesForeignKeysTables are valid. |
| */ |
| protected void verifyMultipleTablesForeignKeysTables() { |
| Iterator<Map.Entry<DatabaseTable, Set<DatabaseTable>>> itTargetTables = getMultipleTableForeignKeys().entrySet().iterator(); |
| while(itTargetTables.hasNext()) { |
| Map.Entry<DatabaseTable, Set<DatabaseTable>> entry = itTargetTables.next(); |
| DatabaseTable targetTable = entry.getKey(); |
| if (getTables().indexOf(targetTable) == -1) { |
| throw DescriptorException.illegalTableNameInMultipleTableForeignKeyField(this, targetTable); |
| } |
| Iterator<DatabaseTable> itSourceTables = entry.getValue().iterator(); |
| while(itSourceTables.hasNext()) { |
| DatabaseTable sourceTable = itSourceTables.next(); |
| if (getTables().indexOf(sourceTable) == -1) { |
| throw DescriptorException.illegalTableNameInMultipleTableForeignKeyField(this, targetTable); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This helper method creates a matrix that contains insertion order comparison for the tables. |
| * Comparison is done for indexes from nStart to tables.size()-1. |
| */ |
| protected int[][] createTableComparison(List<DatabaseTable> tables, int nStart) { |
| int nTables = tables.size(); |
| // tableComparison[i][j] indicates the order between i and j tables: |
| // -1 i table before j table; |
| // +1 i table after j table; |
| // 0 - not defined (could be either before or after) |
| int[][] tableComparison = new int[nTables - nStart][nTables - nStart]; |
| |
| Iterator<Map.Entry<DatabaseTable, Set<DatabaseTable>>> itTargetTables = getMultipleTableForeignKeys().entrySet().iterator(); |
| // loop through all pairs of target and source tables and insert either +1 or -1 into tableComparison for each pair. |
| while(itTargetTables.hasNext()) { |
| Map.Entry<DatabaseTable, Set<DatabaseTable>> entry = itTargetTables.next(); |
| DatabaseTable targetTable = entry.getKey(); |
| int targetIndex = tables.indexOf(targetTable) - nStart; |
| if(targetIndex >= 0) { |
| Set<DatabaseTable> sourceTables = entry.getValue(); |
| Iterator<DatabaseTable> itSourceTables = sourceTables.iterator(); |
| while(itSourceTables.hasNext()) { |
| DatabaseTable sourceTable = itSourceTables.next(); |
| int sourceIndex = tables.indexOf(sourceTable) - nStart; |
| if(sourceIndex >= 0) { |
| // targetTable should be before sourceTable: tableComparison[sourceIndex, targetIndex] = 1; tableComparison[targetIndex, sourceIndex] =-1. |
| if(tableComparison[targetIndex][sourceIndex] == 1) { |
| throw DescriptorException.insertOrderCyclicalDependencyBetweenTwoTables(this, sourceTable, targetTable); |
| } else { |
| tableComparison[targetIndex][sourceIndex] =-1; |
| tableComparison[sourceIndex][targetIndex] = 1; |
| } |
| } else { |
| throw DescriptorException.insertOrderChildBeforeParent(this, sourceTable, targetTable); |
| } |
| } |
| } |
| } |
| return tableComparison; |
| } |
| |
| /** |
| * INTERNAL: |
| * This helper method creates multipleTableInsertOrderFromComparison using comparison matrix |
| * created by createTableComparison(getTables()) method call. |
| */ |
| protected void createMultipleTableInsertOrderFromComparison(int[][] tableComparison, int nStart) { |
| int nTables = getTables().size(); |
| int[] tableOrder = new int[nTables - nStart]; |
| boolean bOk = createTableOrder(0, nTables - nStart, tableOrder, tableComparison); |
| if(bOk) { |
| if(nStart == 0) { |
| setMultipleTableInsertOrder(NonSynchronizedVector.newInstance(nTables)); |
| } |
| for(int k=0; k < nTables - nStart; k++) { |
| getMultipleTableInsertOrder().add(getTables().get(tableOrder[k] + nStart)); |
| } |
| } else { |
| throw DescriptorException.insertOrderCyclicalDependencyBetweenThreeOrMoreTables(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This helper method recursively puts indexes from 0 to nTables-1 into tableOrder according to tableComparison 2 dim array. |
| * k is index in tableOrder that currently the method is working on - the method should be called with k = 0. |
| */ |
| protected boolean createTableOrder(int k, int nTables, int[] tableOrder, int[][] tableComparison) { |
| if(k == nTables) { |
| return true; |
| } |
| |
| // array of indexes not yet ordered |
| int[] iAvailable = new int[nTables-k]; |
| int l = 0; |
| for(int i=0; i < nTables; i++) { |
| boolean isUsed = false; |
| for(int j=0; j<k && !isUsed; j++) { |
| if(i == tableOrder[j]) { |
| isUsed = true; |
| } |
| } |
| if(!isUsed) { |
| iAvailable[l] = i; |
| l++; |
| } |
| } |
| |
| boolean bOk = false; |
| for(int i=0; (i < nTables-k) && !bOk; i++) { |
| boolean isSmallest = true; |
| for(int j=0; (j < nTables-k) && isSmallest; j++) { |
| if(i != j) { |
| if(tableComparison[iAvailable[i]][iAvailable[j]] > 0) { |
| isSmallest = false; |
| } |
| } |
| } |
| if(isSmallest) { |
| // iAvailable[i] is less or equal according to tableComparison to all other remaining indexes - let's try to use it as tableOrder[k] |
| tableOrder[k] = iAvailable[i]; |
| // now try to fill out the last remaining n - k - 1 elements of tableOrder |
| bOk = createTableOrder(k + 1, nTables, tableOrder, tableComparison); |
| } |
| } |
| return bOk; |
| } |
| |
| /** |
| * INTERNAL: |
| * Clones the descriptor |
| */ |
| @Override |
| public Object clone() { |
| ClassDescriptor clonedDescriptor = null; |
| |
| // clones itself |
| try { |
| clonedDescriptor = (ClassDescriptor)super.clone(); |
| } catch (Exception exception) { |
| throw new AssertionError(exception); |
| } |
| |
| Vector mappingsVector = NonSynchronizedVector.newInstance(); |
| |
| // All the mappings |
| for (Enumeration<DatabaseMapping> mappingsEnum = getMappings().elements(); mappingsEnum.hasMoreElements();) { |
| DatabaseMapping mapping; |
| |
| mapping = (DatabaseMapping) mappingsEnum.nextElement().clone(); |
| mapping.setDescriptor(clonedDescriptor); |
| mappingsVector.addElement(mapping); |
| } |
| clonedDescriptor.setMappings(mappingsVector); |
| |
| Map queryKeys = new HashMap(getQueryKeys().size() + 2); |
| |
| // All the query keys |
| for (QueryKey queryKey : getQueryKeys().values()) { |
| queryKey = (QueryKey)queryKey.clone(); |
| queryKey.setDescriptor(clonedDescriptor); |
| queryKeys.put(queryKey.getName(), queryKey); |
| } |
| clonedDescriptor.setQueryKeys(queryKeys); |
| |
| // PrimaryKeyFields |
| List primaryKeyVector = new ArrayList(getPrimaryKeyFields().size()); |
| List<DatabaseField> primaryKeyFields = getPrimaryKeyFields(); |
| for (int index = 0; index < primaryKeyFields.size(); index++) { |
| DatabaseField primaryKey = primaryKeyFields.get(index).clone(); |
| primaryKeyVector.add(primaryKey); |
| } |
| clonedDescriptor.setPrimaryKeyFields(primaryKeyVector); |
| |
| // fields. |
| clonedDescriptor.setFields(NonSynchronizedVector.newInstance()); |
| |
| // Referencing classes |
| clonedDescriptor.referencingClasses = new HashSet<>(referencingClasses); |
| |
| // Post-calculate changes |
| if (this.mappingsPostCalculateChanges != null) { |
| clonedDescriptor.mappingsPostCalculateChanges = new ArrayList<>(); |
| for (DatabaseMapping databaseMapping : this.mappingsPostCalculateChanges) { |
| clonedDescriptor.mappingsPostCalculateChanges.add((DatabaseMapping)databaseMapping.clone()); |
| } |
| } |
| |
| // Post-calculate on delete |
| if (this.mappingsPostCalculateChangesOnDeleted != null) { |
| clonedDescriptor.mappingsPostCalculateChangesOnDeleted = new ArrayList<>(); |
| for (DatabaseMapping databaseMapping : this.mappingsPostCalculateChangesOnDeleted) { |
| clonedDescriptor.mappingsPostCalculateChangesOnDeleted.add((DatabaseMapping)databaseMapping.clone()); |
| } |
| } |
| |
| // The inheritance policy |
| if (clonedDescriptor.hasInheritance()) { |
| clonedDescriptor.setInheritancePolicy((InheritancePolicy)getInheritancePolicy().clone()); |
| clonedDescriptor.getInheritancePolicy().setDescriptor(clonedDescriptor); |
| } |
| |
| if (clonedDescriptor.hasSerializedObjectPolicy()) { |
| clonedDescriptor.setSerializedObjectPolicy(getSerializedObjectPolicy().clone()); |
| } |
| |
| // The returning policy |
| if (clonedDescriptor.hasReturningPolicy()) { |
| clonedDescriptor.setReturningPolicy((ReturningPolicy)getReturningPolicy().clone()); |
| clonedDescriptor.getReturningPolicy().setDescriptor(clonedDescriptor); |
| } |
| if (clonedDescriptor.hasReturningPolicies()) { |
| clonedDescriptor.returningPolicies = new ArrayList<>(); |
| for (ReturningPolicy returningPolicy : this.returningPolicies) { |
| clonedDescriptor.returningPolicies.add((ReturningPolicy)returningPolicy.clone()); |
| } |
| clonedDescriptor.prepareReturnFields(clonedDescriptor.returningPolicies); |
| } |
| |
| // The Object builder |
| clonedDescriptor.setObjectBuilder((ObjectBuilder)getObjectBuilder().clone()); |
| clonedDescriptor.getObjectBuilder().setDescriptor(clonedDescriptor); |
| |
| clonedDescriptor.setEventManager((DescriptorEventManager)getEventManager().clone()); |
| clonedDescriptor.getEventManager().setDescriptor(clonedDescriptor); |
| |
| // The Query manager |
| clonedDescriptor.setQueryManager((DescriptorQueryManager)getQueryManager().clone()); |
| clonedDescriptor.getQueryManager().setDescriptor(clonedDescriptor); |
| |
| //fetch group |
| if (hasFetchGroupManager()) { |
| clonedDescriptor.setFetchGroupManager((FetchGroupManager)getFetchGroupManager().clone()); |
| } |
| |
| if (this.cachePolicy != null) { |
| clonedDescriptor.setCachePolicy(this.cachePolicy.clone()); |
| } |
| |
| // Bug 3037701 - clone several more elements |
| if (this.instantiationPolicy != null) { |
| clonedDescriptor.setInstantiationPolicy((InstantiationPolicy)getInstantiationPolicy().clone()); |
| } |
| if (this.copyPolicy != null) { |
| clonedDescriptor.setCopyPolicy((CopyPolicy)getCopyPolicy().clone()); |
| } |
| if (getOptimisticLockingPolicy() != null) { |
| clonedDescriptor.setOptimisticLockingPolicy((OptimisticLockingPolicy)getOptimisticLockingPolicy().clone()); |
| } |
| //bug 5171059 clone change tracking policies as well |
| clonedDescriptor.setObjectChangePolicy(this.getObjectChangePolicyInternal()); |
| |
| // Clone the tables |
| Vector<DatabaseTable> tables = NonSynchronizedVector.newInstance(3); |
| for (DatabaseTable table : getTables()) { |
| tables.add(table.clone()); |
| } |
| clonedDescriptor.setTables(tables); |
| |
| // Clone the default table |
| if (getDefaultTable() != null) { |
| clonedDescriptor.setDefaultTable(getDefaultTable().clone()); |
| } |
| |
| // Clone the CMPPolicy |
| if (getCMPPolicy() != null) { |
| clonedDescriptor.setCMPPolicy(getCMPPolicy().clone()); |
| clonedDescriptor.getCMPPolicy().setDescriptor(clonedDescriptor); |
| } |
| |
| // Clone the sequence number field. |
| if (getSequenceNumberField() != null) { |
| clonedDescriptor.setSequenceNumberField(getSequenceNumberField().clone()); |
| } |
| |
| // Clone the multitenant policy. |
| if (hasMultitenantPolicy()) { |
| clonedDescriptor.setMultitenantPolicy(getMultitenantPolicy().clone(clonedDescriptor)); |
| } |
| |
| return clonedDescriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Convert all the class-name-based settings in this Descriptor to actual class-based |
| * settings. This method is used when converting a project that has been built |
| * with class names to a project with classes. |
| */ |
| public void convertClassNamesToClasses(ClassLoader classLoader){ |
| Class redirectorClass = null; |
| |
| if (getJavaClassName() != null){ |
| Class descriptorClass = null; |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| descriptorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(getJavaClassName(), true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getJavaClassName(), exception.getException()); |
| } |
| } else { |
| descriptorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getJavaClassName(), true, classLoader); |
| } |
| } catch (ClassNotFoundException exc){ |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getJavaClassName(), exc); |
| } |
| setJavaClass(descriptorClass); |
| } |
| |
| if (getAmendmentClassName() != null) { |
| Class amendmentClass = null; |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| amendmentClass = AccessController.doPrivileged(new PrivilegedClassForName<>(getAmendmentClassName(), true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getAmendmentClassName(), exception.getException()); |
| } |
| } else { |
| amendmentClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getAmendmentClassName(), true, classLoader); |
| } |
| } catch (ClassNotFoundException exc){ |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getAmendmentClassName(), exc); |
| } |
| setAmendmentClass(amendmentClass); |
| } |
| |
| if (copyPolicy == null && getCopyPolicyClassName() != null){ |
| Class copyPolicyClass = null; |
| CopyPolicy newCopyPolicy = null; |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| copyPolicyClass = AccessController.doPrivileged(new PrivilegedClassForName<>(getCopyPolicyClassName(), true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getCopyPolicyClassName(), exception.getException()); |
| } |
| try { |
| newCopyPolicy = (CopyPolicy)AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(copyPolicyClass)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(getCopyPolicyClassName(), exception.getException()); |
| } |
| } else { |
| copyPolicyClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getCopyPolicyClassName(), true, classLoader); |
| newCopyPolicy = (CopyPolicy)org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(copyPolicyClass); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(getCopyPolicyClassName(), exc); |
| } catch (IllegalAccessException ex){ |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(getCopyPolicyClassName(), ex); |
| } catch (InstantiationException e){ |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(getCopyPolicyClassName(), e); |
| } |
| setCopyPolicy(newCopyPolicy); |
| } |
| |
| if (this.serializedObjectPolicy != null && this.serializedObjectPolicy instanceof SerializedObjectPolicyWrapper) { |
| String serializedObjectPolicyClassName = ((SerializedObjectPolicyWrapper)this.serializedObjectPolicy).getSerializedObjectPolicyClassName(); |
| Class serializedObjectPolicyClass = null; |
| SerializedObjectPolicy newSerializedObjectPolicy = null; |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| serializedObjectPolicyClass = AccessController.doPrivileged(new PrivilegedClassForName<>(serializedObjectPolicyClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(serializedObjectPolicyClassName, exception.getException()); |
| } |
| try { |
| newSerializedObjectPolicy = (SerializedObjectPolicy)AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(serializedObjectPolicyClass)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(serializedObjectPolicyClassName, exception.getException()); |
| } |
| } else { |
| serializedObjectPolicyClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(serializedObjectPolicyClassName, true, classLoader); |
| newSerializedObjectPolicy = (SerializedObjectPolicy)org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(serializedObjectPolicyClass); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(serializedObjectPolicyClassName, exc); |
| } catch (IllegalAccessException ex){ |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(serializedObjectPolicyClassName, ex); |
| } catch (InstantiationException e){ |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(serializedObjectPolicyClassName, e); |
| } |
| newSerializedObjectPolicy.setField(this.serializedObjectPolicy.getField()); |
| setSerializedObjectPolicy(newSerializedObjectPolicy); |
| } |
| |
| //Create and set default QueryRedirector instances |
| if (this.defaultQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultQueryRedirectorClassName, true, classLoader); |
| setDefaultQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, e); |
| } |
| } |
| |
| if (this.defaultReadObjectQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultReadObjectQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultReadObjectQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReadObjectQueryRedirectorClassName, true, classLoader); |
| setDefaultReadObjectQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, e); |
| } |
| } |
| if (this.defaultReadAllQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultReadAllQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultReadAllQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReadAllQueryRedirectorClassName, true, classLoader); |
| setDefaultReadAllQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, e); |
| } |
| } |
| if (this.defaultReportQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultReportQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultReportQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReportQueryRedirectorClassName, true, classLoader); |
| setDefaultReportQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, e); |
| } |
| } |
| if (this.defaultInsertObjectQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultInsertObjectQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultInsertObjectQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultInsertObjectQueryRedirectorClassName, true, classLoader); |
| setDefaultInsertObjectQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, e); |
| } |
| } |
| if (this.defaultUpdateObjectQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultUpdateObjectQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultUpdateObjectQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultUpdateObjectQueryRedirectorClassName, true, classLoader); |
| setDefaultUpdateObjectQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, e); |
| } |
| } |
| if (this.defaultDeleteObjectQueryRedirectorClassName != null){ |
| try{ |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| redirectorClass = AccessController.doPrivileged(new PrivilegedClassForName<>(defaultDeleteObjectQueryRedirectorClassName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, exception.getException()); |
| } |
| try { |
| setDefaultDeleteObjectQueryRedirector((QueryRedirector) AccessController.doPrivileged(new PrivilegedNewInstanceFromClass(redirectorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, exception.getException()); |
| } |
| } else { |
| redirectorClass = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultDeleteObjectQueryRedirectorClassName, true, classLoader); |
| setDefaultDeleteObjectQueryRedirector((QueryRedirector) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass)); |
| } |
| } catch (ClassNotFoundException exc) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, exc); |
| } catch (Exception e) { |
| // Catches IllegalAccessException and InstantiationException |
| throw ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, e); |
| } |
| } |
| Iterator<DatabaseMapping> mappings = getMappings().iterator(); |
| while (mappings.hasNext()){ |
| mappings.next().convertClassNamesToClasses(classLoader); |
| } |
| if (this.inheritancePolicy != null){ |
| this.inheritancePolicy.convertClassNamesToClasses(classLoader); |
| } |
| if (this.interfacePolicy != null){ |
| this.interfacePolicy.convertClassNamesToClasses(classLoader); |
| } |
| if (this.instantiationPolicy != null){ |
| this.instantiationPolicy.convertClassNamesToClasses(classLoader); |
| } |
| if (hasCMPPolicy()) { |
| getCMPPolicy().convertClassNamesToClasses(classLoader); |
| } |
| if(this.queryManager != null) { |
| this.queryManager.convertClassNamesToClasses(classLoader); |
| } |
| if(this.cachePolicy != null) { |
| this.cachePolicy.convertClassNamesToClasses(classLoader); |
| } |
| |
| if (hasUnconvertedProperties()) { |
| for (String propertyName : getUnconvertedProperties().keySet()) { |
| List<String> valuePair = getUnconvertedProperties().get(propertyName); |
| String value = valuePair.get(0); |
| String valueTypeName = valuePair.get(1); |
| Class<String> valueType = String.class; |
| |
| if (valueTypeName != null) { |
| // Have to initialize the valueType now |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| valueType = AccessController.doPrivileged(new PrivilegedClassForName<>(valueTypeName, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(valueTypeName, exception.getException()); |
| } |
| } else { |
| valueType = org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(valueTypeName, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc){ |
| throw ValidationException.classNotFoundWhileConvertingClassNames(valueTypeName, exc); |
| } |
| } |
| |
| // Add the converted property. If the value type is the same |
| // as the source (value) type, no conversion is made. |
| getProperties().put(propertyName, ConversionManager.getDefaultManager().convertObject(value, valueType)); |
| } |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Create a copy policy of the type passed in as a string. |
| */ |
| public void createCopyPolicy(String policyType) { |
| if (policyType.equals("clone")) { |
| useCloneCopyPolicy(); |
| return; |
| } |
| if (policyType.equals("constructor")) { |
| useInstantiationCopyPolicy(); |
| return; |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Create a instantiation policy of the type passed in as a string. |
| */ |
| public void createInstantiationPolicy(String policyType) { |
| if (policyType.equals("static method")) { |
| //do nothing for now |
| return; |
| } |
| if (policyType.equals("constructor")) { |
| useDefaultConstructorInstantiationPolicy(); |
| return; |
| } |
| if (policyType.equals("factory")) { |
| //do nothing for now |
| return; |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the descriptor to be an aggregate. |
| * An aggregate descriptor is contained within another descriptor's table. |
| * Aggregate descriptors are insert/updated/deleted with their owner and cannot exist without their owner as they share the same row. |
| * Aggregates are not cached (they are cached as part of their owner) and cannot be read/written/deleted/registered. |
| * All aggregate descriptors must call this. |
| */ |
| public void descriptorIsAggregate() { |
| setDescriptorType(AGGREGATE); |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the descriptor to be part of an aggregate collection. |
| * An aggregate collection descriptor stored in a separate table but some of the fields (the primary key) comes from its owner. |
| * Aggregate collection descriptors are insert/updated/deleted with their owner and cannot exist without their owner as they share the primary key. |
| * Aggregate collections are not cached (they are cached as part of their owner) and cannot be read/written/deleted/registered. |
| * All aggregate collection descriptors must call this. |
| */ |
| public void descriptorIsAggregateCollection() { |
| setDescriptorType(AGGREGATE_COLLECTION); |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the descriptor to be for an interface. |
| * An interface descriptor allows for other classes to reference an interface or one of several other classes. |
| * The implementor classes can be completely unrelated in term of the database stored in distinct tables. |
| * Queries can also be done for the interface which will query each of the implementor classes. |
| * An interface descriptor cannot define any mappings as an interface is just API and not state, |
| * a interface descriptor should define the common query key of its implementors to allow querying. |
| * An interface descriptor also does not define a primary key or table or other settings. |
| * If an interface only has a single implementor (i.e. a classes public interface or remote) then an interface |
| * descriptor should not be defined for it and relationships should be to the implementor class not the interface, |
| * in this case the implementor class can add the interface through its interface policy to map queries on the interface to it. |
| */ |
| public void descriptorIsForInterface() { |
| setDescriptorType(INTERFACE); |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the descriptor to be normal. |
| * This is the default and means the descriptor is not aggregate or for an interface. |
| */ |
| public void descriptorIsNormal() { |
| setDescriptorType(NORMAL); |
| } |
| |
| /** |
| * PUBLIC: |
| * Allow for cache hits on primary key read object queries to be disabled. |
| * This can be used with {@link #alwaysRefreshCache} or {@link #alwaysRefreshCacheOnRemote} to ensure queries always go to the database. |
| */ |
| public void disableCacheHits() { |
| setShouldDisableCacheHits(true); |
| } |
| |
| /** |
| * PUBLIC: |
| * Allow for remote session cache hits on primary key read object queries to be disabled. |
| * This can be used with alwaysRefreshCacheOnRemote() to ensure queries always go to the server session cache. |
| * |
| * @see #alwaysRefreshCacheOnRemote() |
| */ |
| public void disableCacheHitsOnRemote() { |
| setShouldDisableCacheHitsOnRemote(true); |
| } |
| |
| /** |
| * PUBLIC: |
| * The descriptor is defined to not conform the results in unit of work in read query. Default. |
| * |
| */ |
| public void dontAlwaysConformResultsInUnitOfWork() { |
| setShouldAlwaysConformResultsInUnitOfWork(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldAlwaysRefreshCache} with an argument of <CODE>false</CODE>: |
| * it ensures that a <CODE>ClassDescriptor</CODE> is not configured to always refresh the cache if data is received from the database by any query. |
| * |
| * @see #alwaysRefreshCache |
| */ |
| public void dontAlwaysRefreshCache() { |
| setShouldAlwaysRefreshCache(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldAlwaysRefreshCacheOnRemote} with an argument of <CODE>false</CODE>: |
| * it ensures that a <CODE>ClassDescriptor</CODE> is not configured to always remotely refresh the cache if data is received from the |
| * database by any query in a {@link org.eclipse.persistence.sessions.remote.RemoteSession}. |
| * |
| * @see #alwaysRefreshCacheOnRemote |
| */ |
| public void dontAlwaysRefreshCacheOnRemote() { |
| setShouldAlwaysRefreshCacheOnRemote(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * Allow for cache hits on primary key read object queries. |
| * |
| * @see #disableCacheHits() |
| */ |
| public void dontDisableCacheHits() { |
| setShouldDisableCacheHits(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * Allow for remote session cache hits on primary key read object queries. |
| * |
| * @see #disableCacheHitsOnRemote() |
| */ |
| public void dontDisableCacheHitsOnRemote() { |
| setShouldDisableCacheHitsOnRemote(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldOnlyRefreshCacheIfNewerVersion} with an argument of <CODE>false</CODE>: |
| * it ensures that a <CODE>ClassDescriptor</CODE> is not configured to only refresh the cache if the data received from the database by |
| * a query is newer than the data in the cache (as determined by the optimistic locking field). |
| * |
| * @see #onlyRefreshCacheIfNewerVersion |
| */ |
| public void dontOnlyRefreshCacheIfNewerVersion() { |
| setShouldOnlyRefreshCacheIfNewerVersion(false); |
| } |
| |
| /** |
| * INTERNAL: |
| * The first table in the tables is always treated as default. |
| */ |
| protected DatabaseTable extractDefaultTable() { |
| if (getTables().isEmpty()) { |
| if (isChildDescriptor()) { |
| return getInheritancePolicy().getParentDescriptor().extractDefaultTable(); |
| } else { |
| return null; |
| } |
| } |
| |
| return getTables().get(0); |
| } |
| |
| /** |
| * INTERNAL: |
| * additionalAggregateCollectionKeyFields are used by aggregate descriptors to hold additional fields needed when they are stored in an AggregatateCollection |
| * These fields are generally foreign key fields that are required in addition to the fields in the descriptor's |
| * mappings to uniquely identify the Aggregate |
| */ |
| public List<DatabaseField> getAdditionalAggregateCollectionKeyFields(){ |
| if (additionalAggregateCollectionKeyFields == null){ |
| additionalAggregateCollectionKeyFields = new ArrayList<>(); |
| } |
| return additionalAggregateCollectionKeyFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * This is used to map the primary key field names in a multiple table descriptor. |
| */ |
| public Map<DatabaseTable, Map<DatabaseField, DatabaseField>> getAdditionalTablePrimaryKeyFields() { |
| if (additionalTablePrimaryKeyFields == null) { |
| additionalTablePrimaryKeyFields = new HashMap(5); |
| } |
| return additionalTablePrimaryKeyFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return a list of fields that are written by map keys |
| * Used to determine if there is a multiple writable mappings issue |
| */ |
| public List<DatabaseField> getAdditionalWritableMapKeyFields() { |
| if (additionalWritableMapKeyFields == null) { |
| additionalWritableMapKeyFields = new ArrayList(2); |
| } |
| return additionalWritableMapKeyFields; |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the alias |
| */ |
| public String getAlias() { |
| |
| /* CR3310: Steven Vo |
| * Default alias to the Java class name if the alias is not set |
| */ |
| if ((alias == null) && (getJavaClassName() != null)) { |
| alias = org.eclipse.persistence.internal.helper.Helper.getShortClassName(getJavaClassName()); |
| } |
| return alias; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the fields which include all child class fields. |
| * By default it is initialized to the fields for the current descriptor. |
| */ |
| public Vector<DatabaseField> getAllFields() { |
| return allFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all selection fields which include all child class fields. |
| * By default it is initialized to selection fields for the current descriptor. |
| */ |
| public List<DatabaseField> getAllSelectionFields() { |
| return allSelectionFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all selection fields which include all child class fields. |
| * By default it is initialized to selection fields for the current descriptor. |
| */ |
| public List<DatabaseField> getAllSelectionFields(ObjectLevelReadQuery query) { |
| if (hasSerializedObjectPolicy() && query.shouldUseSerializedObjectPolicy()) { |
| return this.serializedObjectPolicy.getAllSelectionFields(); |
| } else { |
| return allSelectionFields; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return fields used to build insert statement. |
| */ |
| public Vector<DatabaseField> getReturnFieldsToGenerateInsert() { |
| return this.returnFieldsToGenerateInsert; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return fields used to build update statement. |
| */ |
| public Vector<DatabaseField> getReturnFieldsToGenerateUpdate() { |
| return this.returnFieldsToGenerateUpdate; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return fields used in to map into entity for insert. |
| */ |
| public List<DatabaseField> getReturnFieldsToMergeInsert() { |
| return this.returnFieldsToMergeInsert; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return fields used in to map into entity for update. |
| */ |
| public List<DatabaseField> getReturnFieldsToMergeUpdate() { |
| return this.returnFieldsToMergeUpdate; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the amendment class. |
| * The amendment method will be called on the class before initialization to allow for it to initialize the descriptor. |
| * The method must be a public static method on the class. |
| */ |
| public Class getAmendmentClass() { |
| return amendmentClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return amendment class name, used by the MW. |
| */ |
| public String getAmendmentClassName() { |
| if ((amendmentClassName == null) && (amendmentClass != null)) { |
| amendmentClassName = amendmentClass.getName(); |
| } |
| return amendmentClassName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the amendment method. |
| * This will be called on the amendment class before initialization to allow for it to initialize the descriptor. |
| * The method must be a public static method on the class. |
| */ |
| public String getAmendmentMethodName() { |
| return amendmentMethodName; |
| } |
| |
| /** |
| * @return the accessorTree |
| */ |
| public List<AttributeAccessor> getAccessorTree() { |
| return accessorTree; |
| } |
| |
| |
| /** |
| * PUBLIC: |
| * Return this objects ObjectChangePolicy. |
| */ |
| public ObjectChangePolicy getObjectChangePolicy() { |
| // part of fix for 4410581: project xml must save change policy |
| // if no change-policy XML element, field is null: lazy-init to default |
| if (changePolicy == null) { |
| changePolicy = new DeferredChangeDetectionPolicy(); |
| } |
| return changePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return this objects ObjectChangePolicy and do not lazy initialize |
| */ |
| public ObjectChangePolicy getObjectChangePolicyInternal() { |
| return changePolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return this descriptor's HistoryPolicy. |
| */ |
| public HistoryPolicy getHistoryPolicy() { |
| return historyPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the descriptor's partitioning policy. |
| */ |
| public PartitioningPolicy getPartitioningPolicy() { |
| return partitioningPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor's partitioning policy. |
| * A PartitioningPolicy is used to partition the data for a class across multiple difference databases |
| * or across a database cluster such as Oracle RAC. |
| * Partitioning can provide improved scalability by allowing multiple database machines to service requests. |
| */ |
| public void setPartitioningPolicy(PartitioningPolicy partitioningPolicy) { |
| this.partitioningPolicy = partitioningPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the name of the descriptor's partitioning policy. |
| * A PartitioningPolicy with the same name must be defined on the Project. |
| * A PartitioningPolicy is used to partition the data for a class across multiple difference databases |
| * or across a database cluster such as Oracle RAC. |
| * Partitioning can provide improved scalability by allowing multiple database machines to service requests. |
| */ |
| public String getPartitioningPolicyName() { |
| return partitioningPolicyName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the name of the descriptor's partitioning policy. |
| * A PartitioningPolicy with the same name must be defined on the Project. |
| * A PartitioningPolicy is used to partition the data for a class across multiple difference databases |
| * or across a database cluster such as Oracle RAC. |
| * Partitioning can provide improved scalability by allowing multiple database machines to service requests. |
| */ |
| public void setPartitioningPolicyName(String partitioningPolicyName) { |
| this.partitioningPolicyName = partitioningPolicyName; |
| } |
| |
| /** |
| * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction |
| * between EclipseLink and the internal cache for that class will pass through the Interceptor. |
| * Advanced users could use this interceptor to audit, profile or log cache access. This Interceptor |
| * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism. |
| * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor. |
| * |
| * As with IdentityMaps an entire class inheritance hierarchy will share the same interceptor. |
| * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor |
| */ |
| public Class<? extends CacheInterceptor> getCacheInterceptorClass() { |
| return getCachePolicy().getCacheInterceptorClass(); |
| } |
| |
| /** |
| * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction |
| * between EclipseLink and the internal cache for that class will pass through the Interceptor. |
| * Advanced users could use this interceptor to audit, profile or log cache access. This Interceptor |
| * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism. |
| * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor. |
| * |
| * As with IdentityMaps an entire class inheritance hierarchy will share the same interceptor. |
| * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor |
| */ |
| public String getCacheInterceptorClassName() { |
| return getCachePolicy().getCacheInterceptorClassName(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the CacheInvalidationPolicy for this descriptor |
| * For uninitialized cache invalidation policies, this will return a NoExpiryCacheInvalidationPolicy |
| * @return CacheInvalidationPolicy |
| * @see org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy |
| */ |
| public CacheInvalidationPolicy getCacheInvalidationPolicy() { |
| if (cacheInvalidationPolicy == null) { |
| cacheInvalidationPolicy = new NoExpiryCacheInvalidationPolicy(); |
| } |
| return cacheInvalidationPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Get a value indicating the type of cache synchronization that will be used on objects of |
| * this type. Possible values are: |
| * SEND_OBJECT_CHANGES |
| * INVALIDATE_CHANGED_OBJECTS |
| * SEND_NEW_OBJECTS+WITH_CHANGES |
| * DO_NOT_SEND_CHANGES |
| */ |
| public int getCacheSynchronizationType() { |
| return getCachePolicy().getCacheSynchronizationType(); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public List<CascadeLockingPolicy> getCascadeLockingPolicies() { |
| if (this.cascadeLockingPolicies == null) { |
| this.cascadeLockingPolicies = new ArrayList(); |
| } |
| return cascadeLockingPolicies; |
| } |
| |
| /** |
| * ADVANCED: |
| * automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings. |
| * In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint, |
| * this defines that this descriptor has a foreign key constraint to another class and must be inserted after |
| * instances of the other class. |
| */ |
| public Vector getConstraintDependencies() { |
| if (constraintDependencies == null) { |
| constraintDependencies = NonSynchronizedVector.newInstance(1); |
| } |
| return constraintDependencies; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the copy policy. |
| */ |
| public CopyPolicy getCopyPolicy() { |
| // Lazy initialize for XML deployment. |
| if (copyPolicy == null) { |
| setCopyPolicy(new InstantiationCopyPolicy()); |
| } |
| return copyPolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the name of a Class that implements CopyPolicy |
| * Will be instantiated as a copy policy at initialization times |
| * using the no-args constructor |
| */ |
| public String getCopyPolicyClassName(){ |
| return copyPolicyClassName; |
| } |
| |
| /** |
| * INTERNAL: |
| * The first table in the tables is always treated as default. |
| */ |
| public DatabaseTable getDefaultTable() { |
| return defaultTable; |
| } |
| |
| /** |
| * ADVANCED: |
| * return the descriptor type (NORMAL by default, others include INTERFACE, AGGREGATE, AGGREGATE COLLECTION) |
| */ |
| public int getDescriptorType() { |
| return descriptorType; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is explicitly used by the XML reader. |
| */ |
| public String getDescriptorTypeValue() { |
| if (isAggregateCollectionDescriptor()) { |
| return "Aggregate collection"; |
| } else if (isAggregateDescriptor()) { |
| return "Aggregate"; |
| } else if (isDescriptorForInterface()) { |
| return "Interface"; |
| } else { |
| // Default. |
| return "Normal"; |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the derives id mappings. |
| */ |
| public Collection<DatabaseMapping> getDerivesIdMappinps() { |
| return derivesIdMappings.values(); |
| } |
| |
| /** |
| * INTERNAL: |
| * DescriptorCustomizer is the JPA equivalent of an amendment method. |
| */ |
| public String getDescriptorCustomizerClassName(){ |
| return descriptorCustomizerClassName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the event manager for the descriptor. The event manager is responsible |
| * for managing the pre/post selectors. |
| */ |
| public DescriptorEventManager getDescriptorEventManager() { |
| return getEventManager(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the event manager for the descriptor. The event manager is responsible |
| * for managing the pre/post selectors. |
| */ |
| @Override |
| public DescriptorEventManager getEventManager() { |
| // Lazy initialize for XML deployment. |
| if (eventManager == null) { |
| setEventManager(new org.eclipse.persistence.descriptors.DescriptorEventManager()); |
| } |
| return eventManager; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the fields |
| */ |
| public Vector<DatabaseField> getFields() { |
| if (fields == null) { |
| fields = NonSynchronizedVector.newInstance(); |
| } |
| return fields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all selection fields |
| */ |
| public List<DatabaseField> getSelectionFields() { |
| return selectionFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all selection fields |
| */ |
| public List<DatabaseField> getSelectionFields(ObjectLevelReadQuery query) { |
| if (hasSerializedObjectPolicy() && query.shouldUseSerializedObjectPolicy()) { |
| return this.serializedObjectPolicy.getSelectionFields(); |
| } else { |
| return selectionFields; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public Set<DatabaseField> getForeignKeyValuesForCaching() { |
| return foreignKeyValuesForCaching; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the class of identity map to be used by this descriptor. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public Class getIdentityMapClass() { |
| return getCachePolicy().getIdentityMapClass(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the size of the identity map. |
| */ |
| public int getIdentityMapSize() { |
| return getCachePolicy().getIdentityMapSize(); |
| } |
| |
| /** |
| * PUBLIC: |
| * The inheritance policy is used to define how a descriptor takes part in inheritance. |
| * All inheritance properties for both child and parent classes is configured in inheritance policy. |
| * Caution must be used in using this method as it lazy initializes an inheritance policy. |
| * Calling this on a descriptor that does not use inheritance will cause problems, #hasInheritance() must always first be called. |
| */ |
| public InheritancePolicy getDescriptorInheritancePolicy() { |
| return getInheritancePolicy(); |
| } |
| |
| /** |
| * PUBLIC: |
| * The inheritance policy is used to define how a descriptor takes part in inheritance. |
| * All inheritance properties for both child and parent classes is configured in inheritance policy. |
| * Caution must be used in using this method as it lazy initializes an inheritance policy. |
| * Calling this on a descriptor that does not use inheritance will cause problems, #hasInheritance() must always first be called. |
| */ |
| @Override |
| public InheritancePolicy getInheritancePolicy() { |
| if (inheritancePolicy == null) { |
| // Lazy initialize to conserve space in non-inherited classes. |
| setInheritancePolicy(new org.eclipse.persistence.descriptors.InheritancePolicy(this)); |
| } |
| return inheritancePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the inheritance policy. |
| */ |
| public InheritancePolicy getInheritancePolicyOrNull() { |
| return inheritancePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the instantiation policy. |
| */ |
| @Override |
| public InstantiationPolicy getInstantiationPolicy() { |
| // Lazy initialize for XML deployment. |
| if (instantiationPolicy == null) { |
| setInstantiationPolicy(new InstantiationPolicy()); |
| } |
| return instantiationPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the InterfacePolicy. |
| * The interface policy allows for a descriptor's public and variable interfaces to be defined. |
| * Caution must be used in using this method as it lazy initializes an interface policy. |
| * Calling this on a descriptor that does not use interfaces will cause problems, #hasInterfacePolicy() must always first be called. |
| */ |
| public InterfacePolicy getInterfacePolicy() { |
| if (interfacePolicy == null) { |
| // Lazy initialize to conserve space in non-inherited classes. |
| setInterfacePolicy(new InterfacePolicy(this)); |
| } |
| return interfacePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the InterfacePolicy. |
| */ |
| public InterfacePolicy getInterfacePolicyOrNull() { |
| return interfacePolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the java class. |
| */ |
| @Override |
| public Class getJavaClass() { |
| return javaClass; |
| } |
| |
| /** |
| * Return the class name, used by the MW. |
| */ |
| public String getJavaClassName() { |
| if ((javaClassName == null) && (javaClass != null)) { |
| javaClassName = javaClass.getName(); |
| } |
| return javaClassName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns a reference to the mappings that must be traverse when locking |
| */ |
| public List<DatabaseMapping> getLockableMappings() { |
| if (this.lockableMappings == null) { |
| this.lockableMappings = new ArrayList(); |
| } |
| return this.lockableMappings; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the mapping associated with a given attribute name. |
| * This can be used to find a descriptors mapping in a amendment method before the descriptor has been initialized. |
| */ |
| public DatabaseMapping getMappingForAttributeName(String attributeName) { |
| // ** Don't use this internally, just for amendments, see getMappingForAttributeName on ObjectBuilder. |
| for (Enumeration<DatabaseMapping> mappingsNum = mappings.elements(); mappingsNum.hasMoreElements();) { |
| DatabaseMapping mapping = mappingsNum.nextElement(); |
| if ((mapping.getAttributeName() != null) && mapping.getAttributeName().equals(attributeName)) { |
| return mapping; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * ADVANCED: |
| * Removes the locally defined mapping associated with a given attribute name. |
| * This can be used in a amendment method before the descriptor has been initialized. |
| */ |
| public DatabaseMapping removeMappingForAttributeName(String attributeName) { |
| DatabaseMapping mapping = getMappingForAttributeName(attributeName); |
| getMappings().remove(mapping); |
| return mapping; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns mappings |
| */ |
| public Vector<DatabaseMapping> getMappings() { |
| return mappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the foreign key relationships used for multiple tables which were specified by the user. Used |
| * by the Project XML writer to output these associations |
| * |
| * @see #adjustMultipleTableInsertOrder() |
| */ |
| public Vector getMultipleTableForeignKeyAssociations() { |
| Vector associations = new Vector(getAdditionalTablePrimaryKeyFields().size() * 2); |
| Iterator<Map<DatabaseField, DatabaseField>> tablesHashtable = getAdditionalTablePrimaryKeyFields().values().iterator(); |
| while (tablesHashtable.hasNext()) { |
| Map<DatabaseField, DatabaseField> tableHash = tablesHashtable.next(); |
| Iterator<DatabaseField> fieldEnumeration = tableHash.keySet().iterator(); |
| while (fieldEnumeration.hasNext()) { |
| DatabaseField keyField = fieldEnumeration.next(); |
| |
| //PRS#36802(CR#2057) contains() is changed to containsKey() |
| if (getMultipleTableForeignKeys().containsKey(keyField.getTable())) { |
| Association association = new Association(keyField.getQualifiedName(), tableHash.get(keyField).getQualifiedName()); |
| associations.addElement(association); |
| } |
| } |
| } |
| return associations; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the foreign key relationships used for multiple tables which were specified by the user. The key |
| * of the Map is the field in the source table of the foreign key relationship. The value is the field |
| * name of the target table. |
| * |
| * @see #adjustMultipleTableInsertOrder() |
| */ |
| public Map<DatabaseTable, Set<DatabaseTable>> getMultipleTableForeignKeys() { |
| if (multipleTableForeignKeys == null) { |
| multipleTableForeignKeys = new HashMap<>(5); |
| } |
| return multipleTableForeignKeys; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the List of DatabaseTables in the order which INSERTS should take place. This order is |
| * determined by the foreign key fields which are specified by the user. |
| */ |
| public List<DatabaseTable> getMultipleTableInsertOrder() throws DescriptorException { |
| return multipleTableInsertOrder; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the foreign key relationships used for multiple tables which were specified by the user. Used |
| * by the Project XML writer to output these associations |
| * |
| * @see #adjustMultipleTableInsertOrder() |
| */ |
| public Vector getMultipleTablePrimaryKeyAssociations() { |
| Vector associations = new Vector(getAdditionalTablePrimaryKeyFields().size() * 2); |
| Iterator<Map<DatabaseField, DatabaseField>> tablesHashtable = getAdditionalTablePrimaryKeyFields().values().iterator(); |
| while (tablesHashtable.hasNext()) { |
| Map<DatabaseField, DatabaseField> tableHash = tablesHashtable.next(); |
| Iterator<DatabaseField> fieldEnumeration = tableHash.keySet().iterator(); |
| while (fieldEnumeration.hasNext()) { |
| DatabaseField keyField = fieldEnumeration.next(); |
| |
| //PRS#36802(CR#2057) contains() is changed to containsKey() |
| if (!getMultipleTableForeignKeys().containsKey(keyField.getTable())) { |
| Association association = new Association(keyField.getQualifiedName(), tableHash.get(keyField).getQualifiedName()); |
| associations.addElement(association); |
| } |
| } |
| } |
| return associations; |
| } |
| |
| /** |
| * INTERNAL: |
| * Retun the multitenant policy |
| */ |
| public MultitenantPolicy getMultitenantPolicy() { |
| return multitenantPolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the object builder |
| */ |
| @Override |
| public ObjectBuilder getObjectBuilder() { |
| return objectBuilder; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the OptimisticLockingPolicy. By default this is an instance of VersionLockingPolicy. |
| */ |
| public OptimisticLockingPolicy getOptimisticLockingPolicy() { |
| return optimisticLockingPolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set of mappings that require early delete behavior. |
| * This is used to handle deletion constraints. |
| */ |
| public List<DatabaseMapping> getPreDeleteMappings() { |
| if (this.preDeleteMappings == null) { |
| this.preDeleteMappings = new ArrayList<>(); |
| } |
| return this.preDeleteMappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * Add the mapping to be notified before deletion. |
| * Must also be added to child descriptors. |
| */ |
| public void addPreDeleteMapping(DatabaseMapping mapping) { |
| getPreDeleteMappings().add(mapping); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the names of all the primary keys. |
| */ |
| @Override |
| public Vector<String> getPrimaryKeyFieldNames() { |
| Vector<String> result = new Vector(getPrimaryKeyFields().size()); |
| List<DatabaseField> primaryKeyFields = getPrimaryKeyFields(); |
| for (int index = 0; index < primaryKeyFields.size(); index++) { |
| result.addElement(primaryKeyFields.get(index).getQualifiedName()); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the primary key fields |
| */ |
| @Override |
| public List<DatabaseField> getPrimaryKeyFields() { |
| return primaryKeyFields; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the user defined properties. |
| */ |
| public Map getProperties() { |
| if (properties == null) { |
| properties = new HashMap(5); |
| } |
| return properties; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the descriptor property associated the given String. |
| */ |
| public Object getProperty(String name) { |
| return getProperties().get(name); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the query key with the specified name |
| */ |
| public QueryKey getQueryKeyNamed(String queryKeyName) { |
| return this.getQueryKeys().get(queryKeyName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the query keys. |
| */ |
| public Map<String, QueryKey> getQueryKeys() { |
| return queryKeys; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the queryManager. |
| * The query manager can be used to specify customization of the SQL |
| * that generates for this descriptor. |
| */ |
| public DescriptorQueryManager getDescriptorQueryManager() { |
| return this.getQueryManager(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the queryManager. |
| * The query manager can be used to specify customization of the SQL |
| * that generates for this descriptor. |
| */ |
| public DescriptorQueryManager getQueryManager() { |
| // Lazy initialize for XML deployment. |
| if (queryManager == null) { |
| setQueryManager(new org.eclipse.persistence.descriptors.DescriptorQueryManager()); |
| } |
| return queryManager; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the class of identity map to be used by this descriptor. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public Class getRemoteIdentityMapClass() { |
| return getCachePolicy().getRemoteIdentityMapClass(); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method returns the root descriptor for for this descriptor's class heirarchy. |
| * If the user is not using inheritance then the root class will be this class. |
| */ |
| public ClassDescriptor getRootDescriptor(){ |
| if (this.hasInheritance()){ |
| return this.getInheritancePolicy().getRootParentDescriptor(); |
| } |
| return this; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the size of the remote identity map. |
| */ |
| public int getRemoteIdentityMapSize() { |
| return getCachePolicy().getRemoteIdentityMapSize(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return returning policy. |
| */ |
| public ReturningPolicy getReturningPolicy() { |
| return returningPolicy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return returning policy from current descriptor and from mappings |
| */ |
| public List<ReturningPolicy> getReturningPolicies() { |
| return returningPolicies; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get sequence number field |
| */ |
| public DatabaseField getSequenceNumberField() { |
| return sequenceNumberField; |
| } |
| |
| /** |
| * PUBLIC: |
| * Get sequence number field name |
| */ |
| public String getSequenceNumberFieldName() { |
| if (getSequenceNumberField() == null) { |
| return null; |
| } |
| return getSequenceNumberField().getQualifiedName(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Get sequence number name |
| */ |
| public String getSequenceNumberName() { |
| return sequenceNumberName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the name of the session local to this descriptor. |
| * This is used by the session broker. |
| */ |
| public String getSessionName() { |
| return sessionName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Checks if table name exists with the current descriptor or not. |
| */ |
| public DatabaseTable getTable(String tableName) throws DescriptorException { |
| if (hasTablePerMultitenantPolicy()) { |
| DatabaseTable table = ((TablePerMultitenantPolicy) getMultitenantPolicy()).getTable(tableName); |
| |
| if (table != null) { |
| return table; |
| } |
| } |
| |
| if (getTables().isEmpty()) { |
| return null;// Assume aggregate descriptor. |
| } |
| |
| for (Enumeration<DatabaseTable> tables = getTables().elements(); tables.hasMoreElements();) { |
| DatabaseTable table = tables.nextElement(); |
| |
| if(tableName.indexOf(' ') != -1) { |
| //if looking for a table with a ' ' character, the name will have |
| //been quoted internally. Check for match without quotes. |
| String currentTableName = table.getName(); |
| if(currentTableName.substring(1, currentTableName.length() - 1).equals(tableName)) { |
| return table; |
| } |
| } |
| if (table.getName().equals(tableName)) { |
| return table; |
| } |
| } |
| |
| if (isAggregateDescriptor()) { |
| return getDefaultTable(); |
| } |
| throw DescriptorException.tableNotPresent(tableName, this); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the name of the descriptor's first table. |
| * This method must only be called on single table descriptors. |
| */ |
| public String getTableName() { |
| if (getTables().isEmpty()) { |
| return null; |
| } else { |
| return getTables().get(0).getName(); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the table names. |
| */ |
| public Vector getTableNames() { |
| Vector tableNames = new Vector(getTables().size()); |
| for (Enumeration<DatabaseTable> fieldsEnum = getTables().elements(); fieldsEnum.hasMoreElements();) { |
| tableNames.addElement(fieldsEnum.nextElement().getQualifiedName()); |
| } |
| |
| return tableNames; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns the TablePerClassPolicy. |
| * The table per class policy allows JPA users to configure the |
| * TABLE_PER_CLASS inheritance strategy. Calling this on a descriptor that |
| * does not use table per class will cause problems, |
| * #hasTablePerClassPolicy() must always first be called. |
| * @see #setTablePerClassPolicy |
| */ |
| public TablePerClassPolicy getTablePerClassPolicy() { |
| return (TablePerClassPolicy) interfacePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the tables. |
| */ |
| public Vector<DatabaseTable> getTables() { |
| return tables; |
| } |
| |
| /** |
| * INTERNAL: |
| * searches first descriptor than its ReturningPolicy for an equal field |
| */ |
| @Override |
| public DatabaseField getTypedField(DatabaseField field) { |
| boolean mayBeMoreThanOne = hasMultipleTables() && !field.hasTableName(); |
| DatabaseField foundField = null; |
| for (int index = 0; index < getFields().size(); index++) { |
| DatabaseField descField = getFields().get(index); |
| if (field.equals(descField)) { |
| if (descField.getType() != null) { |
| foundField = descField; |
| if (!mayBeMoreThanOne || descField.getTable().equals(getDefaultTable())) { |
| break; |
| } |
| } |
| } |
| } |
| if ((foundField == null) && hasReturningPolicy()) { |
| DatabaseField returnField = getReturningPolicy().getField(field); |
| if ((returnField != null) && (returnField.getType() != null)) { |
| foundField = returnField; |
| } |
| } |
| if (foundField != null) { |
| foundField = foundField.clone(); |
| if (!field.hasTableName()) { |
| foundField.setTableName(""); |
| } |
| } |
| |
| return foundField; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the WrapperPolicy for this descriptor. |
| * This advanced feature can be used to wrap objects with other classes such as CORBA TIE objects or EJBs. |
| */ |
| public WrapperPolicy getWrapperPolicy() { |
| return wrapperPolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Checks if the class has any private owned parts or other dependencies, (i.e. M:M join table). |
| */ |
| public boolean hasDependencyOnParts() { |
| for (DatabaseMapping mapping : getMappings()) { |
| if (mapping.hasDependency()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * returns true if users have designated one or more mappings as IDs. Used |
| * for CMP3Policy primary key class processing. |
| */ |
| public boolean hasDerivedId() { |
| return ! derivesIdMappings.isEmpty(); |
| } |
| |
| /** |
| * INTERNAL: |
| * returns true if a DescriptorEventManager has been set. |
| */ |
| @Override |
| public boolean hasEventManager() { |
| return null != eventManager; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor is involved in inheritance, (is child or parent). |
| * Note: If this class is part of table per class inheritance strategy this |
| * method will return false. |
| * @see #hasTablePerClassPolicy() |
| */ |
| @Override |
| public boolean hasInheritance() { |
| return (inheritancePolicy != null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor is involved in interface, (is child or parent). |
| */ |
| public boolean hasInterfacePolicy() { |
| return (interfacePolicy != null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if descriptor has multiple tables |
| */ |
| public boolean hasMultipleTables() { |
| return (getTables().size() > 1); |
| } |
| |
| /** |
| * INTERNAL: |
| * Calculates whether descriptor references an entity (directly or through a nested mapping). |
| */ |
| public boolean hasNestedIdentityReference(boolean withChildren) { |
| if (withChildren && hasInheritance() && getInheritancePolicy().hasChildren()) { |
| for (ClassDescriptor childDescriptor : getInheritancePolicy().getAllChildDescriptors()) { |
| // leaf children have all the mappings |
| if (!childDescriptor.getInheritancePolicy().hasChildren()) { |
| if (childDescriptor.hasNestedIdentityReference(false)) { |
| return true; |
| } |
| } |
| } |
| } else { |
| for (DatabaseMapping mapping : getMappings()) { |
| if (mapping.hasNestedIdentityReference()) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * @return the hasNoncacheableMappings |
| */ |
| public boolean hasNoncacheableMappings() { |
| return hasNoncacheableMappings; |
| } |
| |
| /** |
| * @return the preDeleteMappings |
| */ |
| public boolean hasPreDeleteMappings() { |
| return preDeleteMappings != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Checks if the class has any private owned parts are not |
| */ |
| public boolean hasPrivatelyOwnedParts() { |
| for (Enumeration<DatabaseMapping> mappings = getMappings().elements(); mappings.hasMoreElements();) { |
| DatabaseMapping mapping = mappings.nextElement(); |
| if (mapping.isPrivateOwned()) { |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Checks to see if it has a query key or mapping with the specified name or not. |
| */ |
| public boolean hasQueryKeyOrMapping(String attributeName) { |
| return (getQueryKeys().containsKey(attributeName) || (getObjectBuilder().getMappingForAttributeName(attributeName) != null)); |
| } |
| |
| /** |
| * INTERNAL: |
| * return whether this descriptor has any relationships through its mappings, through inheritance, or through aggregates |
| */ |
| public boolean hasRelationships() { |
| return hasRelationships; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method returns true if this descriptor has either a ForeignReferenceMapping to |
| * an object aside from the one described by descriptor or more than one ForeignReferenceMapping |
| * to descriptor. (i.e. It determines if there is any mapping aside from a backpointer to a mapping |
| * defined in descriptor) |
| */ |
| public boolean hasRelationshipsExceptBackpointer(ClassDescriptor descriptor){ |
| Iterator<DatabaseMapping> i = mappings.iterator(); |
| boolean foundRelationship = false; |
| while (i.hasNext()){ |
| DatabaseMapping mapping = i.next(); |
| if (mapping.isForeignReferenceMapping()){ |
| ForeignReferenceMapping frMapping = (ForeignReferenceMapping)mapping; |
| if (frMapping.getReferenceDescriptor().equals(descriptor)){ |
| if (foundRelationship){ |
| return true; |
| } else { |
| foundRelationship = true; |
| } |
| } else { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor has Returning policy. |
| */ |
| public boolean hasReturningPolicy() { |
| return (returningPolicy != null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor or descriptors from mappings has Returning policy. |
| */ |
| public boolean hasReturningPolicies() { |
| return (returningPolicies != null); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean hasSerializedObjectPolicy() { |
| return this.serializedObjectPolicy != null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public SerializedObjectPolicy getSerializedObjectPolicy() { |
| return this.serializedObjectPolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setSerializedObjectPolicy(SerializedObjectPolicy serializedObjectPolicy) { |
| this.serializedObjectPolicy = serializedObjectPolicy; |
| if (serializedObjectPolicy != null) { |
| serializedObjectPolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if a wrapper policy is used. |
| */ |
| public boolean hasWrapperPolicy() { |
| return this.wrapperPolicy != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the mappings as a separate step. |
| * This is done as a separate step to ensure that inheritance has been first resolved. |
| */ |
| public void initialize(AbstractSession session) throws DescriptorException { |
| // Avoid repetitive initialization (this does not solve loops) |
| if (isInitialized(INITIALIZED) || isInvalid()) { |
| return; |
| } |
| |
| setInitializationStage(INITIALIZED); |
| |
| // make sure that parent mappings are initialized? |
| if (isChildDescriptor()) { |
| ClassDescriptor parentDescriptor = getInheritancePolicy().getParentDescriptor(); |
| parentDescriptor.initialize(session); |
| getCachePolicy().initializeFromParent(parentDescriptor.getCachePolicy(), this, |
| parentDescriptor, session); |
| // Setup this early before useOptimisticLocking is called so that subclass |
| // versioned by superclass are also covered |
| getInheritancePolicy().initializeOptimisticLocking(); |
| // EL bug 336486 |
| getInheritancePolicy().initializeCacheInvalidationPolicy(); |
| if (parentDescriptor.hasSerializedObjectPolicy()) { |
| if (!hasSerializedObjectPolicy()) { |
| // If SerializedObjectPolicy set on parent descriptor then should be set on children, too |
| setSerializedObjectPolicy(parentDescriptor.getSerializedObjectPolicy().instantiateChild()); |
| } |
| } |
| } |
| |
| // Mappings must be sorted before field are collected in the order of the mapping for indexes to work. |
| // Sorting the mappings to ensure that all DirectToFields get merged before all other mappings |
| // This prevents null key errors when merging maps |
| if (shouldOrderMappings()) { |
| Vector<DatabaseMapping> mappings = getMappings(); |
| DatabaseMapping[] mappingsArray = new DatabaseMapping[mappings.size()]; |
| for (int index = 0; index < mappings.size(); index++) { |
| mappingsArray[index] = mappings.get(index); |
| } |
| Arrays.sort(mappingsArray, new MappingCompare()); |
| mappings = NonSynchronizedVector.newInstance(mappingsArray.length); |
| for (int index = 0; index < mappingsArray.length; index++) { |
| mappings.add(mappingsArray[index]); |
| } |
| setMappings(mappings); |
| } |
| |
| boolean initializeCascadeLocking = (usesOptimisticLocking() && getOptimisticLockingPolicy().isCascaded()) || hasCascadeLockingPolicies(); |
| for (DatabaseMapping mapping : getMappings()) { |
| validateMappingType(mapping); |
| mapping.initialize(session); |
| if (!mapping.isCacheable()){ |
| this.hasNoncacheableMappings = true; |
| } |
| |
| if (mapping.isForeignReferenceMapping()){ |
| if(((ForeignReferenceMapping)mapping).getIndirectionPolicy() instanceof ProxyIndirectionPolicy) { |
| session.getProject().setHasProxyIndirection(true); |
| } |
| ClassDescriptor referencedDescriptor = mapping.getReferenceDescriptor(); |
| if (referencedDescriptor!= null){ |
| referencedDescriptor.referencingClasses.add(this); |
| } |
| } |
| |
| if (mapping.isAggregateObjectMapping()) { |
| ClassDescriptor referencedDescriptor = mapping.getReferenceDescriptor(); |
| if (referencedDescriptor!= null){ |
| referencedDescriptor.referencingClasses.add(this); |
| } |
| } |
| // If this descriptor uses a cascaded version optimistic locking |
| // or has cascade locking policies set then prepare check the |
| // mappings. |
| if (initializeCascadeLocking) { |
| prepareCascadeLockingPolicy(mapping); |
| } |
| |
| // JPA 2.0 Derived identities - build a map of derived id mappings. |
| if (mapping.derivesId()) { |
| this.derivesIdMappings.put(mapping.getAttributeName(), mapping); |
| } |
| |
| // Add all the fields in the mapping to myself. |
| Helper.addAllUniqueToVector(getFields(), mapping.getFields()); |
| } |
| if (initializeCascadeLocking) { |
| this.cascadedLockingInitialized = true; |
| } |
| |
| if (hasMappingsPostCalculateChangesOnDeleted()) { |
| session.getProject().setHasMappingsPostCalculateChangesOnDeleted(true); |
| } |
| |
| // PERF: Don't initialize locking until after fields have been computed so |
| // field is in correct position. |
| if (!isAggregateDescriptor()) { |
| if (!isChildDescriptor()) { |
| // Add write lock field to getFields |
| if (usesOptimisticLocking()) { |
| getOptimisticLockingPolicy().initializeProperties(); |
| } |
| } |
| if (hasSerializedObjectPolicy()) { |
| getSerializedObjectPolicy().initializeField(session); |
| } |
| } |
| |
| // All the query keys should be initialized. |
| for (Iterator<QueryKey> queryKeys = getQueryKeys().values().iterator(); queryKeys.hasNext();) { |
| QueryKey queryKey = queryKeys.next(); |
| queryKey.initialize(this); |
| } |
| |
| if (getPartitioningPolicyName() != null) { |
| PartitioningPolicy policy = session.getProject().getPartitioningPolicy(getPartitioningPolicyName()); |
| if (policy == null) { |
| session.getIntegrityChecker().handleError(DescriptorException.missingPartitioningPolicy(getPartitioningPolicyName(), this, null)); |
| } |
| setPartitioningPolicy(policy); |
| } |
| |
| // If this descriptor has inheritance then it needs to be initialized before all fields is set. |
| if (hasInheritance()) { |
| getInheritancePolicy().initialize(session); |
| if (getInheritancePolicy().isChildDescriptor()) { |
| ClassDescriptor parentDescriptor = getInheritancePolicy().getParentDescriptor(); |
| for (DatabaseMapping mapping : parentDescriptor.getMappings()) { |
| if (mapping.isAggregateObjectMapping() || ((mapping.isForeignReferenceMapping() && (!mapping.isDirectCollectionMapping())) && (!((ForeignReferenceMapping)mapping).usesIndirection()))) { |
| getLockableMappings().add(mapping);// add those mappings from the parent. |
| } |
| // JPA 2.0 Derived identities - build a map of derived id mappings. |
| if (mapping.derivesId()) { |
| this.derivesIdMappings.put(mapping.getAttributeName(), mapping); |
| } |
| } |
| if (parentDescriptor.hasPreDeleteMappings()) { |
| getPreDeleteMappings().addAll(parentDescriptor.getPreDeleteMappings()); |
| } |
| if (parentDescriptor.hasMappingsPostCalculateChanges()) { |
| getMappingsPostCalculateChanges().addAll(parentDescriptor.getMappingsPostCalculateChanges()); |
| } |
| if (parentDescriptor.hasMappingsPostCalculateChangesOnDeleted()) { |
| getMappingsPostCalculateChangesOnDeleted().addAll(parentDescriptor.getMappingsPostCalculateChangesOnDeleted()); |
| } |
| } |
| } |
| |
| // cr 4097 Ensure that the mappings are ordered after the superclasses mappings have been added. |
| // This ensures that the mappings in the child class are ordered correctly |
| // I am sorting the mappings to ensure that all DirectToFields get merged before all other mappings |
| // This prevents null key errors when merging maps |
| // This resort will change the previous sort order, only do it if has inheritance. |
| if (hasInheritance() && shouldOrderMappings()) { |
| Vector<DatabaseMapping> mappings = getMappings(); |
| DatabaseMapping[] mappingsArray = new DatabaseMapping[mappings.size()]; |
| for (int index = 0; index < mappings.size(); index++) { |
| mappingsArray[index] = mappings.get(index); |
| } |
| Arrays.sort(mappingsArray, new MappingCompare()); |
| mappings = NonSynchronizedVector.newInstance(mappingsArray.length); |
| for (int index = 0; index < mappingsArray.length; index++) { |
| mappings.add(mappingsArray[index]); |
| } |
| setMappings(mappings); |
| } |
| |
| // Initialize the allFields to its fields, this can be done now because the fields have been computed. |
| setAllFields((Vector)getFields().clone()); |
| |
| getObjectBuilder().initialize(session); |
| |
| // Initialize the multitenant policy only after the mappings have been |
| // initialized. |
| if (hasMultitenantPolicy()) { |
| getMultitenantPolicy().initialize(session); |
| } |
| |
| if (shouldOrderMappings()) { |
| // PERF: Ensure direct primary key mappings are first. |
| for (int index = getObjectBuilder().getPrimaryKeyMappings().size() - 1; index >= 0; index--) { |
| DatabaseMapping mapping = getObjectBuilder().getPrimaryKeyMappings().get(index); |
| if ((mapping != null) && mapping.isAbstractColumnMapping()) { |
| getMappings().remove(mapping); |
| getMappings().add(0, mapping); |
| DatabaseField field = mapping.getField(); |
| getFields().remove(field); |
| getFields().add(0, field); |
| getAllFields().remove(field); |
| getAllFields().add(0, field); |
| } |
| } |
| } |
| |
| if (usesOptimisticLocking() && (!isChildDescriptor())) { |
| getOptimisticLockingPolicy().initialize(session); |
| } |
| if (hasInterfacePolicy() || isDescriptorForInterface()) { |
| interfaceInitialization(session); |
| } |
| if (hasWrapperPolicy()) { |
| getWrapperPolicy().initialize(session); |
| } |
| if (hasReturningPolicy()) { |
| getReturningPolicy().initialize(session); |
| } |
| if (hasSerializedObjectPolicy()) { |
| getSerializedObjectPolicy().initialize(session); |
| } |
| getQueryManager().initialize(session); |
| getEventManager().initialize(session); |
| getCopyPolicy().initialize(session); |
| getInstantiationPolicy().initialize(session); |
| getCachePolicy().initialize(this, session); |
| |
| if (getHistoryPolicy() != null) { |
| getHistoryPolicy().initialize(session); |
| } else if (hasInheritance()) { |
| // Only one level of inheritance needs to be checked as parent descriptors |
| // are initialized before children are |
| ClassDescriptor parentDescriptor = getInheritancePolicy().getParentDescriptor(); |
| if ((parentDescriptor != null) && (parentDescriptor.getHistoryPolicy() != null)) { |
| setHistoryPolicy((HistoryPolicy)parentDescriptor.getHistoryPolicy().clone()); |
| } |
| } |
| |
| if (getCMPPolicy() != null) { |
| getCMPPolicy().initialize(this, session); |
| } |
| |
| // Validate the fetch group setting during descriptor initialization. |
| if (hasFetchGroupManager()) { |
| getFetchGroupManager().initialize(session); |
| } |
| |
| // By default if change policy is not configured set to attribute change tracking if weaved. |
| if ((getObjectChangePolicyInternal() == null) && (ChangeTracker.class.isAssignableFrom(getJavaClass()))) { |
| // Only auto init if this class "itself" was weaved for change tracking, i.e. not just a superclass. |
| if (Arrays.asList(getJavaClass().getInterfaces()).contains(PersistenceWeavedChangeTracking.class) |
| || (DynamicEntityImpl.class.isAssignableFrom(getJavaClass()))) { |
| // Must double check that this descriptor support change tracking, |
| // when it was weaved it was not initialized, and may now know that it does not support change tracking. |
| if (supportsChangeTracking(session.getProject())) { |
| setObjectChangePolicy(new AttributeChangeTrackingPolicy()); |
| } |
| } |
| } |
| // 3934266 move validation to the policy allowing for this to be done in the sub policies. |
| getObjectChangePolicy().initialize(session, this); |
| |
| // Setup default redirectors. Any redirector that is not set will get assigned the |
| // default redirector. |
| if (this.defaultReadAllQueryRedirector == null){ |
| this.defaultReadAllQueryRedirector = this.defaultQueryRedirector; |
| } |
| if (this.defaultReadObjectQueryRedirector == null){ |
| this.defaultReadObjectQueryRedirector = this.defaultQueryRedirector; |
| } |
| if (this.defaultReportQueryRedirector == null){ |
| this.defaultReportQueryRedirector = this.defaultQueryRedirector; |
| } |
| if (this.defaultInsertObjectQueryRedirector == null){ |
| this.defaultInsertObjectQueryRedirector = this.defaultQueryRedirector; |
| } |
| |
| if (this.defaultUpdateObjectQueryRedirector == null){ |
| this.defaultUpdateObjectQueryRedirector = this.defaultQueryRedirector; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the query manager specific to the descriptor type. |
| */ |
| public void initialize(DescriptorQueryManager queryManager, AbstractSession session) { |
| //PERF: set read-object query to cache generated SQL. |
| if (!queryManager.hasReadObjectQuery()) { |
| // Prepare static read object query always. |
| ReadObjectQuery readObjectQuery = new ReadObjectQuery(); |
| readObjectQuery.setSelectionCriteria(getObjectBuilder().getPrimaryKeyExpression()); |
| queryManager.setReadObjectQuery(readObjectQuery); |
| } |
| queryManager.getReadObjectQuery().setName("read" + getJavaClass().getSimpleName()); |
| |
| if (!queryManager.hasInsertQuery()) { |
| // Prepare insert query always. |
| queryManager.setInsertQuery(new InsertObjectQuery()); |
| } |
| queryManager.getInsertQuery().setModifyRow(getObjectBuilder().buildTemplateInsertRow(session)); |
| |
| if (!usesFieldLocking()) { |
| //do not reset the query if we are using field locking |
| if (!queryManager.hasDeleteQuery()) { |
| // Prepare delete query always. |
| queryManager.setDeleteQuery(new DeleteObjectQuery()); |
| } |
| queryManager.getDeleteQuery().setModifyRow(new DatabaseRecord()); |
| } |
| |
| if (queryManager.hasUpdateQuery()) { |
| // Do not prepare to update by default to allow minimal update. |
| queryManager.getUpdateQuery().setModifyRow(getObjectBuilder().buildTemplateUpdateRow(session)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This initialized method is used exclusively for inheritance. It passes in |
| * true if the child descriptor is isolated. |
| * |
| * This is needed by regular aggregate descriptors (because they require review); |
| * but not by SDK aggregate descriptors. |
| */ |
| public void initializeAggregateInheritancePolicy(AbstractSession session) { |
| ClassDescriptor parentDescriptor = session.getDescriptor(getInheritancePolicy().getParentClass()); |
| parentDescriptor.getInheritancePolicy().addChildDescriptor(this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Rebuild the multiple table primary key map. |
| */ |
| public void initializeMultipleTablePrimaryKeyFields() { |
| int tableSize = getTables().size(); |
| int additionalTablesSize = tableSize - 1; |
| boolean isChild = hasInheritance() && getInheritancePolicy().isChildDescriptor(); |
| if (isChild) { |
| additionalTablesSize = tableSize - getInheritancePolicy().getParentDescriptor().getTables().size(); |
| } |
| if (tableSize <= 1) { |
| return; |
| } |
| ExpressionBuilder builder = new ExpressionBuilder(); |
| Expression joinExpression = getQueryManager().getMultipleTableJoinExpression(); |
| for (int index = 1; index < tableSize; index++) { |
| DatabaseTable table = getTables().get(index); |
| Map<DatabaseField, DatabaseField> oldKeyMapping = getAdditionalTablePrimaryKeyFields().get(table); |
| if (oldKeyMapping != null) { |
| if (!getQueryManager().hasCustomMultipleTableJoinExpression()) { |
| Expression keyJoinExpression = null; |
| // Build the multiple table join expression resulting from the fk relationships. |
| for (Map.Entry<DatabaseField, DatabaseField> entry : oldKeyMapping.entrySet()) { |
| DatabaseField sourceTableField = entry.getKey(); |
| DatabaseField targetTableField = entry.getValue(); |
| // Must add this field to read, so translations work on database row, this could be either. |
| if (!getFields().contains(sourceTableField)) { |
| getFields().add(sourceTableField); |
| } |
| if (!getFields().contains(targetTableField)) { |
| getFields().add(targetTableField); |
| } |
| keyJoinExpression = builder.getField(targetTableField).equal(builder.getField(sourceTableField)).and(keyJoinExpression); |
| } |
| if (keyJoinExpression != null) { |
| joinExpression = keyJoinExpression.and(joinExpression); |
| } |
| getQueryManager().getTablesJoinExpressions().put(table, keyJoinExpression); |
| } |
| } else { |
| // If the user has specified a custom multiple table join then we do not assume that the secondary tables have identically named pk as the primary table. |
| // No additional fk info was specified so assume the pk field(s) are the named the same in the additional table. |
| Map newKeyMapping = new HashMap(getPrimaryKeyFields().size()); |
| getAdditionalTablePrimaryKeyFields().put(table, newKeyMapping); |
| |
| Expression keyJoinExpression = null; |
| // For each primary key field in the primary table, add a pk relationship from the primary table's pk field to the assumed identically named secondary pk field. |
| for (DatabaseField primaryKeyField : getPrimaryKeyFields()) { |
| DatabaseField secondaryKeyField = primaryKeyField.clone(); |
| secondaryKeyField.setTable(table); |
| newKeyMapping.put(primaryKeyField, secondaryKeyField); |
| // Must add this field to read, so translations work on database row. |
| getFields().add(buildField(secondaryKeyField)); |
| |
| if (!getQueryManager().hasCustomMultipleTableJoinExpression()) { |
| keyJoinExpression = builder.getField(secondaryKeyField).equal(builder.getField(primaryKeyField)).and(keyJoinExpression); |
| } |
| } |
| if (keyJoinExpression != null) { |
| joinExpression = keyJoinExpression.and(joinExpression); |
| } |
| getQueryManager().getTablesJoinExpressions().put(table, keyJoinExpression); |
| } |
| } |
| if (joinExpression != null) { |
| getQueryManager().setInternalMultipleTableJoinExpression(joinExpression); |
| } |
| if (getQueryManager().hasCustomMultipleTableJoinExpression()) { |
| Map tablesJoinExpressions = SQLSelectStatement.mapTableToExpression(joinExpression, getTables()); |
| getQueryManager().getTablesJoinExpressions().putAll(tablesJoinExpressions); |
| } |
| if (isChild && (additionalTablesSize > 0)) { |
| for (int index = tableSize - additionalTablesSize; index < tableSize; index++) { |
| DatabaseTable table = getTables().get(index); |
| getInheritancePolicy().addChildTableJoinExpressionToAllParents(table, getQueryManager().getTablesJoinExpressions().get(table)); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the descriptor properties such as write lock and sequencing. |
| */ |
| protected void initializeProperties(AbstractSession session) throws DescriptorException { |
| if (!isAggregateDescriptor()) { |
| if (!isChildDescriptor()) { |
| // Initialize the primary key fields |
| for (int index = 0; index < getPrimaryKeyFields().size(); index++) { |
| DatabaseField primaryKey = getPrimaryKeyFields().get(index); |
| primaryKey = buildField(primaryKey); |
| primaryKey.setPrimaryKey(true); |
| getPrimaryKeyFields().set(index, primaryKey); |
| } |
| List primaryKeyFields = (List)((ArrayList)getPrimaryKeyFields()).clone(); |
| // Remove non-default table primary key (MW used to set these as pk). |
| for (int index = 0; index < primaryKeyFields.size(); index++) { |
| DatabaseField primaryKey = (DatabaseField)primaryKeyFields.get(index); |
| if (!primaryKey.getTable().equals(getDefaultTable())) { |
| getPrimaryKeyFields().remove(primaryKey); |
| } |
| } |
| } |
| |
| // build sequence number field |
| if (getSequenceNumberField() != null) { |
| setSequenceNumberField(buildField(getSequenceNumberField())); |
| } |
| } |
| |
| // Set the local session name for the session broker. |
| setSessionName(session.getName()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Allow the descriptor to initialize any dependencies on this session. |
| */ |
| public void interfaceInitialization(AbstractSession session) throws DescriptorException { |
| if (isInterfaceInitialized(INITIALIZED)) { |
| return; |
| } |
| |
| setInterfaceInitializationStage(INITIALIZED); |
| |
| if (isInterfaceChildDescriptor()) { |
| for (Iterator<Class<?>> interfaces = getInterfacePolicy().getParentInterfaces().iterator(); |
| interfaces.hasNext();) { |
| Class<?> parentInterface = interfaces.next(); |
| ClassDescriptor parentDescriptor = session.getDescriptor(parentInterface); |
| parentDescriptor.interfaceInitialization(session); |
| |
| if (isDescriptorForInterface()) { |
| setQueryKeys(Helper.concatenateMaps(getQueryKeys(), parentDescriptor.getQueryKeys())); |
| } else { |
| //ClassDescriptor is a class, not an interface |
| for (Iterator<String> parentKeys = parentDescriptor.getQueryKeys().keySet().iterator(); |
| parentKeys.hasNext();) { |
| String queryKeyName = parentKeys.next(); |
| if (!hasQueryKeyOrMapping(queryKeyName)) { |
| //the parent descriptor has a query key not defined in the child |
| session.getIntegrityChecker().handleError(DescriptorException.childDoesNotDefineAbstractQueryKeyOfParent(this, parentDescriptor, queryKeyName)); |
| } |
| } |
| } |
| |
| if (parentDescriptor == this) { |
| return; |
| } |
| } |
| } |
| |
| getInterfacePolicy().initialize(session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Convenience method to return true if the java class from this descriptor is abstract. |
| */ |
| public boolean isAbstract() { |
| return java.lang.reflect.Modifier.isAbstract(getJavaClass().getModifiers()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is an aggregate collection descriptor |
| */ |
| public boolean isAggregateCollectionDescriptor() { |
| return this.descriptorType == AGGREGATE_COLLECTION; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is an aggregate descriptor |
| */ |
| public boolean isAggregateDescriptor() { |
| return this.descriptorType == AGGREGATE; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor defines inheritance and is a child. |
| */ |
| public boolean isChildDescriptor() { |
| return hasInheritance() && getInheritancePolicy().isChildDescriptor(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor maps to an EIS or NoSQL datasource. |
| */ |
| public boolean isEISDescriptor() { |
| return false; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor maps to an object-relational structured type. |
| */ |
| public boolean isObjectRelationalDataTypeDescriptor() { |
| return false; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor maps to XML. |
| */ |
| public boolean isXMLDescriptor() { |
| return false; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor maps to a relational table. |
| */ |
| public boolean isRelationalDescriptor() { |
| return false; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the java class is an interface. |
| */ |
| public boolean isDescriptorForInterface() { |
| return this.descriptorType == INTERFACE; |
| } |
| |
| /** |
| * PUBLIC |
| * return true if this descriptor is any type of aggregate descriptor. |
| */ |
| public boolean isDescriptorTypeAggregate(){ |
| return this.descriptorType == AGGREGATE_COLLECTION || this.descriptorType == AGGREGATE; |
| } |
| |
| /** |
| * INTERNAL: |
| * return true if this descriptor is an entity. |
| * (The descriptor may be a mappedSuperclass - only in the internal case during metamodel processing) |
| */ |
| public boolean isDescriptorTypeNormal(){ |
| return this.descriptorType == NORMAL; |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if the descriptor is finished initialization. |
| */ |
| public boolean isFullyInitialized() { |
| return this.initializationStage == POST_INITIALIZED; |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if descriptor is already initialized for the level of initialization. |
| * 1 = pre |
| * 2 = mapping |
| * 3 = post |
| */ |
| protected boolean isInitialized(int initializationStage) { |
| return this.initializationStage >= initializationStage; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the descriptor defines inheritance and is a child. |
| */ |
| public boolean isInterfaceChildDescriptor() { |
| return hasInterfacePolicy() && getInterfacePolicy().isInterfaceChildDescriptor(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if interface descriptor is already initialized for the level of initialization. |
| * 1 = pre |
| * 2 = mapping |
| * 3 = post |
| */ |
| protected boolean isInterfaceInitialized(int interfaceInitializationStage) { |
| return this.interfaceInitializationStage >= interfaceInitializationStage; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if an error occurred during initialization which should abort any further initialization. |
| */ |
| public boolean isInvalid() { |
| return this.initializationStage == ERROR; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns true if the descriptor represents an isolated class |
| */ |
| public boolean isIsolated() { |
| return getCachePolicy().isIsolated(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns true if the descriptor represents an isolated class |
| */ |
| public boolean isProtectedIsolation() { |
| return getCachePolicy().isProtectedIsolation(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns true if the descriptor represents an isolated class |
| */ |
| public boolean isSharedIsolation() { |
| return getCachePolicy().isSharedIsolation(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor has more than one table. |
| */ |
| public boolean isMultipleTableDescriptor() { |
| return getTables().size() > 1; |
| } |
| |
| /** |
| * INTERNAL: |
| * Indicates whether pk or some of its components |
| * set after insert into the database. |
| * Shouldn't be called before ClassDescriptor has been initialized. |
| */ |
| public boolean isPrimaryKeySetAfterInsert(AbstractSession session) { |
| return (usesSequenceNumbers() && getSequence().shouldAcquireValueAfterInsert()) || (hasReturningPolicy() && getReturningPolicy().isUsedToSetPrimaryKey()); |
| } |
| |
| /** |
| * ADVANCED: |
| * When set to false, this setting will allow the UOW to avoid locking the shared cache instance in order to perform a clone. |
| * Caution should be taken as setting this to false may allow cloning of partial updates |
| */ |
| public boolean shouldLockForClone() { |
| return this.shouldLockForClone; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if change sets are required for new objects. |
| */ |
| public boolean shouldUseFullChangeSetsForNewObjects() { |
| return getCachePolicy().getCacheSynchronizationType() == CachePolicy.SEND_NEW_OBJECTS_WITH_CHANGES || shouldUseFullChangeSetsForNewObjects; |
| } |
| |
| /** |
| * PUBLIC: |
| * This method is the equivalent of calling {@link #setShouldOnlyRefreshCacheIfNewerVersion} with an argument of <CODE>true</CODE>: |
| * it configures a <CODE>ClassDescriptor</CODE> to only refresh the cache if the data received from the database by a query is newer than |
| * the data in the cache (as determined by the optimistic locking field) and as long as one of the following is true: |
| * |
| * <UL> |
| * <LI>the <CODE>ClassDescriptor</CODE> was configured by calling {@link #alwaysRefreshCache} or {@link #alwaysRefreshCacheOnRemote},</LI> |
| * <LI>the query was configured by calling {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}, or</LI> |
| * <LI>the query was a call to {@link org.eclipse.persistence.sessions.Session#refreshObject}</LI> |
| * </UL> |
| * <P> |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. For example, by default, |
| * when a query for a single object based on its primary key is executed, EclipseLink will first look in the cache for the object. |
| * If the object is in the cache, the cached object is returned and data is not refreshed. To avoid cache hits, use |
| * the {@link #disableCacheHits} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects. |
| * |
| * @see #dontOnlyRefreshCacheIfNewerVersion |
| */ |
| public void onlyRefreshCacheIfNewerVersion() { |
| setShouldOnlyRefreshCacheIfNewerVersion(true); |
| } |
| |
| /** |
| * INTERNAL: |
| * Post initializations after mappings are initialized. |
| */ |
| public void postInitialize(AbstractSession session) throws DescriptorException { |
| // These cached settings on the project must be set even if descriptor is initialized. |
| if (getHistoryPolicy() != null) { |
| session.getProject().setHasGenericHistorySupport(true); |
| } |
| |
| // Avoid repetitive initialization (this does not solve loops) |
| if (isInitialized(POST_INITIALIZED) || isInvalid()) { |
| return; |
| } |
| |
| setInitializationStage(POST_INITIALIZED); |
| |
| // Make sure that child is post initialized, |
| // this initialize bottom up, unlike the two other phases that to top down. |
| if (hasInheritance()) { |
| for (ClassDescriptor child : getInheritancePolicy().getChildDescriptors()) { |
| child.postInitialize(session); |
| } |
| } |
| |
| // Allow mapping to perform post initialization. |
| for (DatabaseMapping mapping : getMappings()) { |
| // This causes post init to be called multiple times in inheritance. |
| mapping.postInitialize(session); |
| // PERF: computed if deferred locking is required. |
| if (!shouldAcquireCascadedLocks()) { |
| if (mapping.isForeignReferenceMapping()){ |
| if (!((ForeignReferenceMapping)mapping).usesIndirection()){ |
| setShouldAcquireCascadedLocks(true); |
| } |
| hasRelationships = true; |
| } |
| if ((mapping instanceof AggregateObjectMapping)){ |
| if (mapping.getReferenceDescriptor().shouldAcquireCascadedLocks()) { |
| setShouldAcquireCascadedLocks(true); |
| } |
| if (mapping.getReferenceDescriptor().hasRelationships()){ |
| hasRelationships = true; |
| } |
| } |
| } |
| if (getCachePolicy().isProtectedIsolation() && |
| ((mapping.isForeignReferenceMapping() && !mapping.isCacheable()) |
| || (mapping.isAggregateObjectMapping() && mapping.getReferenceDescriptor().hasNoncacheableMappings()))) { |
| mapping.collectQueryParameters(this.foreignKeyValuesForCaching); |
| } |
| if (mapping.isLockableMapping()){ |
| getLockableMappings().add(mapping); |
| } |
| |
| |
| } |
| |
| if (hasInheritance()) { |
| getInheritancePolicy().postInitialize(session); |
| } |
| |
| //PERF: Ensure that the identical primary key fields are used to avoid equals. |
| for (int index = (getPrimaryKeyFields().size() - 1); index >= 0; index--) { |
| DatabaseField primaryKeyField = getPrimaryKeyFields().get(index); |
| int fieldIndex = getFields().indexOf(primaryKeyField); |
| |
| // Aggregate/agg-collections may not have a mapping for pk field. |
| if (fieldIndex != -1) { |
| primaryKeyField = getFields().get(fieldIndex); |
| getPrimaryKeyFields().set(index, primaryKeyField); |
| primaryKeyField.setPrimaryKey(true); |
| } |
| } |
| |
| // List of fields selected by a query that uses SOP when descriptor has SOP. Used to index these fields. |
| List<DatabaseField> sopSelectionFields = null; |
| if (hasSerializedObjectPolicy()) { |
| getSerializedObjectPolicy().postInitialize(session); |
| this.selectionFields = (List<DatabaseField>)getFields().clone(); |
| this.selectionFields.remove(getSerializedObjectPolicy().getField()); |
| this.allSelectionFields = (List<DatabaseField>)getAllFields().clone(); |
| this.allSelectionFields.remove(getSerializedObjectPolicy().getField()); |
| sopSelectionFields = getSerializedObjectPolicy().getSelectionFields(); |
| if (sopSelectionFields.size() == getFields().size()) { |
| // no need for sop field indexes - SOP uses all the field in the descriptor |
| sopSelectionFields = null; |
| } |
| } else { |
| this.selectionFields = getFields(); |
| this.allSelectionFields = getAllFields(); |
| } |
| |
| // Index and classify fields and primary key. |
| // This is in post because it needs field classification defined in initializeMapping |
| // this can come through a 1:1 so requires all descriptors to be initialized (mappings). |
| // May 02, 2000 - Jon D. |
| for (int index = 0; index < getFields().size(); index++) { |
| DatabaseField field = getFields().elementAt(index); |
| if (field.getType() == null){ |
| DatabaseMapping mapping = getObjectBuilder().getMappingForField(field); |
| if (mapping != null) { |
| field.setType(mapping.getFieldClassification(field)); |
| } |
| } |
| // LOB may require lob writer which needs full row to fetch LOB. |
| if ((field.getType() == ClassConstants.BLOB) || (field.getType() == ClassConstants.CLOB)) { |
| setHasMultipleTableConstraintDependecy(true); |
| } |
| field.setIndex(index); |
| if (sopSelectionFields != null) { |
| int sopFieldIndex = sopSelectionFields.indexOf(field); |
| if (sopFieldIndex != -1) { |
| field.setIndex(sopFieldIndex); |
| } |
| } |
| } |
| |
| // EL Bug 426500 - When a mapping has built its selection criteria early with partially |
| // initialized fields, post-initialize any source and target Expression fields. |
| for (DatabaseMapping mapping : getMappings()) { |
| mapping.postInitializeSourceAndTargetExpressions(); |
| } |
| |
| // Set cache key type. |
| if (getCachePolicy().getCacheKeyType() == null || (getCachePolicy().getCacheKeyType() == CacheKeyType.AUTO)) { |
| if ((getPrimaryKeyFields().size() > 1) || getObjectBuilder().isXMLObjectBuilder()) { |
| setCacheKeyType(CacheKeyType.CACHE_ID); |
| } else if ((getPrimaryKeyFields().size() == 1) && (getObjectBuilder().getPrimaryKeyClassifications().size() == 1)) { |
| Class type = getObjectBuilder().getPrimaryKeyClassifications().get(0); |
| if ((type == null) || type.isArray()) { |
| getCachePolicy().setCacheKeyType(CacheKeyType.CACHE_ID); |
| } else { |
| getCachePolicy().setCacheKeyType(CacheKeyType.ID_VALUE); |
| } |
| } else { |
| getCachePolicy().setCacheKeyType(CacheKeyType.CACHE_ID); |
| } |
| } else if ((getCachePolicy().getCacheKeyType() == CacheKeyType.ID_VALUE) && (getPrimaryKeyFields().size() > 1)) { |
| session.getIntegrityChecker().handleError(DescriptorException.cannotUseIdValueForCompositeId(this)); |
| } |
| if (hasFetchGroupManager()) { |
| getFetchGroupManager().postInitialize(session); |
| } |
| getObjectBuilder().postInitialize(session); |
| getQueryManager().postInitialize(session); |
| |
| // Post initialize the multitenant policy after the query manager. |
| if (hasMultitenantPolicy()) { |
| getMultitenantPolicy().postInitialize(session); |
| } |
| |
| getCachePolicy().postInitialize(this, session); |
| |
| postInitializeReturningPolicies(); |
| |
| validateAfterInitialization(session); |
| |
| checkDatabase(session); |
| } |
| |
| private void postInitializeReturningPolicies() { |
| //Initialize ReturningPolicies |
| List<ReturningPolicy> returningPolicies = new ArrayList<>(); |
| if (this.hasReturningPolicy()) { |
| returningPolicies.add(this.getReturningPolicy()); |
| } |
| browseReturningPolicies(returningPolicies, this.getMappings()); |
| if (returningPolicies.size() > 0) { |
| this.returningPolicies = returningPolicies; |
| prepareReturnFields(returningPolicies); |
| } |
| } |
| |
| private void browseReturningPolicies(List<ReturningPolicy> returningPolicies, Vector<DatabaseMapping> mappings) { |
| for (DatabaseMapping databaseMapping :mappings) { |
| if (databaseMapping instanceof AggregateObjectMapping) { |
| ClassDescriptor referenceDescriptor = databaseMapping.getReferenceDescriptor(); |
| if (referenceDescriptor != null) { |
| browseReturningPolicies(returningPolicies, referenceDescriptor.getMappings()); |
| if (referenceDescriptor.hasReturningPolicy()) { |
| returningPolicies.add(referenceDescriptor.getReturningPolicy()); |
| } |
| } |
| } |
| } |
| } |
| |
| private void prepareReturnFields(List<ReturningPolicy> returningPolicies) { |
| Vector<DatabaseField> returnFieldsInsert = new NonSynchronizedVector(); |
| Vector<DatabaseField> returnFieldsUpdate = new NonSynchronizedVector(); |
| List<DatabaseField> returnFieldsToMergeInsert = new ArrayList<>(); |
| List<DatabaseField> returnFieldsToMergeUpdate = new ArrayList<>(); |
| Collection tmpFields; |
| for (ReturningPolicy returningPolicy: returningPolicies) { |
| tmpFields = returningPolicy.getFieldsToGenerateInsert(this.defaultTable); |
| if (tmpFields != null) { |
| returnFieldsInsert.addAll(tmpFields); |
| } |
| tmpFields = returningPolicy.getFieldsToGenerateUpdate(this.defaultTable); |
| if (tmpFields != null) { |
| returnFieldsUpdate.addAll(tmpFields); |
| } |
| tmpFields = returningPolicy.getFieldsToMergeInsert(); |
| if (tmpFields != null) { |
| returnFieldsToMergeInsert.addAll(tmpFields); |
| } |
| tmpFields = returningPolicy.getFieldsToMergeUpdate(); |
| if (tmpFields != null) { |
| returnFieldsToMergeUpdate.addAll(tmpFields); |
| } |
| } |
| this.returnFieldsToGenerateInsert = (returnFieldsInsert.isEmpty()) ? null : returnFieldsInsert; |
| this.returnFieldsToGenerateUpdate = (returnFieldsUpdate.isEmpty()) ? null : returnFieldsUpdate; |
| this.returnFieldsToMergeInsert = (returnFieldsToMergeInsert.isEmpty()) ? null : returnFieldsToMergeInsert; |
| this.returnFieldsToMergeUpdate = (returnFieldsToMergeUpdate.isEmpty()) ? null : returnFieldsToMergeUpdate; |
| } |
| |
| /** |
| * INTERNAL: |
| * Configure all descriptors referencing this class to be protected and update their cache settings. |
| */ |
| public void notifyReferencingDescriptorsOfIsolation(AbstractSession session) { |
| for (ClassDescriptor descriptor : this.referencingClasses){ |
| if (descriptor.getCachePolicy().getCacheIsolation() == null || descriptor.getCachePolicy().getCacheIsolation() == CacheIsolationType.SHARED) { |
| descriptor.getCachePolicy().setCacheIsolation(CacheIsolationType.PROTECTED); |
| descriptor.getCachePolicy().postInitialize(descriptor, session); |
| for (DatabaseMapping mapping : descriptor.getMappings()) { |
| if (mapping.isAggregateMapping() && (mapping.getReferenceDescriptor() != null)) { |
| mapping.getReferenceDescriptor().getCachePolicy().setCacheIsolation(CacheIsolationType.PROTECTED); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Allow the descriptor to initialize any dependencies on this session. |
| */ |
| public void preInitialize(AbstractSession session) throws DescriptorException { |
| // Avoid repetitive initialization (this does not solve loops) |
| if (isInitialized(PREINITIALIZED)) { |
| return; |
| } |
| setInitializationStage(PREINITIALIZED); |
| |
| assignDefaultValues(session); |
| |
| if (this.isCascadeOnDeleteSetOnDatabaseOnSecondaryTables && !session.getPlatform().supportsDeleteOnCascade()) { |
| this.isCascadeOnDeleteSetOnDatabaseOnSecondaryTables = false; |
| } |
| // Set the fetchgroup manager is the class implements the tracking interface. |
| if (FetchGroupTracker.class.isAssignableFrom(getJavaClass())) { |
| if (getFetchGroupManager() == null && !isAggregateDescriptor()) { |
| //aggregate descriptors will set fetchgroupmanager during mapping init. |
| setFetchGroupManager(new FetchGroupManager()); |
| } |
| } |
| // PERF: Check if the class "itself" was weaved. |
| // If weaved avoid reflection, use clone copy and empty new. |
| if (Arrays.asList(getJavaClass().getInterfaces()).contains(PersistenceObject.class)) { |
| // Cloning is only auto set for field access, as method access |
| // may not have simple fields, same with empty new and reflection get/set. |
| boolean isMethodAccess = false; |
| for (Iterator<DatabaseMapping> iterator = getMappings().iterator(); iterator.hasNext(); ) { |
| DatabaseMapping mapping = iterator.next(); |
| if (mapping.isUsingMethodAccess()) { |
| // Ok for lazy 1-1s |
| if (!mapping.isOneToOneMapping() || !((ForeignReferenceMapping)mapping).usesIndirection()) { |
| isMethodAccess = true; |
| } |
| } else if (!mapping.isWriteOnly()) { |
| // Avoid reflection. |
| mapping.setAttributeAccessor(new PersistenceObjectAttributeAccessor(mapping.getAttributeName())); |
| } |
| } |
| if (!isMethodAccess) { |
| if (this.copyPolicy == null) { |
| setCopyPolicy(new PersistenceEntityCopyPolicy()); |
| } |
| if (!isAbstract()) { |
| try { |
| if (this.instantiationPolicy == null) { |
| setInstantiationPolicy(new PersistenceObjectInstantiationPolicy((PersistenceObject)getJavaClass().getConstructor().newInstance())); |
| } |
| } catch (Exception ignore) { } |
| } |
| } |
| } |
| |
| // 4924665 Check for spaces in table names, and add the appropriate quote character |
| Iterator<DatabaseTable> tables = this.getTables().iterator(); |
| while(tables.hasNext()) { |
| DatabaseTable next = tables.next(); |
| if(next.getName().indexOf(' ') != -1) { |
| // EL Bug 382420 - set use delimiters to true if table name contains a space |
| next.setUseDelimiters(true); |
| } |
| } |
| |
| // Allow mapping pre init, must be done before validate. |
| for (DatabaseMapping mapping : getMappings()) { |
| try { |
| mapping.preInitialize(session); |
| } catch (DescriptorException exception) { |
| session.getIntegrityChecker().handleError(exception); |
| } |
| } |
| |
| validateBeforeInitialization(session); |
| |
| preInitializeInheritancePolicy(session); |
| |
| // Make sure that parent is already preinitialized |
| if (hasInheritance()) { |
| // The default table will be set in this call once the duplicate |
| // tables have been removed. |
| getInheritancePolicy().preInitialize(session); |
| } else { |
| // This must be done now, after validate, before init anything else. |
| setInternalDefaultTable(); |
| } |
| |
| // Once the table and mapping information has been settled, we'll need |
| // to set tenant id fields on the descriptor for each table. These are |
| // at least used for DDL generation. Doesn't seem to interfere or |
| // duplicate anything else we have done to support tenant id fields. |
| if (hasMultitenantPolicy()) { |
| getMultitenantPolicy().preInitialize(session); |
| } |
| |
| verifyTableQualifiers(session.getDatasourcePlatform()); |
| initializeProperties(session); |
| if (!isAggregateDescriptor()) { |
| // Adjust before you initialize ... |
| adjustMultipleTableInsertOrder(); |
| initializeMultipleTablePrimaryKeyFields(); |
| } |
| |
| if (hasInterfacePolicy()) { |
| preInterfaceInitialization(session); |
| } |
| |
| getQueryManager().preInitialize(session); |
| |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void prepareCascadeLockingPolicy(DatabaseMapping mapping) { |
| if (mapping.isPrivateOwned() && mapping.isForeignReferenceMapping()) { |
| if (mapping.isCascadedLockingSupported()) { |
| // Even if the mapping says it is supported in general, there |
| // may be conditions where it is not. Need the following checks. |
| if (((ForeignReferenceMapping)mapping).hasCustomSelectionQuery()) { |
| throw ValidationException.unsupportedCascadeLockingMappingWithCustomQuery(mapping); |
| } else if (isDescriptorTypeAggregate()) { |
| throw ValidationException.unsupportedCascadeLockingDescriptor(this); |
| } else { |
| mapping.prepareCascadeLockingPolicy(); |
| } |
| } else { |
| throw ValidationException.unsupportedCascadeLockingMapping(mapping); |
| } |
| } |
| } |
| |
| /** |
| * Hook together the inheritance policy tree. |
| */ |
| protected void preInitializeInheritancePolicy(AbstractSession session) throws DescriptorException { |
| if (isChildDescriptor() && (requiresInitialization(session))) { |
| if (getInheritancePolicy().getParentClass().equals(getJavaClass())) { |
| setInterfaceInitializationStage(ERROR); |
| throw DescriptorException.parentClassIsSelf(this); |
| } |
| ClassDescriptor parentDescriptor = session.getDescriptor(getInheritancePolicy().getParentClass()); |
| parentDescriptor.getInheritancePolicy().addChildDescriptor(this); |
| getInheritancePolicy().setParentDescriptor(parentDescriptor); |
| parentDescriptor.preInitialize(session); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Allow the descriptor to initialize any dependencies on this session. |
| */ |
| public void preInterfaceInitialization(AbstractSession session) throws DescriptorException { |
| if (isInterfaceInitialized(PREINITIALIZED)) { |
| return; |
| } |
| |
| setInterfaceInitializationStage(PREINITIALIZED); |
| |
| assignDefaultValues(session); |
| |
| if (isInterfaceChildDescriptor()) { |
| for (Iterator<Class<?>> interfaces = getInterfacePolicy().getParentInterfaces().iterator(); |
| interfaces.hasNext();) { |
| Class<?> parentInterface = interfaces.next(); |
| ClassDescriptor parentDescriptor = session.getDescriptor(parentInterface); |
| if ((parentDescriptor == null) || (parentDescriptor.getJavaClass() == getJavaClass()) || parentDescriptor.getInterfacePolicy().usesImplementorDescriptor()) { |
| session.getProject().getDescriptors().put(parentInterface, this); |
| session.clearLastDescriptorAccessed(); |
| } else if (!parentDescriptor.isDescriptorForInterface()) { |
| throw DescriptorException.descriptorForInterfaceIsMissing(parentInterface.getName()); |
| } else { |
| parentDescriptor.preInterfaceInitialization(session); |
| parentDescriptor.getInterfacePolicy().addChildDescriptor(this); |
| getInterfacePolicy().addParentDescriptor(parentDescriptor); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Rehash any hashtables based on fields. |
| * This is used to clone descriptors for aggregates, which hammer field names, |
| * it is probably better not to hammer the field name and this should be refactored. |
| */ |
| public void rehashFieldDependancies(AbstractSession session) { |
| getObjectBuilder().rehashFieldDependancies(session); |
| |
| for (Enumeration<DatabaseMapping> enumtr = getMappings().elements(); enumtr.hasMoreElements();) { |
| enumtr.nextElement().rehashFieldDependancies(session); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * A user should not be setting which attributes to join or not to join |
| * after descriptor initialization; provided only for backwards compatibility. |
| */ |
| public void reInitializeJoinedAttributes() { |
| if (!isInitialized(POST_INITIALIZED)) { |
| // wait until the descriptor gets initialized first |
| return; |
| } |
| getObjectBuilder().initializeJoinedAttributes(); |
| if (hasInheritance()) { |
| for (ClassDescriptor child : getInheritancePolicy().getChildDescriptors()) { |
| child.reInitializeJoinedAttributes(); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to initialize a remote descriptor. |
| */ |
| public void remoteInitialization(DistributedSession session) { |
| // These cached settings on the project must be set even if descriptor is initialized. |
| if (getHistoryPolicy() != null) { |
| session.getProject().setHasGenericHistorySupport(true); |
| } |
| |
| // Record that there is an isolated class in the project. |
| if (!getCachePolicy().isSharedIsolation()) { |
| session.getProject().setHasIsolatedClasses(true); |
| } |
| if (!getCachePolicy().shouldIsolateObjectsInUnitOfWork() && !shouldBeReadOnly()) { |
| session.getProject().setHasNonIsolatedUOWClasses(true); |
| } |
| |
| for (DatabaseMapping mapping : getMappings()) { |
| mapping.remoteInitialization(session); |
| } |
| |
| getEventManager().remoteInitialization(session); |
| getInstantiationPolicy().initialize(session); |
| getCopyPolicy().initialize(session); |
| |
| if (hasInheritance()) { |
| getInheritancePolicy().remoteInitialization(session); |
| } |
| |
| if (getCMPPolicy() != null) { |
| getCMPPolicy().remoteInitialize(this, session); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Remove the user defined property. |
| */ |
| public void removeProperty(String property) { |
| getProperties().remove(property); |
| } |
| |
| /** |
| * INTERNAL: |
| * Aggregate and Interface descriptors do not require initialization as they are cloned and |
| * initialized by each mapping. Descriptors with table per tenant policies are cloned per |
| * client session (per tenant) so do not initialize the original descriptor. |
| */ |
| public boolean requiresInitialization(AbstractSession session) { |
| // If we are an aggregate or interface descriptor we do not require initialization. |
| if (isDescriptorTypeAggregate() || isDescriptorForInterface()) { |
| return false; |
| } |
| |
| // If we have a table per tenant policy then check for our tenant |
| // context property. If it is available from the session, set it and |
| // return true to initialize. Otherwise do not initialize the |
| // descriptor (it will be initialized per client session). |
| if (hasTablePerMultitenantPolicy()) { |
| return ((TablePerMultitenantPolicy) getMultitenantPolicy()).shouldInitialize(session); |
| } |
| |
| // By default it should be initialized. |
| return true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Validate that the descriptor was defined correctly. |
| * This allows for checks to be done that require the descriptor initialization to be completed. |
| */ |
| protected void selfValidationAfterInitialization(AbstractSession session) throws DescriptorException { |
| // This has to be done after, because read subclasses must be initialized. |
| if ( (hasInheritance() && (getInheritancePolicy().shouldReadSubclasses() || isAbstract())) || hasTablePerClassPolicy() && isAbstract() ) { |
| // Avoid building a new instance if the inheritance class is abstract. |
| // There is an empty statement here, and this was done if anything for the |
| // readability sake of the statement logic. |
| } else if (session.getIntegrityChecker().shouldCheckInstantiationPolicy()) { |
| getInstantiationPolicy().buildNewInstance(); |
| } |
| |
| if (hasReturningPolicy()) { |
| getReturningPolicy().validationAfterDescriptorInitialization(session); |
| } |
| getObjectBuilder().validate(session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Validate that the descriptor's non-mapping attribute are defined correctly. |
| */ |
| protected void selfValidationBeforeInitialization(AbstractSession session) throws DescriptorException { |
| if (isChildDescriptor()) { |
| ClassDescriptor parentDescriptor = session.getDescriptor(getInheritancePolicy().getParentClass()); |
| |
| if (parentDescriptor == null) { |
| session.getIntegrityChecker().handleError(DescriptorException.parentDescriptorNotSpecified(getInheritancePolicy().getParentClass().getName(), this)); |
| } |
| } else { |
| if (getTables().isEmpty() && (!isAggregateDescriptor())) { |
| session.getIntegrityChecker().handleError(DescriptorException.tableNotSpecified(this)); |
| } |
| } |
| |
| if (!isChildDescriptor() && !isDescriptorTypeAggregate()) { |
| if (getPrimaryKeyFieldNames().isEmpty()) { |
| session.getIntegrityChecker().handleError(DescriptorException.primaryKeyFieldsNotSepcified(this)); |
| } |
| } |
| |
| if ((getIdentityMapClass() == ClassConstants.NoIdentityMap_Class) && (getQueryManager().getDoesExistQuery().shouldCheckCacheForDoesExist())) { |
| session.getIntegrityChecker().handleError(DescriptorException.identityMapNotSpecified(this)); |
| } |
| |
| if (((getSequenceNumberName() != null) && (getSequenceNumberField() == null)) || ((getSequenceNumberName() == null) && (getSequenceNumberField() != null))) { |
| session.getIntegrityChecker().handleError(DescriptorException.sequenceNumberPropertyNotSpecified(this)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This is used to map the primary key field names in a multiple table |
| * descriptor. |
| */ |
| protected void setAdditionalTablePrimaryKeyFields(DatabaseTable table, DatabaseField field1, DatabaseField field2) { |
| Map<DatabaseField, DatabaseField> tableAdditionalPKFields = getAdditionalTablePrimaryKeyFields().get(table); |
| |
| if (tableAdditionalPKFields == null) { |
| tableAdditionalPKFields = new HashMap(2); |
| getAdditionalTablePrimaryKeyFields().put(table, tableAdditionalPKFields); |
| } |
| |
| tableAdditionalPKFields.put(field1, field2); |
| } |
| |
| /** |
| * INTERNAL: |
| * Eclipselink needs additionalTablePKFields entries to be associated with tables other than the main (getTables.get(0)) one. |
| * Also in case of two non-main tables additionalTablePKFields entry should be associated with the one |
| * father down insert order. |
| */ |
| protected void toggleAdditionalTablePrimaryKeyFields() { |
| if(additionalTablePrimaryKeyFields == null) { |
| // nothing to do |
| return; |
| } |
| |
| // nProcessedTables is a number of tables (first in egtTables() order) that don't require toggle - to, but may be toggled - from |
| // (meaning by "toggle - to" table: setAdditionalTablePrimaryKeyFields(table, .., ..);) |
| // "Processed" tables always include the main table (getTables().get(0)) plus all the inherited tables. |
| // Don't toggle between processed tables (that has been already done by the parent); |
| // always toggle from processed to non-processed; |
| // toggle between two non-processed to the one that is father down insert order. |
| int nProcessedTables = 1; |
| if (isChildDescriptor()) { |
| nProcessedTables = getInheritancePolicy().getParentDescriptor().getTables().size(); |
| // if this is multiple table inheritance, we should include the table for this child in the processed tables |
| if (getTables().size() > nProcessedTables){ |
| nProcessedTables++; |
| } |
| } |
| |
| // cache the original map in a new variable |
| Map<DatabaseTable, Map<DatabaseField, DatabaseField>> additionalTablePrimaryKeyFieldsOld = additionalTablePrimaryKeyFields; |
| // nullify the original map variable - it will be re-created from scratch |
| additionalTablePrimaryKeyFields = null; |
| Iterator<Map.Entry<DatabaseTable, Map<DatabaseField, DatabaseField>>> itTable = additionalTablePrimaryKeyFieldsOld.entrySet().iterator(); |
| // loop through the cached original map and add all its entries (either toggled or unchanged) to the re-created map |
| while(itTable.hasNext()) { |
| Map.Entry<DatabaseTable, Map<DatabaseField, DatabaseField>> entryTable = itTable.next(); |
| DatabaseTable sourceTable = entryTable.getKey(); |
| boolean isSourceProcessed = getTables().indexOf(sourceTable) < nProcessedTables; |
| int sourceInsertOrderIndex = getMultipleTableInsertOrder().indexOf(sourceTable); |
| Map<DatabaseField, DatabaseField> targetTableAdditionalPKFields = entryTable.getValue(); |
| Iterator<Map.Entry<DatabaseField, DatabaseField>> itField = targetTableAdditionalPKFields.entrySet().iterator(); |
| while(itField.hasNext()) { |
| Map.Entry<DatabaseField, DatabaseField> entryField = itField.next(); |
| DatabaseField targetField = entryField.getKey(); |
| DatabaseField sourceField = entryField.getValue(); |
| DatabaseTable targetTable = targetField.getTable(); |
| boolean isTargetProcessed = getTables().indexOf(targetTable) < nProcessedTables; |
| int targetInsertOrderIndex = getMultipleTableInsertOrder().indexOf(targetTable); |
| // add the entry to the map |
| if(!isTargetProcessed && (isSourceProcessed || (sourceInsertOrderIndex > targetInsertOrderIndex))) { |
| // source and target toggled |
| setAdditionalTablePrimaryKeyFields(targetTable, sourceField, targetField); |
| } else { |
| // exactly the same as in the original map |
| setAdditionalTablePrimaryKeyFields(sourceTable, targetField, sourceField); |
| } |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This is used to map the primary key field names in a multiple table |
| * descriptor. |
| */ |
| public void setAdditionalTablePrimaryKeyFields(Map<DatabaseTable, Map<DatabaseField, DatabaseField>> additionalTablePrimaryKeyFields) { |
| this.additionalTablePrimaryKeyFields = additionalTablePrimaryKeyFields; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the alias |
| */ |
| public void setAlias(String alias) { |
| this.alias = alias; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set all the fields. |
| */ |
| protected void setAllFields(Vector<DatabaseField> allFields) { |
| this.allFields = allFields; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the amendment class. |
| * The amendment method will be called on the class before initialization to allow for it to initialize the descriptor. |
| * The method must be a public static method on the class. |
| */ |
| public void setAmendmentClass(Class amendmentClass) { |
| this.amendmentClass = amendmentClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the amendment class name, used by the MW. |
| */ |
| public void setAmendmentClassName(String amendmentClassName) { |
| this.amendmentClassName = amendmentClassName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the amendment method. |
| * This will be called on the amendment class before initialization to allow for it to initialize the descriptor. |
| * The method must be a public static method on the class. |
| */ |
| public void setAmendmentMethodName(String amendmentMethodName) { |
| this.amendmentMethodName = amendmentMethodName; |
| } |
| |
| /** |
| * @param accessorTree the accessorTree to set |
| */ |
| public void setAccessorTree(List<AttributeAccessor> accessorTree) { |
| this.accessorTree = accessorTree; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the type of cache synchronization that will be used on objects of this type. Possible values |
| * are: |
| * SEND_OBJECT_CHANGES |
| * INVALIDATE_CHANGED_OBJECTS |
| * SEND_NEW_OBJECTS_WITH_CHANGES |
| * DO_NOT_SEND_CHANGES |
| * Note: Cache Synchronization type cannot be altered for descriptors that are set as isolated using |
| * the setIsIsolated method. |
| * @param type int The synchronization type for this descriptor |
| * |
| */ |
| public void setCacheSynchronizationType(int type) { |
| getCachePolicy().setCacheSynchronizationType(type); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the ObjectChangePolicy for this descriptor. |
| */ |
| public void setObjectChangePolicy(ObjectChangePolicy policy) { |
| this.changePolicy = policy; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the HistoryPolicy for this descriptor. |
| */ |
| public void setHistoryPolicy(HistoryPolicy policy) { |
| this.historyPolicy = policy; |
| if (policy != null) { |
| policy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction |
| * between EclipseLink and the internal cache for that class will pass through the Interceptor. |
| * Advanced users could use this interceptor to audit, profile or log cache access. This Interceptor |
| * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism. |
| * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor. |
| |
| * As with IdentityMaps an entire class inheritance hierarchy will share the same interceptor. |
| * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor |
| */ |
| public void setCacheInterceptorClass(Class cacheInterceptorClass) { |
| getCachePolicy().setCacheInterceptorClass(cacheInterceptorClass); |
| } |
| |
| /** |
| * PUBLIC: |
| * A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction |
| * between EclipseLink and the internal cache for that class will pass through the Interceptor. |
| * Advanced users could use this interceptor to audit, profile or log cache access. This Interceptor |
| * could also be used to redirect or augment the TopLink cache with an alternate cache mechanism. |
| * EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor. |
| |
| * As with IdentityMaps an entire class inheritance hierarchy will share the same interceptor. |
| * @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor |
| */ |
| public void setCacheInterceptorClassName(String cacheInterceptorClassName) { |
| getCachePolicy().setCacheInterceptorClassName(cacheInterceptorClassName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the Cache Invalidation Policy for this descriptor. |
| * @see org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy |
| */ |
| public void setCacheInvalidationPolicy(CacheInvalidationPolicy policy) { |
| cacheInvalidationPolicy = policy; |
| } |
| |
| /** |
| * ADVANCED: |
| * automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings. |
| * In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint, |
| * this defines that this descriptor has a foreign key constraint to another class and must be inserted after |
| * instances of the other class. |
| */ |
| public void setConstraintDependencies(Vector constraintDependencies) { |
| this.constraintDependencies = constraintDependencies; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the copy policy. |
| * This would be 'protected' but the EJB stuff in another |
| * package needs it to be public |
| */ |
| public void setCopyPolicy(CopyPolicy policy) { |
| copyPolicy = policy; |
| if (policy != null) { |
| policy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the name of a Class that implements CopyPolicy |
| * Will be instantiatied as a copy policy at initialization times |
| * using the no-args constructor |
| */ |
| public void setCopyPolicyClassName(String className) { |
| copyPolicyClassName = className; |
| } |
| |
| /** |
| * INTERNAL: |
| * The descriptors default table can be configured if the first table is not desired. |
| */ |
| public void setDefaultTable(DatabaseTable defaultTable) { |
| this.defaultTable = defaultTable; |
| } |
| |
| /** |
| * PUBLIC: |
| * The descriptors default table can be configured if the first table is not desired. |
| */ |
| public void setDefaultTableName(String defaultTableName) { |
| setDefaultTable(new DatabaseTable(defaultTableName)); |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the JPA DescriptorCustomizer class name. |
| * DescriptorCustomizer is the JPA equivalent of an amendment method. |
| */ |
| public void setDescriptorCustomizerClassName(String descriptorCustomizerClassName) { |
| this.descriptorCustomizerClassName = descriptorCustomizerClassName; |
| } |
| |
| /** |
| * ADVANCED: |
| * set the descriptor type (NORMAL by default, others include INTERFACE, AGGREGATE, AGGREGATE COLLECTION) |
| */ |
| public void setDescriptorType(int descriptorType) { |
| this.descriptorType = descriptorType; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is explicitly used by the XML reader. |
| */ |
| public void setDescriptorTypeValue(String value) { |
| if (value.equals("Aggregate collection")) { |
| descriptorIsAggregateCollection(); |
| } else if (value.equals("Aggregate")) { |
| descriptorIsAggregate(); |
| } else if (value.equals("Interface")) { |
| descriptorIsForInterface(); |
| } else { |
| descriptorIsNormal(); |
| } |
| } |
| |
| |
| /** |
| * INTERNAL: |
| * Set the event manager for the descriptor. The event manager is responsible |
| * for managing the pre/post selectors. |
| */ |
| @Override |
| public void setEventManager(DescriptorEventManager eventManager) { |
| this.eventManager = eventManager; |
| if (eventManager != null) { |
| eventManager.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * OBSOLETE - old reader. |
| * This method is explicitly used by the Builder only. |
| */ |
| public void setExistenceChecking(String token) throws DescriptorException { |
| getQueryManager().setExistenceCheck(token); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the fields used by this descriptor. |
| */ |
| public void setFields(Vector<DatabaseField> fields) { |
| this.fields = fields; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is used by the XML Deployment ClassDescriptor to read and write these mappings |
| */ |
| public void setForeignKeyFieldNamesForMultipleTable(List<Association> associations) throws DescriptorException { |
| for (Association association: associations) { |
| addForeignKeyFieldNameForMultipleTable((String) association.getKey(), (String) association.getValue()); |
| } |
| } |
| |
| /** |
| * @param fullyMergeEntity the fullyMergeEntity to set |
| */ |
| public void setFullyMergeEntity(boolean fullyMergeEntity) { |
| getCachePolicy().setFullyMergeEntity(fullyMergeEntity); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be used by this descriptor. |
| * The default is the "FullIdentityMap". |
| */ |
| public void setIdentityMapClass(Class<? extends IdentityMap> theIdentityMapClass) { |
| getCachePolicy().setIdentityMapClass(theIdentityMapClass); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the size of the identity map to be used by this descriptor. |
| * The default is the 100. |
| */ |
| public void setIdentityMapSize(int identityMapSize) { |
| getCachePolicy().setIdentityMapSize(identityMapSize); |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the inheritance policy. |
| */ |
| @Override |
| public void setInheritancePolicy(InheritancePolicy inheritancePolicy) { |
| this.inheritancePolicy = inheritancePolicy; |
| if (inheritancePolicy != null) { |
| inheritancePolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the returning policy. |
| */ |
| public void setReturningPolicy(ReturningPolicy returningPolicy) { |
| this.returningPolicy = returningPolicy; |
| if (returningPolicy != null) { |
| returningPolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void setInitializationStage(int initializationStage) { |
| this.initializationStage = initializationStage; |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the instantiation policy. |
| */ |
| @Override |
| public void setInstantiationPolicy(InstantiationPolicy instantiationPolicy) { |
| this.instantiationPolicy = instantiationPolicy; |
| if (instantiationPolicy != null) { |
| instantiationPolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void setInterfaceInitializationStage(int interfaceInitializationStage) { |
| this.interfaceInitializationStage = interfaceInitializationStage; |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the interface policy. |
| */ |
| public void setInterfacePolicy(InterfacePolicy interfacePolicy) { |
| this.interfacePolicy = interfacePolicy; |
| if (interfacePolicy != null) { |
| interfacePolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the default table if one if not already set. This method will |
| * extract the default table. |
| */ |
| public void setInternalDefaultTable() { |
| if (getDefaultTable() == null) { |
| setDefaultTable(extractDefaultTable()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the default table if one if not already set. This method will set |
| * the table that is provided as the default. |
| */ |
| public void setInternalDefaultTable(DatabaseTable defaultTable) { |
| if (getDefaultTable() == null) { |
| setDefaultTable(defaultTable); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set entity @Cacheable annotation value in cache configuration object. |
| * @param cacheable Entity @Cacheable annotation value for current class |
| * or <code>null</code> if @Cacheable annotation is not set. Parent |
| * values are ignored, value shall refer to current class only. |
| * This value should be set only when SharedCacheMode allows |
| * to override caching on entity level (DISABLE_SELECTIVE |
| * or ENABLE_SELECTIVE). |
| */ |
| public void setCacheable(Boolean cacheable) { |
| getCachePolicy().setCacheable(cacheable); |
| } |
| |
| /** |
| * PUBLIC: |
| * Controls how the Entity instances will be cached. See the CacheIsolationType for details on the options. |
| * @return the isolationType |
| */ |
| public CacheIsolationType getCacheIsolation() { |
| return getCachePolicy().getCacheIsolation(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Controls how the Entity instances and data will be cached. See the CacheIsolationType for details on the options. |
| * To disable all second level caching simply set CacheIsolationType.ISOLATED. Note that setting the isolation |
| * will automatically set the corresponding cacheSynchronizationType. |
| * ISOLATED = DO_NOT_SEND_CHANGES, PROTECTED and SHARED = SEND_OBJECT_CHANGES |
| */ |
| public void setCacheIsolation(CacheIsolationType isolationType) { |
| getCachePolicy().setCacheIsolation(isolationType); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the unit of work should by-pass the session cache. |
| * Objects will be built in the unit of work, and never merged into the session cache. |
| */ |
| public boolean shouldIsolateObjectsInUnitOfWork() { |
| return getCachePolicy().shouldIsolateObjectsInUnitOfWork(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the unit of work should by-pass the IsolatedSession cache. |
| * Objects will be built/merged into the unit of work and into the session cache. |
| * but not built/merge into the IsolatedClientSession cache. |
| */ |
| public boolean shouldIsolateProtectedObjectsInUnitOfWork() { |
| return getCachePolicy().shouldIsolateProtectedObjectsInUnitOfWork(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the unit of work should by-pass the session cache after an early transaction. |
| */ |
| public boolean shouldIsolateObjectsInUnitOfWorkEarlyTransaction() { |
| return getCachePolicy().shouldIsolateObjectsInUnitOfWorkEarlyTransaction(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the unit of work should use the session cache after an early transaction. |
| */ |
| public boolean shouldUseSessionCacheInUnitOfWorkEarlyTransaction() { |
| return getCachePolicy().shouldUseSessionCacheInUnitOfWorkEarlyTransaction(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to store un-converted properties, which are subsequenctly converted |
| * at runtime (through the convertClassNamesToClasses method. |
| */ |
| public Map<String, List<String>> getUnconvertedProperties() { |
| if (unconvertedProperties == null) { |
| unconvertedProperties = new HashMap<>(5); |
| } |
| |
| return unconvertedProperties; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the unit of work cache isolation setting. |
| * This setting configures how the session cache will be used in a unit of work. |
| * @see #setUnitOfWorkCacheIsolationLevel(int) |
| */ |
| public int getUnitOfWorkCacheIsolationLevel() { |
| return getCachePolicy().getUnitOfWorkCacheIsolationLevel(); |
| } |
| |
| /** |
| * ADVANCED: |
| * This setting configures how the session cache will be used in a unit of work. |
| * Most of the options only apply to a unit of work in an early transaction, |
| * such as a unit of work that was flushed (writeChanges), issued a modify query, or acquired a pessimistic lock. |
| * <p> USE_SESSION_CACHE_AFTER_TRANSACTION - Objects built from new data accessed after a unit of work early transaction are stored in the session cache. |
| * This options is the most efficient as it allows the cache to be used after an early transaction. |
| * This should only be used if it is known that this class is not modified in the transaction, |
| * otherwise this could cause uncommitted data to be loaded into the session cache. |
| * ISOLATE_NEW_DATA_AFTER_TRANSACTION - Default (when using caching): Objects built from new data accessed after a unit of work early transaction are only stored in the unit of work. |
| * This still allows previously cached objects to be accessed in the unit of work after an early transaction, |
| * but ensures uncommitted data will never be put in the session cache by storing any object built from new data only in the unit of work. |
| * ISOLATE_CACHE_AFTER_TRANSACTION - After a unit of work early transaction the session cache is no longer used for this class. |
| * Objects will be directly built from the database data and only stored in the unit of work, even if previously cached. |
| * Note that this may lead to poor performance as the session cache is bypassed after an early transaction. |
| * ISOLATE_CACHE_ALWAYS - Default (when using isolated cache): The session cache will never be used for this class. |
| * Objects will be directly built from the database data and only stored in the unit of work. |
| * New objects and changes will also never be merged into the session cache. |
| * Note that this may lead to poor performance as the session cache is bypassed, |
| * however if this class is isolated or pessimistic locked and always accessed in a transaction, this can avoid having to build two copies of the object. |
| */ |
| public void setUnitOfWorkCacheIsolationLevel(int unitOfWorkCacheIsolationLevel) { |
| getCachePolicy().setUnitOfWorkCacheIsolationLevel(unitOfWorkCacheIsolationLevel); |
| } |
| |
| /** |
| * INTERNAL: |
| * set whether this descriptor has any relationships through its mappings, through inheritance, or through aggregates |
| */ |
| public void setHasRelationships(boolean hasRelationships) { |
| this.hasRelationships = hasRelationships; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the Java class that this descriptor maps. |
| * Every descriptor maps one and only one class. |
| */ |
| @Override |
| public void setJavaClass(Class theJavaClass) { |
| javaClass = theJavaClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the java class name, used by the MW. |
| */ |
| public void setJavaClassName(String theJavaClassName) { |
| javaClassName = theJavaClassName; |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the descriptor to be for an interface. |
| * An interface descriptor allows for other classes to reference an interface or one of several other classes. |
| * The implementor classes can be completely unrelated in term of the database stored in distinct tables. |
| * Queries can also be done for the interface which will query each of the implementor classes. |
| * An interface descriptor cannot define any mappings as an interface is just API and not state, |
| * a interface descriptor should define the common query key of its implementors to allow querying. |
| * An interface descriptor also does not define a primary key or table or other settings. |
| * If an interface only has a single implementor (i.e. a classes public interface or remote) then an interface |
| * descriptor should not be defined for it and relationships should be to the implementor class not the interface, |
| * in this case the implementor class can add the interface through its interface policy to map queries on the interface to it. |
| */ |
| public void setJavaInterface(Class theJavaInterface) { |
| javaClass = theJavaInterface; |
| descriptorIsForInterface(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the java interface name, used by the MW. |
| */ |
| public void setJavaInterfaceName(String theJavaInterfaceName) { |
| javaClassName = theJavaInterfaceName; |
| descriptorIsForInterface(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the list of lockable mappings for this project |
| * This method is provided for CMP use. Normally, the lockable mappings are initialized |
| * at descriptor initialization time. |
| */ |
| public void setLockableMappings(List<DatabaseMapping> lockableMappings) { |
| this.lockableMappings = lockableMappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the mappings. |
| */ |
| public void setMappings(Vector<DatabaseMapping> mappings) { |
| // This is used from XML reader so must ensure that all mapping's descriptor has been set. |
| for (Enumeration<DatabaseMapping> mappingsEnum = mappings.elements(); mappingsEnum.hasMoreElements();) { |
| DatabaseMapping mapping = mappingsEnum.nextElement(); |
| |
| // For CR#2646, if the mapping already points to the parent descriptor then leave it. |
| if (mapping.getDescriptor() == null) { |
| mapping.setDescriptor(this); |
| } |
| } |
| this.mappings = mappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * |
| * @see #getMultipleTableForeignKeys |
| */ |
| protected void setMultipleTableForeignKeys(Map<DatabaseTable, Set<DatabaseTable>> newValue) { |
| this.multipleTableForeignKeys = newValue; |
| } |
| |
| /** |
| * ADVANCED: |
| * Sets the List of DatabaseTables in the order which INSERTS should take place. |
| * This is normally computed correctly by , however in advanced cases in it may be overridden. |
| */ |
| public void setMultipleTableInsertOrder(List<DatabaseTable> newValue) { |
| this.multipleTableInsertOrder = newValue; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set a multitenant policy on the descriptor. |
| */ |
| public void setMultitenantPolicy(MultitenantPolicy multitenantPolicy) { |
| this.multitenantPolicy = multitenantPolicy; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return if delete cascading has been set on the database for the descriptor's |
| * multiple tables. |
| */ |
| public boolean isCascadeOnDeleteSetOnDatabaseOnSecondaryTables() { |
| return isCascadeOnDeleteSetOnDatabaseOnSecondaryTables; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set if delete cascading has been set on the database for the descriptor's |
| * multiple tables. |
| * This will avoid the delete SQL being generated for those tables. |
| */ |
| public void setIsCascadeOnDeleteSetOnDatabaseOnSecondaryTables(boolean isCascadeOnDeleteSetOnDatabaseOnSecondaryTables) { |
| this.isCascadeOnDeleteSetOnDatabaseOnSecondaryTables = isCascadeOnDeleteSetOnDatabaseOnSecondaryTables; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the ObjectBuilder. |
| */ |
| @Override |
| protected void setObjectBuilder(ObjectBuilder builder) { |
| objectBuilder = builder; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the OptimisticLockingPolicy. |
| * This can be one of the provided locking policies or a user defined policy. |
| * @see VersionLockingPolicy |
| * @see TimestampLockingPolicy |
| * @see FieldsLockingPolicy |
| */ |
| public void setOptimisticLockingPolicy(OptimisticLockingPolicy optimisticLockingPolicy) { |
| this.optimisticLockingPolicy = optimisticLockingPolicy; |
| if (optimisticLockingPolicy != null) { |
| optimisticLockingPolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the primary key field of the descriptors table. |
| * This should only be called if it is a singlton primary key field, |
| * otherwise addPrimaryKeyFieldName should be called. |
| * If the descriptor has many tables, this must be the primary key in all of the tables. |
| * |
| * @see #addPrimaryKeyFieldName(String) |
| */ |
| public void setPrimaryKeyFieldName(String fieldName) { |
| addPrimaryKeyFieldName(fieldName); |
| } |
| |
| /** |
| * PUBLIC: |
| * User can specify a vector of all the primary key field names if primary key is composite. |
| * |
| * @see #addPrimaryKeyFieldName(String) |
| */ |
| @Override |
| public void setPrimaryKeyFieldNames(Vector primaryKeyFieldsName) { |
| setPrimaryKeyFields(new ArrayList(primaryKeyFieldsName.size())); |
| for (Enumeration keyEnum = primaryKeyFieldsName.elements(); keyEnum.hasMoreElements();) { |
| addPrimaryKeyFieldName((String)keyEnum.nextElement()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the primary key fields |
| */ |
| @Override |
| public void setPrimaryKeyFields(List<DatabaseField> thePrimaryKeyFields) { |
| primaryKeyFields = thePrimaryKeyFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the user defined properties. |
| */ |
| public void setProperties(Map properties) { |
| this.properties = properties; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the user defined property. |
| */ |
| public void setProperty(String name, Object value) { |
| getProperties().put(name, value); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the query keys. |
| */ |
| public void setQueryKeys(Map<String, QueryKey> queryKeys) { |
| this.queryKeys = queryKeys; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the query manager. |
| */ |
| public void setQueryManager(DescriptorQueryManager queryManager) { |
| this.queryManager = queryManager; |
| if (queryManager != null) { |
| queryManager.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be used by this descriptor. |
| * The default is the "FullIdentityMap". |
| */ |
| public void setRemoteIdentityMapClass(Class theIdentityMapClass) { |
| getCachePolicy().setRemoteIdentityMapClass(theIdentityMapClass); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the size of the identity map to be used by this descriptor. |
| * The default is the 100. |
| */ |
| public void setRemoteIdentityMapSize(int identityMapSize) { |
| getCachePolicy().setRemoteIdentityMapSize(identityMapSize); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the sequence number field. |
| */ |
| public void setSequenceNumberField(DatabaseField sequenceNumberField) { |
| this.sequenceNumberField = sequenceNumberField; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the sequence number field name. |
| * This is the field in the descriptors table that needs its value to be generated. |
| * This is normally the primary key field of the descriptor. |
| */ |
| public void setSequenceNumberFieldName(String fieldName) { |
| if (fieldName == null) { |
| setSequenceNumberField(null); |
| } else { |
| setSequenceNumberField(new DatabaseField(fieldName)); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the sequence number name. |
| * This is the seq_name part of the row stored in the sequence table for this descriptor. |
| * If using Oracle native sequencing this is the name of the Oracle sequence object. |
| * If using Sybase native sequencing this name has no meaning, but should still be set for compatibility. |
| * The name does not have to be unique among descriptors, as having descriptors share sequences can |
| * improve pre-allocation performance. |
| */ |
| public void setSequenceNumberName(String name) { |
| sequenceNumberName = name; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the name of the session local to this descriptor. |
| * This is used by the session broker. |
| */ |
| protected void setSessionName(String sessionName) { |
| this.sessionName = sessionName; |
| } |
| |
| /** |
| * PUBLIC: |
| * set if the descriptor is defined to always conform the results in unit of work in read query. |
| * |
| */ |
| public void setShouldAlwaysConformResultsInUnitOfWork(boolean shouldAlwaysConformResultsInUnitOfWork) { |
| this.shouldAlwaysConformResultsInUnitOfWork = shouldAlwaysConformResultsInUnitOfWork; |
| } |
| |
| /** |
| * PUBLIC: |
| * When the <CODE>shouldAlwaysRefreshCache</CODE> argument passed into this method is <CODE>true</CODE>, |
| * this method configures a <CODE>ClassDescriptor</CODE> to always refresh the cache if data is received from |
| * the database by any query.<P> |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. |
| * For example, by default, when a query for a single object based on its primary key is executed, OracleAS TopLink |
| * will first look in the cache for the object. If the object is in the cache, the cached object is returned and |
| * data is not refreshed. To avoid cache hits, use the {@link #disableCacheHits} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.<P> |
| * |
| * Use this property with caution because it can lead to poor performance and may refresh on queries when it is not desired. |
| * Normally, if you require fresh data, it is better to configure a query with {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}. |
| * To ensure that refreshes are only done when required, use this method in conjunction with {@link #onlyRefreshCacheIfNewerVersion}.<P> |
| * |
| * When the <CODE>shouldAlwaysRefreshCache</CODE> argument passed into this method is <CODE>false</CODE>, this method |
| * ensures that a <CODE>ClassDescriptor</CODE> is not configured to always refresh the cache if data is received from the database by any query. |
| * |
| * @see #alwaysRefreshCache |
| * @see #dontAlwaysRefreshCache |
| */ |
| public void setShouldAlwaysRefreshCache(boolean shouldAlwaysRefreshCache) { |
| getCachePolicy().setShouldAlwaysRefreshCache(shouldAlwaysRefreshCache); |
| } |
| |
| /** |
| * PUBLIC: |
| * When the <CODE>shouldAlwaysRefreshCacheOnRemote</CODE> argument passed into this method is <CODE>true</CODE>, |
| * this method configures a <CODE>ClassDescriptor</CODE> to always remotely refresh the cache if data is received from |
| * the database by any query in a {@link org.eclipse.persistence.sessions.remote.RemoteSession}. |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. For |
| * example, by default, when a query for a single object based on its primary key is executed, OracleAS TopLink |
| * will first look in the cache for the object. If the object is in the cache, the cached object is returned and |
| * data is not refreshed. To avoid cache hits, use the {@link #disableCacheHitsOnRemote} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.<P> |
| * |
| * Use this property with caution because it can lead to poor performance and may refresh on queries when it is |
| * not desired. Normally, if you require fresh data, it is better to configure a query with {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}. |
| * To ensure that refreshes are only done when required, use this method in conjunction with {@link #onlyRefreshCacheIfNewerVersion}.<P> |
| * |
| * When the <CODE>shouldAlwaysRefreshCacheOnRemote</CODE> argument passed into this method is <CODE>false</CODE>, |
| * this method ensures that a <CODE>ClassDescriptor</CODE> is not configured to always remotely refresh the cache if data |
| * is received from the database by any query in a {@link org.eclipse.persistence.sessions.remote.RemoteSession}. |
| * |
| * @see #alwaysRefreshCacheOnRemote |
| * @see #dontAlwaysRefreshCacheOnRemote |
| */ |
| public void setShouldAlwaysRefreshCacheOnRemote(boolean shouldAlwaysRefreshCacheOnRemote) { |
| getCachePolicy().setShouldAlwaysRefreshCacheOnRemote(shouldAlwaysRefreshCacheOnRemote); |
| } |
| |
| /** |
| * PUBLIC: |
| * Define if the descriptor reference class is read-only |
| */ |
| public void setShouldBeReadOnly(boolean shouldBeReadOnly) { |
| this.shouldBeReadOnly = shouldBeReadOnly; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor to be read-only. |
| * Declaring a descriptor is read-only means that instances of the reference class will never be modified. |
| * Read-only descriptor is usually used in the unit of work to gain performance as there is no need for |
| * the registration, clone and merge for the read-only classes. |
| */ |
| public void setReadOnly() { |
| setShouldBeReadOnly(true); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if cache hits on primary key read object queries should be disabled. |
| * |
| * @see #alwaysRefreshCache() |
| */ |
| public void setShouldDisableCacheHits(boolean shouldDisableCacheHits) { |
| getCachePolicy().setShouldDisableCacheHits(shouldDisableCacheHits); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if the remote session cache hits on primary key read object queries is allowed or not. |
| * |
| * @see #disableCacheHitsOnRemote() |
| */ |
| public void setShouldDisableCacheHitsOnRemote(boolean shouldDisableCacheHitsOnRemote) { |
| getCachePolicy().setShouldDisableCacheHitsOnRemote(shouldDisableCacheHitsOnRemote); |
| } |
| |
| /** |
| * ADVANCED: |
| * When set to false, this setting will allow the UOW to avoid locking the shared cache instance in order to perform a clone. |
| * Caution should be taken as setting this to false may allow cloning of partial updates |
| */ |
| public void setShouldLockForClone(boolean shouldLockForClone) { |
| this.shouldLockForClone = shouldLockForClone; |
| } |
| |
| /** |
| * PUBLIC: |
| * When the <CODE>shouldOnlyRefreshCacheIfNewerVersion</CODE> argument passed into this method is <CODE>true</CODE>, |
| * this method configures a <CODE>ClassDescriptor</CODE> to only refresh the cache if the data received from the database |
| * by a query is newer than the data in the cache (as determined by the optimistic locking field) and as long as one of the following is true: |
| * |
| * <UL> |
| * <LI>the <CODE>ClassDescriptor</CODE> was configured by calling {@link #alwaysRefreshCache} or {@link #alwaysRefreshCacheOnRemote},</LI> |
| * <LI>the query was configured by calling {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}, or</LI> |
| * <LI>the query was a call to {@link org.eclipse.persistence.sessions.Session#refreshObject}</LI> |
| * </UL> |
| * <P> |
| * |
| * However, if a query hits the cache, data is not refreshed regardless of how this setting is configured. For example, by default, |
| * when a query for a single object based on its primary key is executed, OracleAS TopLink will first look in the cache for the object. |
| * If the object is in the cache, the cached object is returned and data is not refreshed. To avoid cache hits, use |
| * the {@link #disableCacheHits} method.<P> |
| * |
| * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.<P> |
| * |
| * When the <CODE>shouldOnlyRefreshCacheIfNewerVersion</CODE> argument passed into this method is <CODE>false</CODE>, this method |
| * ensures that a <CODE>ClassDescriptor</CODE> is not configured to only refresh the cache if the data received from the database by a |
| * query is newer than the data in the cache (as determined by the optimistic locking field). |
| * |
| * @see #onlyRefreshCacheIfNewerVersion |
| * @see #dontOnlyRefreshCacheIfNewerVersion |
| */ |
| public void setShouldOnlyRefreshCacheIfNewerVersion(boolean shouldOnlyRefreshCacheIfNewerVersion) { |
| getCachePolicy().setShouldOnlyRefreshCacheIfNewerVersion(shouldOnlyRefreshCacheIfNewerVersion); |
| } |
| |
| /** |
| * PUBLIC: |
| * This is set to turn off the ordering of mappings. By Default this is set to true. |
| * By ordering the mappings insures that object are merged in the right order. |
| * If the order of the mappings needs to be specified by the developer then set this to |
| * false and will use the order that the mappings were added to the descriptor |
| */ |
| public void setShouldOrderMappings(boolean shouldOrderMappings) { |
| this.shouldOrderMappings = shouldOrderMappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set to false to have queries conform to a UnitOfWork without registering |
| * any additional objects not already in that UnitOfWork. |
| * @see #shouldRegisterResultsInUnitOfWork |
| */ |
| public void setShouldRegisterResultsInUnitOfWork(boolean shouldRegisterResultsInUnitOfWork) { |
| //bug 2612601 |
| this.shouldRegisterResultsInUnitOfWork = shouldRegisterResultsInUnitOfWork; |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the table name for the class of objects the receiver describes. |
| * If the table has a qualifier it should be specified using the dot notation, |
| * (i.e. "userid.employee"). This method is used for single table. |
| */ |
| public void setTableName(String tableName) throws DescriptorException { |
| if (getTables().isEmpty()) { |
| addTableName(tableName); |
| } else { |
| getTables().get(0).setPossiblyQualifiedName(tableName); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Specify the all table names for the class of objects the receiver describes. |
| * If the table has a qualifier it should be specified using the dot notation, |
| * (i.e. "userid.employee"). This method is used for multiple tables |
| */ |
| public void setTableNames(Vector tableNames) { |
| setTables(NonSynchronizedVector.newInstance(tableNames.size())); |
| for (Enumeration tableEnum = tableNames.elements(); tableEnum.hasMoreElements();) { |
| addTableName((String)tableEnum.nextElement()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the table per class policy. |
| */ |
| public void setTablePerClassPolicy(TablePerClassPolicy tablePerClassPolicy) { |
| interfacePolicy = tablePerClassPolicy; |
| if (interfacePolicy != null) { |
| interfacePolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * PUBLIC: Set the table Qualifier for this descriptor. This table creator will be used for |
| * all tables in this descriptor |
| */ |
| public void setTableQualifier(String tableQualifier) { |
| for (Enumeration<DatabaseTable> enumtr = getTables().elements(); enumtr.hasMoreElements();) { |
| DatabaseTable table = enumtr.nextElement(); |
| table.setTableQualifier(tableQualifier); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets the tables |
| */ |
| public void setTables(Vector<DatabaseTable> theTables) { |
| tables = theTables; |
| } |
| |
| /** |
| * ADVANCED: |
| * Sets the WrapperPolicy for this descriptor. |
| * This advanced feature can be used to wrap objects with other classes such as CORBA TIE objects or EJBs. |
| */ |
| public void setWrapperPolicy(WrapperPolicy wrapperPolicy) { |
| this.wrapperPolicy = wrapperPolicy; |
| |
| // For bug 2766379 must be able to set the wrapper policy back to default |
| // which is null. |
| if (wrapperPolicy != null) { |
| wrapperPolicy.setDescriptor(this); |
| } |
| getObjectBuilder().setHasWrapperPolicy(wrapperPolicy != null); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor is defined to always conform the results in unit of work in read query. |
| * |
| */ |
| public boolean shouldAlwaysConformResultsInUnitOfWork() { |
| return shouldAlwaysConformResultsInUnitOfWork; |
| } |
| |
| /** |
| * PUBLIC: |
| * This method returns <CODE>true</CODE> if the <CODE>ClassDescriptor</CODE> is configured to always refresh |
| * the cache if data is received from the database by any query. Otherwise, it returns <CODE>false</CODE>. |
| * |
| * @see #setShouldAlwaysRefreshCache |
| */ |
| public boolean shouldAlwaysRefreshCache() { |
| return getCachePolicy().shouldAlwaysRefreshCache(); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method returns <CODE>true</CODE> if the <CODE>ClassDescriptor</CODE> is configured to always remotely |
| * refresh the cache if data is received from the database by any query in a {@link org.eclipse.persistence.sessions.remote.RemoteSession}. |
| * Otherwise, it returns <CODE>false</CODE>. |
| * |
| * @see #setShouldAlwaysRefreshCacheOnRemote |
| */ |
| public boolean shouldAlwaysRefreshCacheOnRemote() { |
| return getCachePolicy().shouldAlwaysRefreshCacheOnRemote(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the descriptor reference class is defined as read-only |
| * |
| */ |
| public boolean shouldBeReadOnly() { |
| return shouldBeReadOnly; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if for cache hits on primary key read object queries to be disabled. |
| * |
| * @see #disableCacheHits() |
| */ |
| public boolean shouldDisableCacheHits() { |
| return getCachePolicy().shouldDisableCacheHits(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return if the remote server session cache hits on primary key read object queries is aloowed or not. |
| * |
| * @see #disableCacheHitsOnRemote() |
| */ |
| public boolean shouldDisableCacheHitsOnRemote() { |
| return getCachePolicy().shouldDisableCacheHitsOnRemote(); |
| } |
| |
| /** |
| * PUBLIC: |
| * This method returns <CODE>true</CODE> if the <CODE>ClassDescriptor</CODE> is configured to only refresh the cache |
| * if the data received from the database by a query is newer than the data in the cache (as determined by the |
| * optimistic locking field). Otherwise, it returns <CODE>false</CODE>. |
| * |
| * @see #setShouldOnlyRefreshCacheIfNewerVersion |
| */ |
| public boolean shouldOnlyRefreshCacheIfNewerVersion() { |
| return getCachePolicy().shouldOnlyRefreshCacheIfNewerVersion(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if mappings should be ordered or not. By default this is set to true |
| * to prevent attributes from being merged in the wrong order |
| * |
| */ |
| public boolean shouldOrderMappings() { |
| return shouldOrderMappings; |
| } |
| |
| /** |
| * INTERNAL: |
| * PERF: Return if the primary key is simple (direct-mapped) to allow fast extraction. |
| */ |
| public boolean hasSimplePrimaryKey() { |
| return hasSimplePrimaryKey; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor is involved in a table per class inheritance. |
| */ |
| public boolean hasTablePerClassPolicy() { |
| return hasInterfacePolicy() && interfacePolicy.isTablePerClassPolicy(); |
| } |
| |
| /** |
| * INTERNAL: |
| * PERF: Set if the primary key is simple (direct-mapped) to allow fast extraction. |
| */ |
| public void setHasSimplePrimaryKey(boolean hasSimplePrimaryKey) { |
| this.hasSimplePrimaryKey = hasSimplePrimaryKey; |
| } |
| |
| /** |
| * INTERNAL: |
| * PERF: Return if deferred locks should be used. |
| * Used to optimize read locking. |
| * This is determined based on if any relationships do not use indirection. |
| */ |
| public boolean shouldAcquireCascadedLocks() { |
| return shouldAcquireCascadedLocks; |
| } |
| |
| /** |
| * INTERNAL: |
| * PERF: Set if deferred locks should be used. |
| * This is determined based on if any relationships do not use indirection, |
| * but this provides a backdoor hook to force on if require because of events usage etc. |
| */ |
| public void setShouldAcquireCascadedLocks(boolean shouldAcquireCascadedLocks) { |
| this.shouldAcquireCascadedLocks = shouldAcquireCascadedLocks; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor should using an additional join expresison. |
| */ |
| public boolean shouldUseAdditionalJoinExpression() { |
| // Return true, if the query manager has an additional join expression |
| // and this descriptor is not part of an inheritance hierarchy using a |
| // view (CR#3701077) |
| return ((getQueryManager().getAdditionalJoinExpression() != null) && ! (hasInheritance() && getInheritancePolicy().hasView())); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using CacheIdentityMap |
| */ |
| public boolean shouldUseCacheIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.CacheIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using FullIdentityMap |
| */ |
| public boolean shouldUseFullIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.FullIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using SoftIdentityMap |
| */ |
| public boolean shouldUseSoftIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.SoftIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using SoftIdentityMap |
| */ |
| public boolean shouldUseRemoteSoftIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.SoftIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using HardCacheWeakIdentityMap. |
| */ |
| public boolean shouldUseHardCacheWeakIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.HardCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using NoIdentityMap |
| */ |
| public boolean shouldUseNoIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.NoIdentityMap_Class); |
| } |
| |
| /** |
| * INTERNAL: |
| * Allows one to do conforming in a UnitOfWork without registering. |
| * Queries executed on a UnitOfWork will only return working copies for objects |
| * that have already been registered. |
| * <p>Extreme care should be taken in using this feature, for a user will |
| * get back a mix of registered and original (unregistered) objects. |
| * <p>Best used with a WrapperPolicy where invoking on an object will trigger |
| * its registration (CMP). Without a WrapperPolicy {@link org.eclipse.persistence.sessions.UnitOfWork#registerExistingObject registerExistingObject} |
| * should be called on any object that you intend to change. |
| * @return true by default. |
| * @see #setShouldRegisterResultsInUnitOfWork |
| * @see org.eclipse.persistence.queries.ObjectLevelReadQuery#shouldRegisterResultsInUnitOfWork |
| */ |
| public boolean shouldRegisterResultsInUnitOfWork() { |
| // bug 2612601 |
| return shouldRegisterResultsInUnitOfWork; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using CacheIdentityMap |
| */ |
| public boolean shouldUseRemoteCacheIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.CacheIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using FullIdentityMap |
| */ |
| public boolean shouldUseRemoteFullIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.FullIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using HardCacheWeakIdentityMap |
| */ |
| public boolean shouldUseRemoteHardCacheWeakIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.HardCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using NoIdentityMap |
| */ |
| public boolean shouldUseRemoteNoIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.NoIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using SoftCacheWeakIdentityMap |
| */ |
| public boolean shouldUseRemoteSoftCacheWeakIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.SoftCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using WeakIdentityMap |
| */ |
| public boolean shouldUseRemoteWeakIdentityMap() { |
| return (getRemoteIdentityMapClass() == ClassConstants.WeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using SoftCacheWeakIdentityMap. |
| */ |
| public boolean shouldUseSoftCacheWeakIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.SoftCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor is using WeakIdentityMap |
| */ |
| public boolean shouldUseWeakIdentityMap() { |
| return (getIdentityMapClass() == ClassConstants.WeakIdentityMap_Class); |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns whether this descriptor is capable of supporting weaved change tracking. |
| * This method is used before the project is initialized. |
| */ |
| public boolean supportsChangeTracking(Project project){ |
| // Check the descriptor: if field-locking is used, cannot do |
| // change tracking because field-locking requires backup clone. |
| OptimisticLockingPolicy lockingPolicy = getOptimisticLockingPolicy(); |
| if (lockingPolicy != null && (lockingPolicy instanceof FieldsLockingPolicy)) { |
| return false; |
| } |
| Vector<DatabaseMapping> mappings = getMappings(); |
| for (Iterator<DatabaseMapping> iterator = mappings.iterator(); iterator.hasNext();) { |
| DatabaseMapping mapping = iterator.next(); |
| if (!mapping.isChangeTrackingSupported(project) ) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * PUBLIC: |
| * Returns a brief string representation of the receiver. |
| */ |
| @Override |
| public String toString() { |
| return Helper.getShortClassName(getClass()) + "(" + getJavaClassName() + " --> " + getTables() + ")"; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the locking policy an all fields locking policy. |
| * A field locking policy is base on locking on all fields by comparing with their previous values to detect field-level collisions. |
| * Note: the unit of work must be used for all updates when using field locking. |
| * @see AllFieldsLockingPolicy |
| */ |
| public void useAllFieldsLocking() { |
| setOptimisticLockingPolicy(new AllFieldsLockingPolicy()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the cache identity map. |
| * This map caches the LRU instances read from the database. |
| * Note: This map does not guarantee object identity. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useCacheIdentityMap() { |
| setIdentityMapClass(ClassConstants.CacheIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the locking policy a changed fields locking policy. |
| * A field locking policy is base on locking on all changed fields by comparing with their previous values to detect field-level collisions. |
| * Note: the unit of work must be used for all updates when using field locking. |
| * @see ChangedFieldsLockingPolicy |
| */ |
| public void useChangedFieldsLocking() { |
| setOptimisticLockingPolicy(new ChangedFieldsLockingPolicy()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Specifies that the creation of clones within a unit of work is done by |
| * sending the #clone() method to the original object. The #clone() method |
| * must return a logical shallow copy of the original object. |
| * This can be used if the default mechanism of creating a new instance |
| * does not handle the object's non-persistent attributes correctly. |
| * |
| * @see #useCloneCopyPolicy(String) |
| */ |
| public void useCloneCopyPolicy() { |
| useCloneCopyPolicy("clone"); |
| } |
| |
| /** |
| * PUBLIC: |
| * Specifies that the creation of clones within a unit of work is done by |
| * sending the cloneMethodName method to the original object. This method |
| * must return a logical shallow copy of the original object. |
| * This can be used if the default mechanism of creating a new instance |
| * does not handle the object's non-persistent attributes correctly. |
| * |
| * @see #useCloneCopyPolicy() |
| */ |
| public void useCloneCopyPolicy(String cloneMethodName) { |
| CloneCopyPolicy policy = new CloneCopyPolicy(); |
| policy.setMethodName(cloneMethodName); |
| setCopyPolicy(policy); |
| } |
| |
| /** |
| * PUBLIC: |
| * Specifies that the creation of clones within a unit of work is done by building |
| * a new instance using the |
| * technique indicated by the descriptor's instantiation policy |
| * (which by default is to use the |
| * the default constructor). This new instance is then populated by using the |
| * descriptor's mappings to copy attributes from the original to the clone. |
| * This is the default. |
| * If another mechanism is desired the copy policy allows for a clone method to be called. |
| * |
| * @see #useCloneCopyPolicy() |
| * @see #useCloneCopyPolicy(String) |
| * @see #useDefaultConstructorInstantiationPolicy() |
| * @see #useMethodInstantiationPolicy(String) |
| * @see #useFactoryInstantiationPolicy(Class, String) |
| * @see #useFactoryInstantiationPolicy(Class, String, String) |
| * @see #useFactoryInstantiationPolicy(Object, String) |
| */ |
| public void useInstantiationCopyPolicy() { |
| setCopyPolicy(new InstantiationCopyPolicy()); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use the default constructor to create new instances of objects built from the database. |
| * This is the default. |
| * The descriptor's class must either define a default constructor or define |
| * no constructors at all. |
| * |
| * @see #useMethodInstantiationPolicy(String) |
| * @see #useFactoryInstantiationPolicy(Class, String) |
| * @see #useFactoryInstantiationPolicy(Class, String, String) |
| * @see #useFactoryInstantiationPolicy(Object, String) |
| */ |
| public void useDefaultConstructorInstantiationPolicy() { |
| getInstantiationPolicy().useDefaultConstructorInstantiationPolicy(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use an object factory to create new instances of objects built from the database. |
| * The methodName is the name of the |
| * method that will be invoked on the factory. When invoked, it must return a new instance |
| * of the descriptor's class. |
| * The factory will be created by invoking the factoryClass's default constructor. |
| * |
| * @see #useDefaultConstructorInstantiationPolicy() |
| * @see #useMethodInstantiationPolicy(String) |
| * @see #useFactoryInstantiationPolicy(Class, String, String) |
| * @see #useFactoryInstantiationPolicy(Object, String) |
| */ |
| public void useFactoryInstantiationPolicy(Class factoryClass, String methodName) { |
| getInstantiationPolicy().useFactoryInstantiationPolicy(factoryClass, methodName); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the factory class name, used by the MW. |
| */ |
| public void useFactoryInstantiationPolicy(String factoryClassName, String methodName) { |
| getInstantiationPolicy().useFactoryInstantiationPolicy(factoryClassName, methodName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use an object factory to create new instances of objects built from the database. |
| * The factoryMethodName is a static method declared by the factoryClass. |
| * When invoked, it must return an instance of the factory. The methodName is the name of the |
| * method that will be invoked on the factory. When invoked, it must return a new instance |
| * of the descriptor's class. |
| * |
| * @see #useDefaultConstructorInstantiationPolicy() |
| * @see #useFactoryInstantiationPolicy(Class, String) |
| * @see #useFactoryInstantiationPolicy(Object, String) |
| * @see #useMethodInstantiationPolicy(String) |
| */ |
| public void useFactoryInstantiationPolicy(Class factoryClass, String methodName, String factoryMethodName) { |
| getInstantiationPolicy().useFactoryInstantiationPolicy(factoryClass, methodName, factoryMethodName); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the factory class name, used by the MW. |
| */ |
| public void useFactoryInstantiationPolicy(String factoryClassName, String methodName, String factoryMethodName) { |
| getInstantiationPolicy().useFactoryInstantiationPolicy(factoryClassName, methodName, factoryMethodName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use an object factory to create new instances of objects built from the database. |
| * The methodName is the name of the |
| * method that will be invoked on the factory. When invoked, it must return a new instance |
| * of the descriptor's class. |
| * |
| * @see #useDefaultConstructorInstantiationPolicy() |
| * @see #useMethodInstantiationPolicy(String) |
| * @see #useFactoryInstantiationPolicy(Class, String) |
| * @see #useFactoryInstantiationPolicy(Class, String, String) |
| */ |
| public void useFactoryInstantiationPolicy(Object factory, String methodName) { |
| getInstantiationPolicy().useFactoryInstantiationPolicy(factory, methodName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the full identity map. |
| * This map caches all instances read and grows to accomodate them. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useFullIdentityMap() { |
| getCachePolicy().useFullIdentityMap(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the hard cache weak identity map. |
| * This map uses weak references to only cache object in-memory. |
| * It also includes a secondary fixed sized hard cache to improve caching performance. |
| * This is provided because some Java VM's implement soft references differently. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useHardCacheWeakIdentityMap() { |
| getCachePolicy().useHardCacheWeakIdentityMap(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the soft identity map. |
| * This map uses soft references to only cache all object in-memory, until memory is low. |
| * Note that "low" is interpreted differently by different JVM's. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useSoftIdentityMap() { |
| getCachePolicy().useSoftIdentityMap(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the soft identity map. |
| * This map uses soft references to only cache all object in-memory, until memory is low. |
| * Note that "low" is interpreted differently by different JVM's. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteSoftIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.SoftIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Use the specified static method to create new instances of objects built from the database. |
| * This method must be statically declared by the descriptor's class, and it must |
| * return a new instance of the descriptor's class. |
| * |
| * @see #useDefaultConstructorInstantiationPolicy() |
| * @see #useFactoryInstantiationPolicy(Class, String) |
| * @see #useFactoryInstantiationPolicy(Class, String, String) |
| * @see #useFactoryInstantiationPolicy(Object, String) |
| */ |
| public void useMethodInstantiationPolicy(String staticMethodName) { |
| getInstantiationPolicy().useMethodInstantiationPolicy(staticMethodName); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the no identity map. |
| * This map does no caching. |
| * Note: This map does not maintain object identity. |
| * In general if caching is not desired a WeakIdentityMap should be used with an isolated descriptor. |
| * The default is the "SoftCacheWeakIdentityMap". |
| * @see #setCacheIsolation(CacheIsolationType) |
| */ |
| public void useNoIdentityMap() { |
| getCachePolicy().useNoIdentityMap(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the cache identity map. |
| * This map caches the LRU instances read from the database. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteCacheIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.CacheIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the full identity map. |
| * This map caches all instances read and grows to accomodate them. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteFullIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.FullIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the hard cache weak identity map. |
| * This map uses weak references to only cache object in-memory. |
| * It also includes a secondary fixed sized soft cache to improve caching performance. |
| * This is provided because some Java VM's do not implement soft references correctly. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteHardCacheWeakIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.HardCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the no identity map. |
| * This map does no caching. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteNoIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.NoIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the soft cache weak identity map. |
| * The SoftCacheIdentityMap holds a fixed number of objects is memory |
| * (using SoftReferences) to improve caching. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteSoftCacheWeakIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.SoftCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the weak identity map. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useRemoteWeakIdentityMap() { |
| getCachePolicy().setRemoteIdentityMapClass(ClassConstants.WeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the locking policy a selected fields locking policy. |
| * A field locking policy is base on locking on the specified fields by comparing with their previous values to detect field-level collisions. |
| * Note: the unit of work must be used for all updates when using field locking. |
| * @see SelectedFieldsLockingPolicy |
| */ |
| public void useSelectedFieldsLocking(Vector fieldNames) { |
| SelectedFieldsLockingPolicy policy = new SelectedFieldsLockingPolicy(); |
| policy.setLockFieldNames(fieldNames); |
| setOptimisticLockingPolicy(policy); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if the receiver uses either all or changed fields for optimistic locking. |
| */ |
| public boolean usesFieldLocking() { |
| return (usesOptimisticLocking() && (getOptimisticLockingPolicy() instanceof FieldsLockingPolicy)); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the soft cache weak identity map. |
| * The SoftCacheIdentityMap holds a fixed number of objects is memory |
| * (using SoftReferences) to improve caching. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useSoftCacheWeakIdentityMap() { |
| setIdentityMapClass(ClassConstants.SoftCacheWeakIdentityMap_Class); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if the receiver uses write (optimistic) locking. |
| */ |
| public boolean usesOptimisticLocking() { |
| return (optimisticLockingPolicy != null); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if the receiver uses version optimistic locking. |
| */ |
| public boolean usesVersionLocking() { |
| return (usesOptimisticLocking() && (getOptimisticLockingPolicy() instanceof VersionLockingPolicy)); |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if the receiver uses sequence numbers. |
| */ |
| public boolean usesSequenceNumbers() { |
| return this.sequenceNumberField != null; |
| } |
| |
| /** |
| * PUBLIC: |
| * Use the Timestamps locking policy and storing the value in the cache key |
| * #see useVersionLocking(String) |
| */ |
| public void useTimestampLocking(String writeLockFieldName) { |
| useTimestampLocking(writeLockFieldName, true); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the locking policy to use timestamp version locking. |
| * This updates the timestamp field on all updates, first comparing that the field has not changed to detect locking conflicts. |
| * Note: many database have limited precision of timestamps which can be an issue is highly concurrent systems. |
| * |
| * The parameter 'shouldStoreInCache' configures the version lock value to be stored in the cache or in the object. |
| * Note: if using a stateless model where the object can be passed to a client and then later updated in a different transaction context, |
| * then the version lock value should not be stored in the cache, but in the object to ensure it is the correct value for that object. |
| * @see VersionLockingPolicy |
| */ |
| public void useTimestampLocking(String writeLockFieldName, boolean shouldStoreInCache) { |
| TimestampLockingPolicy policy = new TimestampLockingPolicy(writeLockFieldName); |
| if (shouldStoreInCache) { |
| policy.storeInCache(); |
| } else { |
| policy.storeInObject(); |
| } |
| setOptimisticLockingPolicy(policy); |
| } |
| |
| /** |
| * PUBLIC: |
| * Default to use the version locking policy and storing the value in the cache key |
| * #see useVersionLocking(String) |
| */ |
| public void useVersionLocking(String writeLockFieldName) { |
| useVersionLocking(writeLockFieldName, true); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the locking policy to use numeric version locking. |
| * This updates the version field on all updates, first comparing that the field has not changed to detect locking conflicts. |
| * |
| * The parameter 'shouldStoreInCache' configures the version lock value to be stored in the cache or in the object. |
| * Note: if using a stateless model where the object can be passed to a client and then later updated in a different transaction context, |
| * then the version lock value should not be stored in the cache, but in the object to ensure it is the correct value for that object. |
| * @see TimestampLockingPolicy |
| */ |
| public void useVersionLocking(String writeLockFieldName, boolean shouldStoreInCache) { |
| VersionLockingPolicy policy = new VersionLockingPolicy(writeLockFieldName); |
| if (shouldStoreInCache) { |
| policy.storeInCache(); |
| } else { |
| policy.storeInObject(); |
| } |
| setOptimisticLockingPolicy(policy); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the class of identity map to be the weak identity map. |
| * The default is the "SoftCacheWeakIdentityMap". |
| */ |
| public void useWeakIdentityMap() { |
| getCachePolicy().useWeakIdentityMap(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Validate the entire post-initialization descriptor. |
| */ |
| protected void validateAfterInitialization(AbstractSession session) { |
| selfValidationAfterInitialization(session); |
| for (DatabaseMapping mapping : getMappings()) { |
| mapping.validateAfterInitialization(session); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Validate the entire pre-initialization descriptor. |
| */ |
| protected void validateBeforeInitialization(AbstractSession session) { |
| selfValidationBeforeInitialization(session); |
| for (DatabaseMapping mapping : getMappings()) { |
| mapping.validateBeforeInitialization(session); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Check that the qualifier on the table names are properly set. |
| */ |
| protected void verifyTableQualifiers(Platform platform) { |
| String tableQualifier = platform.getTableQualifier(); |
| if (tableQualifier.length() == 0) { |
| return; |
| } |
| |
| for (DatabaseTable table : getTables()) { |
| if (table.getTableQualifier().length() == 0) { |
| table.setTableQualifier(tableQualifier); |
| } |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the cmp descriptor that holds cmp specific information. |
| * A null return will mean that the descriptor does not represent an Entity, |
| * however it may still represent a MappedSuperclass. |
| * It will be null if it is not being used. |
| */ |
| public CMPPolicy getCMPPolicy() { |
| return cmpPolicy; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set the cmp descriptor that holds cmp specific information. |
| */ |
| public void setCMPPolicy(CMPPolicy newCMPPolicy) { |
| cmpPolicy = newCMPPolicy; |
| if (cmpPolicy != null) { |
| cmpPolicy.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * Return the cache policy. |
| * The cache policy allows for the configuration of caching options. |
| */ |
| public CachePolicy getCachePolicy() { |
| if (this.cachePolicy == null) { |
| this.cachePolicy = new CachePolicy(); |
| } |
| return cachePolicy; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set cache policy for the descriptor. |
| */ |
| public void setCachePolicy(CachePolicy cachePolicy) { |
| this.cachePolicy = cachePolicy; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean hasPessimisticLockingPolicy() { |
| return (cmpPolicy != null) && cmpPolicy.hasPessimisticLockingPolicy(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Get the fetch group manager for the descriptor. The fetch group manager is responsible |
| * for managing the fetch group behaviors and operations. |
| * To use the fetch group, the domain object must implement FetchGroupTracker interface. Otherwise, |
| * a descriptor validation exception would throw during initialization. |
| * |
| * @see org.eclipse.persistence.queries.FetchGroupTracker |
| */ |
| public FetchGroupManager getFetchGroupManager() { |
| return this.fetchGroupManager; |
| } |
| |
| /** |
| * @return the fullyMergeEntity |
| */ |
| public boolean getFullyMergeEntity() { |
| return getCachePolicy().getFullyMergeEntity(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the fetch group manager for the descriptor. The fetch group manager is responsible |
| * for managing the fetch group behaviors and operations. |
| */ |
| public void setFetchGroupManager(FetchGroupManager fetchGroupManager) { |
| this.fetchGroupManager = fetchGroupManager; |
| if (fetchGroupManager != null) { |
| //set the back reference |
| fetchGroupManager.setDescriptor(this); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the descriptor has a fetch group manager associated with. |
| */ |
| public boolean hasFetchGroupManager() { |
| return (fetchGroupManager != null); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean hasCascadeLockingPolicies() { |
| return (this.cascadeLockingPolicies != null) && !this.cascadeLockingPolicies.isEmpty(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the descriptor has a CMP policy. |
| */ |
| public boolean hasCMPPolicy() { |
| return (cmpPolicy != null); |
| } |
| |
| /** |
| * INTERNAL: |
| * |
| * Return the default fetch group on the descriptor. |
| * All read object and read all queries will use the default fetch group if |
| * no fetch group is explicitly defined for the query. |
| */ |
| public FetchGroup getDefaultFetchGroup() { |
| if (!hasFetchGroupManager()) { |
| //fetch group manager is not set, therefore no default fetch group. |
| return null; |
| } |
| return getFetchGroupManager().getDefaultFetchGroup(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Indicates if a return type is required for the field set on the |
| * returning policy. For relational descriptors, this should always |
| * return true. |
| */ |
| public boolean isReturnTypeRequiredForReturningPolicy() { |
| return true; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set if the descriptor requires usage of a native (unwrapped) JDBC connection. |
| * This may be required for some Oracle JDBC support when a wrapping DataSource is used. |
| */ |
| public void setIsNativeConnectionRequired(boolean isNativeConnectionRequired) { |
| this.isNativeConnectionRequired = isNativeConnectionRequired; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return if the descriptor requires usage of a native (unwrapped) JDBC connection. |
| * This may be required for some Oracle JDBC support when a wrapping DataSource is used. |
| */ |
| public boolean isNativeConnectionRequired() { |
| return isNativeConnectionRequired; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set what types are allowed as a primary key (id). |
| */ |
| public void setIdValidation(IdValidation idValidation) { |
| this.idValidation = idValidation; |
| if (getPrimaryKeyIdValidations() != null) { |
| for (int index = 0; index < getPrimaryKeyIdValidations().size(); index++) { |
| getPrimaryKeyIdValidations().set(index, idValidation); |
| } |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Return what types are allowed as a primary key (id). |
| */ |
| public IdValidation getIdValidation() { |
| return idValidation; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return what types are allowed in each primary key field (id). |
| */ |
| public List<IdValidation> getPrimaryKeyIdValidations() { |
| return primaryKeyIdValidations; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return what types are allowed in each primary key field (id). |
| */ |
| public void setPrimaryKeyIdValidations(List<IdValidation> primaryKeyIdValidations) { |
| this.primaryKeyIdValidations = primaryKeyIdValidations; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set what cache key type to use to store the object in the cache. |
| */ |
| public void setCacheKeyType(CacheKeyType cacheKeyType) { |
| getCachePolicy().setCacheKeyType(cacheKeyType); |
| } |
| |
| /** |
| * ADVANCED: |
| * Return what cache key type to use to store the object in the cache. |
| */ |
| public CacheKeyType getCacheKeyType() { |
| return getCachePolicy().getCacheKeyType(); |
| } |
| |
| /** |
| * A Default Query Redirector will be applied to any executing object query |
| * that does not have a more precise default (like the default |
| * ReadObjectQuery Redirector) or a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultQueryRedirector() { |
| return defaultQueryRedirector; |
| } |
| |
| /** |
| * A Default Query Redirector will be applied to any executing object query |
| * that does not have a more precise default (like the default |
| * ReadObjectQuery Redirector) or a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultQueryRedirector(QueryRedirector defaultRedirector) { |
| this.defaultQueryRedirector = defaultRedirector; |
| } |
| |
| /** |
| * A Default ReadAllQuery Redirector will be applied to any executing |
| * ReadAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultReadAllQueryRedirector() { |
| return defaultReadAllQueryRedirector; |
| } |
| |
| /** |
| * A Default ReadAllQuery Redirector will be applied to any executing |
| * ReadAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReadAllQueryRedirector( |
| QueryRedirector defaultReadAllQueryRedirector) { |
| this.defaultReadAllQueryRedirector = defaultReadAllQueryRedirector; |
| } |
| |
| /** |
| * A Default ReadObjectQuery Redirector will be applied to any executing |
| * ReadObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultReadObjectQueryRedirector() { |
| return defaultReadObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default ReadObjectQuery Redirector will be applied to any executing |
| * ReadObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReadObjectQueryRedirector( |
| QueryRedirector defaultReadObjectQueryRedirector) { |
| this.defaultReadObjectQueryRedirector = defaultReadObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default ReportQuery Redirector will be applied to any executing |
| * ReportQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultReportQueryRedirector() { |
| return defaultReportQueryRedirector; |
| } |
| |
| /** |
| * A Default ReportQuery Redirector will be applied to any executing |
| * ReportQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReportQueryRedirector( |
| QueryRedirector defaultReportQueryRedirector) { |
| this.defaultReportQueryRedirector = defaultReportQueryRedirector; |
| } |
| |
| /** |
| * A Default UpdateObjectQuery Redirector will be applied to any executing |
| * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultUpdateObjectQueryRedirector() { |
| return defaultUpdateObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default UpdateObjectQuery Redirector will be applied to any executing |
| * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultUpdateObjectQueryRedirector(QueryRedirector defaultUpdateQueryRedirector) { |
| this.defaultUpdateObjectQueryRedirector = defaultUpdateQueryRedirector; |
| } |
| |
| /** |
| * A Default InsertObjectQuery Redirector will be applied to any executing |
| * InsertObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultInsertObjectQueryRedirector() { |
| return defaultInsertObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default InsertObjectQuery Redirector will be applied to any executing |
| * InsertObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultInsertObjectQueryRedirector(QueryRedirector defaultInsertQueryRedirector) { |
| this.defaultInsertObjectQueryRedirector = defaultInsertQueryRedirector; |
| } |
| |
| /** |
| * A Default DeleteObjectQuery Redirector will be applied to any executing |
| * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public QueryRedirector getDefaultDeleteObjectQueryRedirector() { |
| return defaultDeleteObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default DeleteObjectQuery Redirector will be applied to any executing |
| * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultDeleteObjectQueryRedirector(QueryRedirector defaultDeleteObjectQueryRedirector) { |
| this.defaultDeleteObjectQueryRedirector = defaultDeleteObjectQueryRedirector; |
| } |
| |
| /** |
| * A Default Query Redirector will be applied to any executing object query |
| * that does not have a more precise default (like the default |
| * ReadObjectQuery Redirector) or a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultQueryRedirectorClassName(String defaultQueryRedirectorClassName) { |
| this.defaultQueryRedirectorClassName = defaultQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default ReadAllQuery Redirector will be applied to any executing |
| * ReadAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query exection preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReadAllQueryRedirectorClassName(String defaultReadAllQueryRedirectorClassName) { |
| this.defaultReadAllQueryRedirectorClassName = defaultReadAllQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default ReadObjectQuery Redirector will be applied to any executing |
| * ReadObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReadObjectQueryRedirectorClassName( |
| String defaultReadObjectQueryRedirectorClassName) { |
| this.defaultReadObjectQueryRedirectorClassName = defaultReadObjectQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default ReportQuery Redirector will be applied to any executing |
| * ReportQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultReportQueryRedirectorClassName( |
| String defaultReportQueryRedirectorClassName) { |
| this.defaultReportQueryRedirectorClassName = defaultReportQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default UpdateObjectQuery Redirector will be applied to any executing |
| * UpdateObjectQuery or UpdateAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query execution preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultUpdateObjectQueryRedirectorClassName( |
| String defaultUpdateObjectQueryRedirectorClassName) { |
| this.defaultUpdateObjectQueryRedirectorClassName = defaultUpdateObjectQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default InsertObjectQuery Redirector will be applied to any executing |
| * InsertObjectQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query exection preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultInsertObjectQueryRedirectorClassName( |
| String defaultInsertObjectQueryRedirectorClassName) { |
| this.defaultInsertObjectQueryRedirectorClassName = defaultInsertObjectQueryRedirectorClassName; |
| } |
| |
| /** |
| * A Default DeleteObjectQuery Redirector will be applied to any executing |
| * DeleteObjectQuery or DeleteAllQuery that does not have a redirector set directly on the query. |
| * Query redirectors allow the user to intercept query exection preventing |
| * it or alternately performing some side effect like auditing. |
| * |
| * @see org.eclipse.persistence.queries.QueryRedirector |
| */ |
| public void setDefaultDeleteObjectQueryRedirectorClassName( |
| String defaultDeleteObjectQueryRedirectorClassName) { |
| this.defaultDeleteObjectQueryRedirectorClassName = defaultDeleteObjectQueryRedirectorClassName; |
| } |
| |
| /** |
| * Return the descriptor's sequence. |
| * This is normally set when the descriptor is initialized. |
| */ |
| public Sequence getSequence() { |
| return sequence; |
| } |
| |
| /** |
| * Set the descriptor's sequence. |
| * This is normally set when the descriptor is initialized. |
| */ |
| public void setSequence(Sequence sequence) { |
| this.sequence = sequence; |
| } |
| |
| /** |
| * Mappings that require postCalculateChanges method to be called |
| */ |
| public List<DatabaseMapping> getMappingsPostCalculateChanges() { |
| if(mappingsPostCalculateChanges == null) { |
| mappingsPostCalculateChanges = new ArrayList<>(); |
| } |
| return mappingsPostCalculateChanges; |
| } |
| |
| /** |
| * Are there any mappings that require postCalculateChanges method to be called. |
| */ |
| public boolean hasMappingsPostCalculateChanges() { |
| return mappingsPostCalculateChanges != null; |
| } |
| |
| /** |
| * Add a mapping to the list of mappings that require postCalculateChanges method to be called. |
| */ |
| public void addMappingsPostCalculateChanges(DatabaseMapping mapping) { |
| //474752 :ReferenceDescriptor may not be available during |
| //predeploy. It is required for calculating changes. |
| if (mapping.getReferenceDescriptor() != null) { |
| getMappingsPostCalculateChanges().add(mapping); |
| } |
| } |
| |
| /** |
| * Mappings that require mappingsPostCalculateChangesOnDeleted method to be called |
| */ |
| public List<DatabaseMapping> getMappingsPostCalculateChangesOnDeleted() { |
| if (mappingsPostCalculateChangesOnDeleted == null) { |
| mappingsPostCalculateChangesOnDeleted = new ArrayList<>(); |
| } |
| return mappingsPostCalculateChangesOnDeleted; |
| } |
| |
| /** |
| * Are there any mappings that require mappingsPostCalculateChangesOnDeleted method to be called. |
| */ |
| public boolean hasMappingsPostCalculateChangesOnDeleted() { |
| return mappingsPostCalculateChangesOnDeleted != null; |
| } |
| |
| /** |
| * Add a mapping to the list of mappings that require mappingsPostCalculateChangesOnDeleted method to be called. |
| */ |
| public void addMappingsPostCalculateChangesOnDeleted(DatabaseMapping mapping) { |
| getMappingsPostCalculateChangesOnDeleted().add(mapping); |
| } |
| |
| /** |
| * Return if any mapping reference a field in a secondary table. |
| * This is used to disable deferring multiple table writes. |
| */ |
| public boolean hasMultipleTableConstraintDependecy() { |
| return hasMultipleTableConstraintDependecy; |
| } |
| |
| /** |
| * Return true if the descriptor has a multitenant policy |
| */ |
| public boolean hasMultitenantPolicy() { |
| return multitenantPolicy != null; |
| } |
| /** |
| * PUBLIC |
| * @return true if this descriptor is configured with a table per tenant policy. |
| */ |
| public boolean hasTablePerMultitenantPolicy() { |
| return hasMultitenantPolicy() && getMultitenantPolicy().isTablePerMultitenantPolicy(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to store un-converted properties, which are subsequenctly converted |
| * at runtime (through the convertClassNamesToClasses method. |
| */ |
| public boolean hasUnconvertedProperties() { |
| return unconvertedProperties != null; |
| } |
| |
| /** |
| * Set if any mapping reference a field in a secondary table. |
| * This is used to disable deferring multiple table writes. |
| */ |
| public void setHasMultipleTableConstraintDependecy(boolean hasMultipleTableConstraintDependecy) { |
| this.hasMultipleTableConstraintDependecy = hasMultipleTableConstraintDependecy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return whether this descriptor uses property access. This information is used to |
| * modify the behavior of some of our weaving features |
| */ |
| public boolean usesPropertyAccessForWeaving(){ |
| return weavingUsesPropertyAccess; |
| } |
| |
| |
| /** |
| * INTERNAL: |
| * Record that this descriptor uses property access. This information is used to |
| * modify the behavior of some of our weaving features |
| */ |
| public void usePropertyAccessForWeaving(){ |
| weavingUsesPropertyAccess = true; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the list of virtual methods sets for this Entity. |
| * This list is used to control which methods are weaved |
| **/ |
| public List<VirtualAttributeMethodInfo> getVirtualAttributeMethods() { |
| if (this.virtualAttributeMethods == null) { |
| this.virtualAttributeMethods = new ArrayList<>(); |
| } |
| return this.virtualAttributeMethods; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the list of methods used my mappings with virtual access |
| * this list is used to determine which methods to weave |
| */ |
| public void setVirtualAttributeMethods(List<VirtualAttributeMethodInfo> virtualAttributeMethods) { |
| this.virtualAttributeMethods = virtualAttributeMethods; |
| } |
| |
| /** |
| * INTERNAL: |
| * Indicates whether descriptor has at least one target foreign key mapping |
| */ |
| public boolean hasTargetForeignKeyMapping(AbstractSession session) { |
| for (DatabaseMapping mapping: getMappings()) { |
| if (mapping.isCollectionMapping() || |
| (mapping.isObjectReferenceMapping() && !((ObjectReferenceMapping)mapping).isForeignKeyRelationship()) || |
| mapping.isAbstractCompositeDirectCollectionMapping()) { |
| return true; |
| } else if (mapping.isAggregateObjectMapping()) { |
| ClassDescriptor referenceDescriptor = mapping.getReferenceDescriptor(); |
| if (referenceDescriptor == null) { |
| // the mapping has not been initialized yet |
| referenceDescriptor = session.getDescriptor(((AggregateObjectMapping)mapping).getReferenceClass()); |
| } |
| if (referenceDescriptor.hasTargetForeignKeyMapping(session)) { |
| return true; |
| } |
| } |
| } |
| return false; |
| } |
| |
| @Override |
| public AttributeGroup getAttributeGroup(String name) { |
| return super.getAttributeGroup(name); |
| } |
| |
| @Override |
| public Map<String, AttributeGroup> getAttributeGroups() { |
| return super.getAttributeGroups(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Cleans referencingClasses set. Called from ClientSession for proper cleanup and avoid memory leak. |
| */ |
| public void clearReferencingClasses() { |
| this.referencingClasses.clear(); |
| } |
| } |