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

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

The newest version!
/*
 * Copyright (c) 1998, 2024 Oracle and/or its affiliates. All rights reserved.
 * Copyright (c) 1998, 2024 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 org.eclipse.persistence.annotations.CacheKeyType;
import org.eclipse.persistence.annotations.IdValidation;
import org.eclipse.persistence.annotations.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.copying.RecordCopyPolicy;
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.RecordInstantiationPolicy;
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.identitymaps.IdentityMap;
import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
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;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
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;

/**
 * 

Purpose: * 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, ObjectBuilder> implements Cloneable, Serializable { protected Class javaClass; protected String javaClassName; protected List tables; protected transient DatabaseTable defaultTable; protected List primaryKeyFields; protected Map> additionalTablePrimaryKeyFields; protected transient List multipleTableInsertOrder; protected Map> multipleTableForeignKeys; /** Support delete cascading on the database for multiple and inheritance tables. */ protected boolean isCascadeOnDeleteSetOnDatabaseOnSecondaryTables; protected transient List fields; protected transient List allFields; protected transient List selectionFields; protected transient List allSelectionFields; protected transient List returnFieldsToGenerateInsert; protected transient List returnFieldsToGenerateUpdate; protected transient List returnFieldsToMergeInsert; protected transient List returnFieldsToMergeUpdate; protected List mappings; //Used to track which other classes reference this class in cases where // the referencing classes need to be notified of something. protected Set referencingClasses; //used by the lock on clone process. This will contain the foreign reference //mapping without indirection protected List lockableMappings; protected Map queryKeys; // Additional properties. protected String sequenceNumberName; protected DatabaseField sequenceNumberField; protected transient String sessionName; protected transient List> 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 cascadeLockingPolicies; protected WrapperPolicy wrapperPolicy; protected ObjectChangePolicy changePolicy; protected ReturningPolicy returningPolicy; protected List 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> 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 primaryKeyIdValidations; // JPA 2.0 Derived identities - map of mappings that act as derived ids protected Map 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 mappingsPostCalculateChanges; /** Mappings that require postCalculateChangesOnDeleted method to be called */ protected List 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 additionalAggregateCollectionKeyFields; /** stores a list of mappings that require preDelete as a group prior to the delete individually */ protected List preDeleteMappings; /** stores fields that are written by Map key mappings so they can be checked for multiple writable mappings */ protected transient List 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 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 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 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 = new ArrayList<>(3); this.mappings = new ArrayList<>(); this.primaryKeyFields = new ArrayList<>(2); this.fields = new ArrayList<>(); this.allFields = new ArrayList<>(); this.constraintDependencies = new ArrayList<>(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 sourceTables = getMultipleTableForeignKeys().computeIfAbsent(targetPrimaryKeyField.getTable(), k -> new HashSet<>(3)); 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 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 valuePair = new ArrayList<>(2); valuePair.add(propertyValue); valuePair.add(propertyType); getUnconvertedProperties().put(propertyName, valuePair); } /** * INTERNAL: * Adjust the order of the tables in the multipleTableInsertOrder List 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 true: * it configures a ClassDescriptor to always refresh the cache if data is received from the database by any query.

* * 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.

* * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.

* * 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 true: * it configures a ClassDescriptor 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.

* * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.

* * 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; final Method lambdaMethod = method; PrivilegedAccessHelper.callDoPrivilegedWithException( () -> PrivilegedAccessHelper.invokeMethod(lambdaMethod, null, args), (ex) -> DescriptorException.errorOccuredInAmendmentMethod(getAmendmentClass(), getAmendmentMethodName(), ex, this) ); } /** * INTERNAL: * Used to determine if a foreign key references the primary key. */ public boolean arePrimaryKeyFields(List fields) { if (!(fields.size() == (getPrimaryKeyFields().size()))) { return false; } for (DatabaseField field: fields) { 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 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 list. */ public List 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 List 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 iterator = getTables().iterator(); iterator.hasNext();) { DatabaseTable table = iterator.next(); if (session.getIntegrityChecker().checkTable(table, session)) { // To load the fields of database into a list List databaseFields = new ArrayList<>(); List result = session.getAccessor().getColumnInfo(table.getName(), null, session); // Table name may need to be lowercase. if (result.isEmpty() && session.getPlatform().shouldForceFieldNamesToUpperCase()) { result = session.getAccessor().getColumnInfo(table.getName().toLowerCase(), null, session); } for (Iterator resultIterator = result.iterator(); resultIterator.hasNext();) { AbstractRecord row = resultIterator.next(); if (session.getPlatform().shouldForceFieldNamesToUpperCase()) { databaseFields.add(((String)row.get("COLUMN_NAME")).toUpperCase()); } else { databaseFields.add((String)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). */ @SuppressWarnings({"unchecked"}) 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((List) ((ArrayList)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 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>> itTargetTables = getMultipleTableForeignKeys().entrySet().iterator(); while(itTargetTables.hasNext()) { Map.Entry> entry = itTargetTables.next(); DatabaseTable targetTable = entry.getKey(); if (!getTables().contains(targetTable)) { throw DescriptorException.illegalTableNameInMultipleTableForeignKeyField(this, targetTable); } Iterator itSourceTables = entry.getValue().iterator(); while(itSourceTables.hasNext()) { DatabaseTable sourceTable = itSourceTables.next(); if (!getTables().contains(sourceTable)) { 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 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>> 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> entry = itTargetTables.next(); DatabaseTable targetTable = entry.getKey(); int targetIndex = tables.indexOf(targetTable) - nStart; if(targetIndex >= 0) { Set sourceTables = entry.getValue(); Iterator 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(new ArrayList<>(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 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() { // clones itself try { ClassDescriptor clonedDescriptor = (ClassDescriptor) super.clone(); List mappingsList = new ArrayList<>(); // All the mappings for (DatabaseMapping m: getMappings()) { DatabaseMapping mapping = (DatabaseMapping) m.clone(); mapping.setDescriptor(clonedDescriptor); mappingsList.add(mapping); } clonedDescriptor.setMappings(mappingsList); 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 primaryKeyList = new ArrayList<>(getPrimaryKeyFields().size()); List primaryKeyFields = getPrimaryKeyFields(); for (int index = 0; index < primaryKeyFields.size(); index++) { DatabaseField primaryKey = primaryKeyFields.get(index).clone(); primaryKeyList.add(primaryKey); } clonedDescriptor.setPrimaryKeyFields(primaryKeyList); // fields. clonedDescriptor.setFields(new ArrayList<>()); // 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 List tables = new ArrayList<>(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; } catch (CloneNotSupportedException exception) { throw new AssertionError(exception); } } /** * 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 (getJavaClass() == null && getJavaClassName() != null) { final Class descriptorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getJavaClassName(), true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(getJavaClassName(), ex) ); setJavaClass(descriptorClass); } if (getAmendmentClass() == null && getAmendmentClassName() != null) { final Class amendmentClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getAmendmentClassName(), true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(getAmendmentClassName(), ex) ); setAmendmentClass(amendmentClass); } if (copyPolicy == null && getCopyPolicyClassName() != null) { final Class copyPolicyClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getCopyPolicyClassName(), true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(getCopyPolicyClassName(), ex) ); final CopyPolicy newCopyPolicy = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> (CopyPolicy) org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(copyPolicyClass), (ex) -> ValidationException.reflectiveExceptionWhileCreatingClassInstance(getCopyPolicyClassName(), ex) ); setCopyPolicy(newCopyPolicy); } if (this.serializedObjectPolicy != null && this.serializedObjectPolicy instanceof SerializedObjectPolicyWrapper) { final String serializedObjectPolicyClassName = ((SerializedObjectPolicyWrapper)this.serializedObjectPolicy).getSerializedObjectPolicyClassName(); final Class serializedObjectPolicyClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(serializedObjectPolicyClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(serializedObjectPolicyClassName, ex) ); final SerializedObjectPolicy newSerializedObjectPolicy = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> (SerializedObjectPolicy)org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(serializedObjectPolicyClass), (ex) -> ValidationException.reflectiveExceptionWhileCreatingClassInstance(serializedObjectPolicyClassName, ex) ); newSerializedObjectPolicy.setField(this.serializedObjectPolicy.getField()); setSerializedObjectPolicy(newSerializedObjectPolicy); } //Create and set default QueryRedirector instances if (this.defaultQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, ex) ); setDefaultQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultQueryRedirectorClassName, ex) )); } if (this.defaultReadObjectQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReadObjectQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, ex) ); setDefaultReadObjectQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReadObjectQueryRedirectorClassName, ex) )); } if (this.defaultReadAllQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReadAllQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, ex) ); setDefaultReadAllQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReadAllQueryRedirectorClassName, ex) )); } if (this.defaultReportQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultReportQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, ex) ); setDefaultReportQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultReportQueryRedirectorClassName, ex) )); } if (this.defaultInsertObjectQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultInsertObjectQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, ex) ); setDefaultInsertObjectQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultInsertObjectQueryRedirectorClassName, ex) )); } if (this.defaultUpdateObjectQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultUpdateObjectQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, ex) ); setDefaultUpdateObjectQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultUpdateObjectQueryRedirectorClassName, ex) )); } if (this.defaultDeleteObjectQueryRedirectorClassName != null) { final Class redirectorClass = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(defaultDeleteObjectQueryRedirectorClassName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, ex) ); setDefaultDeleteObjectQueryRedirector((QueryRedirector) PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.newInstanceFromClass(redirectorClass), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(defaultDeleteObjectQueryRedirectorClassName, ex) )); } Iterator 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 valuePair = getUnconvertedProperties().get(propertyName); String value = valuePair.get(0); String valueTypeName = valuePair.get(1); Class valueType = String.class; if (valueTypeName != null) { // Have to initialize the valueType now valueType = PrivilegedAccessHelper.callDoPrivilegedWithException( () -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(valueTypeName, true, classLoader), (ex) -> ValidationException.classNotFoundWhileConvertingClassNames(valueTypeName, ex) ); } // 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 false: * it ensures that a ClassDescriptor 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 false: * it ensures that a ClassDescriptor 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 false: * it ensures that a ClassDescriptor 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 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> 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 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 = 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 List 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 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 getAllSelectionFields(ObjectLevelReadQuery query) { if (hasSerializedObjectPolicy() && query.shouldUseSerializedObjectPolicy()) { return this.serializedObjectPolicy.getAllSelectionFields(); } else { return allSelectionFields; } } /** * INTERNAL: * Return fields used to build insert statement. */ public List getReturnFieldsToGenerateInsert() { return this.returnFieldsToGenerateInsert; } /** * INTERNAL: * Return fields used to build update statement. */ public List getReturnFieldsToGenerateUpdate() { return this.returnFieldsToGenerateUpdate; } /** * INTERNAL: * Return fields used in to map into entity for insert. */ public List getReturnFieldsToMergeInsert() { return this.returnFieldsToMergeInsert; } /** * INTERNAL: * Return fields used in to map into entity for update. */ public List 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 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 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 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 List> getConstraintDependencies() { if (constraintDependencies == null) { constraintDependencies = new ArrayList<>(1); } return constraintDependencies; } /** * INTERNAL: * Returns the copy policy. */ public CopyPolicy getCopyPolicy() { // Lazy initialize for XML deployment. if (copyPolicy == null) { if (javaClass != null && javaClass.isRecord()) { setCopyPolicy(new RecordCopyPolicy()); } else { 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 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 List getFields() { if (fields == null) { fields = new ArrayList<>(); } return fields; } /** * INTERNAL: * Return all selection fields */ public List getSelectionFields() { return selectionFields; } /** * INTERNAL: * Return all selection fields */ public List getSelectionFields(ObjectLevelReadQuery query) { if (hasSerializedObjectPolicy() && query.shouldUseSerializedObjectPolicy()) { return this.serializedObjectPolicy.getSelectionFields(); } else { return selectionFields; } } /** * INTERNAL: */ public Set 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) { if (javaClass != null && javaClass.isRecord()) { setInstantiationPolicy(new RecordInstantiationPolicy(javaClass)); } else { 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 @SuppressWarnings({"unchecked"}) public Class getJavaClass() { return (Class) 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 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 (DatabaseMapping mapping: mappings) { 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 List 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 List getMultipleTableForeignKeyAssociations() { List associations = new ArrayList<>(getAdditionalTablePrimaryKeyFields().size() * 2); Iterator> tablesHashtable = getAdditionalTablePrimaryKeyFields().values().iterator(); while (tablesHashtable.hasNext()) { Map tableHash = tablesHashtable.next(); Iterator 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.add(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> 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 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 List getMultipleTablePrimaryKeyAssociations() { List associations = new ArrayList<>(getAdditionalTablePrimaryKeyFields().size() * 2); Iterator> tablesHashtable = getAdditionalTablePrimaryKeyFields().values().iterator(); while (tablesHashtable.hasNext()) { Map tableHash = tablesHashtable.next(); Iterator 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.add(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 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 List getPrimaryKeyFieldNames() { List result = new ArrayList<>(getPrimaryKeyFields().size()); List primaryKeyFields = getPrimaryKeyFields(); for (int index = 0; index < primaryKeyFields.size(); index++) { result.add(primaryKeyFields.get(index).getQualifiedName()); } return result; } /** * INTERNAL: * Return all the primary key fields */ @Override public List 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 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 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 (Iterator tables = getTables().iterator(); tables.hasNext();) { DatabaseTable table = tables.next(); 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 List getTableNames() { List tableNames = new ArrayList<>(getTables().size()); for (DatabaseTable table: getTables()) { tableNames.add(table.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 List 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 (DatabaseMapping mapping: getMappings()) { 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 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()) { List 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 = new ArrayList<>(mappingsArray.length); mappings.addAll(Arrays.asList(mappingsArray)); 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.addAllUniqueToList(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 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()) { List 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 = new ArrayList<>(mappingsArray.length); mappings.addAll(Arrays.asList(mappingsArray)); setMappings(mappings); } // Initialize the allFields to its fields, this can be done now because the fields have been computed. setAllFields((List) ((ArrayList) 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 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 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); } ArrayList pkFields = (ArrayList) getPrimaryKeyFields(); if (!pkFields.isEmpty()) { @SuppressWarnings({"unchecked"}) List primaryKeyFields = (List) (pkFields.clone()); // Remove non-default table primary key (MW used to set these as pk). for (int index = 0; index < primaryKeyFields.size(); index++) { DatabaseField primaryKey = 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> 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 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 true: * it configures a ClassDescriptor 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: * *

    *
  • the ClassDescriptor was configured by calling {@link #alwaysRefreshCache} or {@link #alwaysRefreshCacheOnRemote},
  • *
  • the query was configured by calling {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}, or
  • *
  • the query was a call to {@link org.eclipse.persistence.sessions.Session#refreshObject}
  • *
*

* * 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.

* * 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.isAggregateObjectMapping())){ 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 sopSelectionFields = null; if (hasSerializedObjectPolicy()) { getSerializedObjectPolicy().postInitialize(session); this.selectionFields = (List) ((ArrayList) getFields()).clone(); this.selectionFields.remove(getSerializedObjectPolicy().getField()); this.allSelectionFields = (List) ((ArrayList) 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().get(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 returningPolicies = new ArrayList<>(); if (this.hasReturningPolicy()) { returningPolicies.add(this.getReturningPolicy()); } browseReturningPolicies(returningPolicies, this.getMappings()); if (!returningPolicies.isEmpty()) { this.returningPolicies = returningPolicies; prepareReturnFields(returningPolicies); } } private void browseReturningPolicies(List returningPolicies, List mappings) { for (DatabaseMapping databaseMapping :mappings) { if (databaseMapping.isAggregateObjectMapping()) { ClassDescriptor referenceDescriptor = databaseMapping.getReferenceDescriptor(); if (referenceDescriptor != null) { browseReturningPolicies(returningPolicies, referenceDescriptor.getMappings()); if (referenceDescriptor.hasReturningPolicy()) { returningPolicies.add(referenceDescriptor.getReturningPolicy()); } } } } } private void prepareReturnFields(List returningPolicies) { List returnFieldsInsert = new ArrayList<>(); List returnFieldsUpdate = new ArrayList<>(); List returnFieldsToMergeInsert = new ArrayList<>(); List 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 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) { if (javaClass != null && javaClass.isRecord()) { setCopyPolicy(new RecordCopyPolicy()); } else { setCopyPolicy(new PersistenceEntityCopyPolicy()); } } if (!isAbstract()) { try { if (this.instantiationPolicy == null) { if (javaClass != null && javaClass.isRecord()) { setInstantiationPolicy(new RecordInstantiationPolicy(javaClass)); } else { setInstantiationPolicy(new PersistenceObjectInstantiationPolicy((PersistenceObject)getJavaClass().getConstructor().newInstance())); } } } catch (Exception ignore) { } } } } // 4924665 Check for spaces in table names, and add the appropriate quote character Iterator 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> 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 (DatabaseMapping mapping: getMappings()) { mapping.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 ((ClassConstants.NoIdentityMap_Class.equals(getIdentityMapClass())) && (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 tableAdditionalPKFields = getAdditionalTablePrimaryKeyFields().computeIfAbsent(table, k -> new HashMap<>(2)); 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> additionalTablePrimaryKeyFieldsOld = additionalTablePrimaryKeyFields; // nullify the original map variable - it will be re-created from scratch additionalTablePrimaryKeyFields = null; Iterator>> 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> entryTable = itTable.next(); DatabaseTable sourceTable = entryTable.getKey(); boolean isSourceProcessed = getTables().indexOf(sourceTable) < nProcessedTables; int sourceInsertOrderIndex = getMultipleTableInsertOrder().indexOf(sourceTable); Map targetTableAdditionalPKFields = entryTable.getValue(); Iterator> itField = targetTableAdditionalPKFields.entrySet().iterator(); while(itField.hasNext()) { Map.Entry 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> additionalTablePrimaryKeyFields) { this.additionalTablePrimaryKeyFields = additionalTablePrimaryKeyFields; } /** * PUBLIC: * Set the alias */ public void setAlias(String alias) { this.alias = alias; } /** * INTERNAL: * Set all the fields. */ protected void setAllFields(List 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 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(List> 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) { switch (value) { case "Aggregate collection" -> descriptorIsAggregateCollection(); case "Aggregate" -> descriptorIsAggregate(); case "Interface" -> descriptorIsForInterface(); default -> 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(List fields) { this.fields = fields; } /** * INTERNAL: * This method is used by the XML Deployment ClassDescriptor to read and write these mappings */ public void setForeignKeyFieldNamesForMultipleTable(List 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 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 null 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> 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. *

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 lockableMappings) { this.lockableMappings = lockableMappings; } /** * INTERNAL: * Set the mappings. */ public void setMappings(List mappings) { // This is used from XML reader so must ensure that all mapping's descriptor has been set. for (DatabaseMapping mapping: mappings) { // 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> 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 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 list of all the primary key field names if primary key is composite. * * @see #addPrimaryKeyFieldName(String) */ @Override public void setPrimaryKeyFieldNames(List primaryKeyFieldsName) { setPrimaryKeyFields(new ArrayList<>(primaryKeyFieldsName.size())); for (String fieldName: primaryKeyFieldsName) { addPrimaryKeyFieldName(fieldName); } } /** * INTERNAL: * Set the primary key fields */ @Override public void setPrimaryKeyFields(List 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 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 shouldAlwaysRefreshCache argument passed into this method is true, * this method configures a ClassDescriptor to always refresh the cache if data is received from * the database by any query.

* * 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.

* * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.

* * 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}.

* * When the shouldAlwaysRefreshCache argument passed into this method is false, this method * ensures that a ClassDescriptor 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 shouldAlwaysRefreshCacheOnRemote argument passed into this method is true, * this method configures a ClassDescriptor 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.

* * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.

* * 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}.

* * When the shouldAlwaysRefreshCacheOnRemote argument passed into this method is false, * this method ensures that a ClassDescriptor 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 shouldOnlyRefreshCacheIfNewerVersion argument passed into this method is true, * this method configures a ClassDescriptor 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: * *

    *
  • the ClassDescriptor was configured by calling {@link #alwaysRefreshCache} or {@link #alwaysRefreshCacheOnRemote},
  • *
  • the query was configured by calling {@link org.eclipse.persistence.queries.ObjectLevelReadQuery#refreshIdentityMapResult}, or
  • *
  • the query was a call to {@link org.eclipse.persistence.sessions.Session#refreshObject}
  • *
*

* * 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.

* * Also note that the {@link org.eclipse.persistence.sessions.UnitOfWork} will not refresh its registered objects.

* * When the shouldOnlyRefreshCacheIfNewerVersion argument passed into this method is false, this method * ensures that a ClassDescriptor 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(List tableNames) { setTables(new ArrayList<>(tableNames.size())); for (String name: tableNames) { addTableName(name); } } /** * 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 (Iterator enumtr = getTables().iterator(); enumtr.hasNext();) { DatabaseTable table = enumtr.next(); table.setTableQualifier(tableQualifier); } } /** * INTERNAL: * Sets the tables */ public void setTables(List 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 true if the ClassDescriptor is configured to always refresh * the cache if data is received from the database by any query. Otherwise, it returns false. * * @see #setShouldAlwaysRefreshCache */ public boolean shouldAlwaysRefreshCache() { return getCachePolicy().shouldAlwaysRefreshCache(); } /** * PUBLIC: * This method returns true if the ClassDescriptor 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 false. * * @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 true if the ClassDescriptor 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 false. * * @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 ClassConstants.CacheIdentityMap_Class.equals(getIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using FullIdentityMap */ public boolean shouldUseFullIdentityMap() { return ClassConstants.FullIdentityMap_Class.equals(getIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using SoftIdentityMap */ public boolean shouldUseSoftIdentityMap() { return ClassConstants.SoftIdentityMap_Class.equals(getIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using SoftIdentityMap */ public boolean shouldUseRemoteSoftIdentityMap() { return ClassConstants.SoftIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using HardCacheWeakIdentityMap. */ public boolean shouldUseHardCacheWeakIdentityMap() { return ClassConstants.HardCacheWeakIdentityMap_Class.equals(getIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using NoIdentityMap */ public boolean shouldUseNoIdentityMap() { return ClassConstants.NoIdentityMap_Class.equals(getIdentityMapClass()); } /** * 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. *

Extreme care should be taken in using this feature, for a user will * get back a mix of registered and original (unregistered) objects. *

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 ObjectLevelReadQuery#shouldRegisterResultsInUnitOfWork() */ public boolean shouldRegisterResultsInUnitOfWork() { // bug 2612601 return shouldRegisterResultsInUnitOfWork; } /** * PUBLIC: * Return true if this descriptor is using CacheIdentityMap */ public boolean shouldUseRemoteCacheIdentityMap() { return ClassConstants.CacheIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using FullIdentityMap */ public boolean shouldUseRemoteFullIdentityMap() { return ClassConstants.FullIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using HardCacheWeakIdentityMap */ public boolean shouldUseRemoteHardCacheWeakIdentityMap() { return ClassConstants.HardCacheWeakIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using NoIdentityMap */ public boolean shouldUseRemoteNoIdentityMap() { return ClassConstants.NoIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using SoftCacheWeakIdentityMap */ public boolean shouldUseRemoteSoftCacheWeakIdentityMap() { return ClassConstants.SoftCacheWeakIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using WeakIdentityMap */ public boolean shouldUseRemoteWeakIdentityMap() { return ClassConstants.WeakIdentityMap_Class.equals(getRemoteIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using SoftCacheWeakIdentityMap. */ public boolean shouldUseSoftCacheWeakIdentityMap() { return ClassConstants.SoftCacheWeakIdentityMap_Class.equals(getIdentityMapClass()); } /** * PUBLIC: * Return true if this descriptor is using WeakIdentityMap */ public boolean shouldUseWeakIdentityMap() { return ClassConstants.WeakIdentityMap_Class.equals(getIdentityMapClass()); } /** * 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 instanceof FieldsLockingPolicy) { return false; } List mappings = getMappings(); for (Iterator 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 getClass().getSimpleName() + "(" + 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(List 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.isEmpty()) { return; } for (DatabaseTable table : getTables()) { if (table.getTableQualifier().isEmpty()) { 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 getPrimaryKeyIdValidations() { return primaryKeyIdValidations; } /** * ADVANCED: * Return what types are allowed in each primary key field (id). */ public void setPrimaryKeyIdValidations(List 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 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 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 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 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 getAttributeGroups() { return super.getAttributeGroups(); } /** * INTERNAL: * Cleans referencingClasses set. Called from ClientSession for proper cleanup and avoid memory leak. */ public void clearReferencingClasses() { this.referencingClasses.clear(); } }