Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 1998, 2022 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2022 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// stardif - updates for Cascaded locking and inheritance
// 02/20/2009-1.1 Guy Pelletier
// - 259829: TABLE_PER_CLASS with abstract classes does not work
// 10/15/2010-2.2 Guy Pelletier
// - 322008: Improve usability of additional criteria applied to queries at the session/EM
// 04/01/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 2)
// 04/05/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 3)
// 04/21/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 5)
// 09/09/2011-2.3.1 Guy Pelletier
// - 356197: Add new VPD type to MultitenantType
// 11/10/2011-2.4 Guy Pelletier
// - 357474: Address primaryKey option from tenant discriminator column
// 14/05/2012-2.4 Guy Pelletier
// - 376603: Provide for table per tenant support for multitenant applications
// 30/05/2012-2.4 Guy Pelletier
// - 354678: Temp classloader is still being used during metadata processing
// 09 Jan 2013-2.5 Gordon Yorke
// - 397772: JPA 2.1 Entity Graph Support
// 06/25/2014-2.5.2 Rick Curtis
// - 438177: Support M2M map with jointable
// 08/12/2015-2.6 Mythily Parthasarathy
// - 474752: Address NPE for Embeddable with 1-M association
// 07/09/2018-2.6 Jody Grassel
// - 536853: MapsID processing sets up to fail validation
package org.eclipse.persistence.descriptors;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.eclipse.persistence.annotations.CacheKeyType;
import org.eclipse.persistence.annotations.IdValidation;
import org.eclipse.persistence.config.CacheIsolationType;
import org.eclipse.persistence.core.descriptors.CoreDescriptor;
import org.eclipse.persistence.descriptors.changetracking.AttributeChangeTrackingPolicy;
import org.eclipse.persistence.descriptors.changetracking.ChangeTracker;
import org.eclipse.persistence.descriptors.changetracking.DeferredChangeDetectionPolicy;
import org.eclipse.persistence.descriptors.changetracking.ObjectChangePolicy;
import org.eclipse.persistence.descriptors.copying.CloneCopyPolicy;
import org.eclipse.persistence.descriptors.copying.CopyPolicy;
import org.eclipse.persistence.descriptors.copying.InstantiationCopyPolicy;
import org.eclipse.persistence.descriptors.copying.PersistenceEntityCopyPolicy;
import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.invalidation.NoExpiryCacheInvalidationPolicy;
import org.eclipse.persistence.descriptors.partitioning.PartitioningPolicy;
import org.eclipse.persistence.exceptions.DatabaseException;
import org.eclipse.persistence.exceptions.DescriptorException;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.expressions.Expression;
import org.eclipse.persistence.expressions.ExpressionBuilder;
import org.eclipse.persistence.history.HistoryPolicy;
import org.eclipse.persistence.internal.databaseaccess.DatabaseCall;
import org.eclipse.persistence.internal.databaseaccess.DatasourceCall;
import org.eclipse.persistence.internal.databaseaccess.Platform;
import org.eclipse.persistence.internal.descriptors.CascadeLockingPolicy;
import org.eclipse.persistence.internal.descriptors.InstantiationPolicy;
import org.eclipse.persistence.internal.descriptors.ObjectBuilder;
import org.eclipse.persistence.internal.descriptors.OptimisticLockingPolicy;
import org.eclipse.persistence.internal.descriptors.PersistenceObject;
import org.eclipse.persistence.internal.descriptors.PersistenceObjectAttributeAccessor;
import org.eclipse.persistence.internal.descriptors.PersistenceObjectInstantiationPolicy;
import org.eclipse.persistence.internal.descriptors.SerializedObjectPolicyWrapper;
import org.eclipse.persistence.internal.descriptors.VirtualAttributeMethodInfo;
import org.eclipse.persistence.internal.dynamic.DynamicEntityImpl;
import org.eclipse.persistence.internal.expressions.SQLSelectStatement;
import org.eclipse.persistence.internal.expressions.SQLStatement;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.internal.helper.DatabaseTable;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.helper.MappingCompare;
import org.eclipse.persistence.internal.helper.NonSynchronizedVector;
import org.eclipse.persistence.internal.identitymaps.IdentityMap;
import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy;
import org.eclipse.persistence.internal.security.PrivilegedAccessHelper;
import org.eclipse.persistence.internal.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;
/**
*
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 implements Cloneable, Serializable {
protected Class> javaClass;
protected String javaClassName;
protected Vector 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 Vector fields;
protected transient Vector allFields;
protected transient List selectionFields;
protected transient List allSelectionFields;
protected transient Vector returnFieldsToGenerateInsert;
protected transient Vector returnFieldsToGenerateUpdate;
protected transient List returnFieldsToMergeInsert;
protected transient List returnFieldsToMergeUpdate;
protected Vector 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 Vector constraintDependencies;
protected transient String amendmentMethodName;
protected transient Class> amendmentClass;
protected String amendmentClassName;
protected String alias;
protected boolean shouldBeReadOnly;
protected boolean shouldAlwaysConformResultsInUnitOfWork;
// for bug 2612601 allow ability not to register results in UOW.
protected boolean shouldRegisterResultsInUnitOfWork = true;
// Delegation objects, these perform most of the behavior.
protected DescriptorQueryManager queryManager;
protected CopyPolicy copyPolicy;
protected String copyPolicyClassName;
protected InterfacePolicy interfacePolicy;
protected OptimisticLockingPolicy optimisticLockingPolicy;
protected transient List 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 = NonSynchronizedVector.newInstance(3);
this.mappings = NonSynchronizedVector.newInstance();
this.primaryKeyFields = new ArrayList<>(2);
this.fields = NonSynchronizedVector.newInstance();
this.allFields = NonSynchronizedVector.newInstance();
this.constraintDependencies = NonSynchronizedVector.newInstance(2);
this.multipleTableForeignKeys = new HashMap<>(5);
this.queryKeys = new HashMap<>(5);
this.initializationStage = UNINITIALIZED;
this.interfaceInitializationStage = UNINITIALIZED;
this.descriptorType = NORMAL;
this.shouldOrderMappings = true;
this.shouldBeReadOnly = false;
this.shouldAlwaysConformResultsInUnitOfWork = false;
this.shouldAcquireCascadedLocks = false;
this.hasSimplePrimaryKey = false;
this.derivesIdMappings = new HashMap<>(5);
this.referencingClasses = new HashSet<>();
// Policies
this.objectBuilder = new ObjectBuilder(this);
this.cachePolicy = new CachePolicy();
this.additionalWritableMapKeyFields = new ArrayList<>(2);
this.foreignKeyValuesForCaching = new HashSet<>();
}
/**
* PUBLIC:
* This method should only be used for interface descriptors. It
* adds an abstract query key to the interface descriptor. Any
* implementors of that interface must define the query key
* defined by this abstract query key.
*/
public void addAbstractQueryKey(String queryKeyName) {
QueryKey queryKey = new QueryKey();
queryKey.setName(queryKeyName);
addQueryKey(queryKey);
}
/**
* INTERNAL:
* Add the cascade locking policy to all children that have a relationship to this descriptor
* either by inheritance or by encapsulation/aggregation.
* @param policy - the CascadeLockingPolicy
*/
public void addCascadeLockingPolicy(CascadeLockingPolicy policy) {
getCascadeLockingPolicies().add(policy);
// 232608: propagate later version changes up to the locking policy on a parent branch by setting the policy on all children here
if (hasInheritance()) {
// InOrder traverse the entire [deep] tree, not just the next level
for (ClassDescriptor parent : getInheritancePolicy().getAllChildDescriptors()) {
// Set the same cascade locking policy on all descriptors that inherit from this descriptor.
parent.addCascadeLockingPolicy(policy);
}
}
// do not propagate an extra locking policy to other mappings, if this descriptor already
// has a cascaded optimistic locking policy that will be cascaded
if (!this.cascadedLockingInitialized) {
// never cascade locking until descriptor is initialized
if (isInitialized(INITIALIZED)) {
// Set cascade locking policies on privately owned children mappings.
for (DatabaseMapping mapping : getMappings()) {
prepareCascadeLockingPolicy(mapping);
}
this.cascadedLockingInitialized = true;
}
}
}
/**
* ADVANCED:
* EclipseLink automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings.
* In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint,
* this defines that this descriptor has a foreign key constraint to another class and must be inserted after
* instances of the other class.
*/
public void addConstraintDependencies(Class> dependencies) {
addConstraintDependency(dependencies);
}
/**
* ADVANCED:
* EclipseLink automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings.
* In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint,
* this defines that this descriptor has a foreign key constraint to another class and must be inserted after
* instances of the other class.
*/
public void addConstraintDependency(Class> dependencies) {
getConstraintDependencies().add(dependencies);
}
/**
* Return a new direct/basic mapping for this type of descriptor.
*/
public AbstractDirectMapping newDirectMapping() {
return new DirectToFieldMapping();
}
/**
* Return a new aggregate/embedded mapping for this type of descriptor.
*/
public AggregateMapping newAggregateMapping() {
return new AggregateObjectMapping();
}
/**
* Return a new aggregate collection/element collection mapping for this type of descriptor.
*/
public DatabaseMapping newAggregateCollectionMapping() {
return new AggregateCollectionMapping();
}
/**
* Return a new direct collection/element collection mapping for this type of descriptor.
*/
public DatabaseMapping newDirectCollectionMapping() {
return new DirectCollectionMapping();
}
/**
* Return a new one to one mapping for this type of descriptor.
*/
public ObjectReferenceMapping newOneToOneMapping() {
OneToOneMapping mapping = new OneToOneMapping();
mapping.setIsOneToOneRelationship(true);
return mapping;
}
/**
* Return a new many to one mapping for this type of descriptor.
*/
public ObjectReferenceMapping newManyToOneMapping() {
return new ManyToOneMapping();
}
/**
* Return a new one to many mapping for this type of descriptor.
*/
public CollectionMapping newOneToManyMapping() {
return new OneToManyMapping();
}
/**
* Return a new one to many mapping for this type of descriptor.
*/
public CollectionMapping newUnidirectionalOneToManyMapping() {
return new UnidirectionalOneToManyMapping();
}
/**
* Return a new one to many mapping for this type of descriptor.
*/
public CollectionMapping newManyToManyMapping() {
return new ManyToManyMapping();
}
/**
* PUBLIC:
* Add a direct to field mapping to the receiver. The new mapping specifies that
* an instance variable of the class of objects which the receiver describes maps in
* the default manner for its type to the indicated database field.
*
* @param attributeName is the name of an instance variable of the
* class which the receiver describes.
* @param fieldName is the name of the database column which corresponds
* with the designated instance variable.
* @return The newly created DatabaseMapping is returned.
*/
public DatabaseMapping addDirectMapping(String attributeName, String fieldName) {
AbstractDirectMapping mapping = newDirectMapping();
mapping.setAttributeName(attributeName);
mapping.setField(new DatabaseField(fieldName));
return addMapping(mapping);
}
/**
* PUBLIC:
* Add a direct to field mapping to the receiver. The new mapping specifies that
* a variable accessed by the get and set methods of the class of objects which
* the receiver describes maps in the default manner for its type to the indicated
* database field.
*/
public DatabaseMapping addDirectMapping(String attributeName, String getMethodName, String setMethodName, String fieldName) {
AbstractDirectMapping mapping = (AbstractDirectMapping)addDirectMapping(attributeName, fieldName);
mapping.setSetMethodName(setMethodName);
mapping.setGetMethodName(getMethodName);
return mapping;
}
/**
* PUBLIC:
* Add a query key to the descriptor. Query keys define Java aliases to database fields.
*/
public void addDirectQueryKey(String queryKeyName, String fieldName) {
DirectQueryKey queryKey = new DirectQueryKey();
DatabaseField field = new DatabaseField(fieldName);
queryKey.setName(queryKeyName);
queryKey.setField(field);
getQueryKeys().put(queryKeyName, queryKey);
}
/**
* PUBLIC:
* This protocol can be used to associate multiple tables with foreign key
* information. Use this method to associate secondary tables to a
* primary table. Specify the source foreign key field to the target
* primary key field. The join criteria will be generated based on the
* fields provided. Unless the customary insert order is specified by the user
* (using setMultipleTableInsertOrder method)
* the (automatically generated) table insert order will ensure that
* insert into target table happens before insert into the source table
* (there may be a foreign key constraint in the database that requires
* target table to be inserted before the source table).
*/
public void addForeignKeyFieldNameForMultipleTable(String sourceForeignKeyFieldName, String targetPrimaryKeyFieldName) throws DescriptorException {
addForeignKeyFieldForMultipleTable(new DatabaseField(sourceForeignKeyFieldName), new DatabaseField(targetPrimaryKeyFieldName));
}
/**
* PUBLIC:
* This protocol can be used to associate multiple tables with foreign key
* information. Use this method to associate secondary tables to a
* primary table. Specify the source foreign key field to the target
* primary key field. The join criteria will be generated based on the
* fields provided.
*/
public void addForeignKeyFieldForMultipleTable(DatabaseField sourceForeignKeyField, DatabaseField targetPrimaryKeyField) throws DescriptorException {
// Make sure that the table is fully qualified.
if ((!sourceForeignKeyField.hasTableName()) || (!targetPrimaryKeyField.hasTableName())) {
throw DescriptorException.multipleTablePrimaryKeyMustBeFullyQualified(this);
}
setAdditionalTablePrimaryKeyFields(sourceForeignKeyField.getTable(), targetPrimaryKeyField, sourceForeignKeyField);
Set sourceTables = getMultipleTableForeignKeys().get(targetPrimaryKeyField.getTable());
if(sourceTables == null) {
sourceTables = new HashSet<>(3);
getMultipleTableForeignKeys().put(targetPrimaryKeyField.getTable(), sourceTables);
}
sourceTables.add(sourceForeignKeyField.getTable());
}
/**
* PUBLIC:
* Add a database mapping to the receiver. Perform any required
* initialization of both the mapping and the receiving descriptor
* as a result of adding the new mapping.
*/
public DatabaseMapping addMapping(DatabaseMapping mapping) {
// For CR#2646, if the mapping already points to the parent descriptor then leave it.
if (mapping.getDescriptor() == null) {
mapping.setDescriptor(this);
}
getMappings().add(mapping);
return mapping;
}
protected void validateMappingType(DatabaseMapping mapping) {
if (!(mapping.isRelationalMapping())) {
throw DescriptorException.invalidMappingType(mapping);
}
}
/**
* PUBLIC:
* Specify the primary key field of the descriptors table.
* This should be called for each field that makes up the primary key of the table.
* If the descriptor has many tables, this must be the primary key in the first table,
* if the other tables have the same primary key nothing else is required, otherwise
* a primary key/foreign key field mapping must be provided for each of the other tables.
* @see #addForeignKeyFieldNameForMultipleTable(String, String)
*/
public void addPrimaryKeyFieldName(String fieldName) {
addPrimaryKeyField(new DatabaseField(fieldName));
}
/**
* ADVANCED:
* Specify the primary key field of the descriptors table.
* This should be called for each field that makes up the primary key of the table.
* This can be used for advanced field types, such as XML nodes, or to set the field type.
*/
public void addPrimaryKeyField(DatabaseField field) {
// Check if the pkFields List already contains a DatabaseField that is equal to the
// field we want to add, in order to avoid duplicates which will fail validation later.
List 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 Vector according to the FK
* relationship if one (or more) were previously specified. I.e. target of FK relationship should be inserted
* before source.
* If the multipleTableInsertOrder has been specified (presumably by the user) then do not change it.
*/
public void adjustMultipleTableInsertOrder() {
// Check if a user defined insert order was given.
if ((getMultipleTableInsertOrder() == null) || getMultipleTableInsertOrder().isEmpty()) {
createMultipleTableInsertOrder();
} else {
verifyMultipleTableInsertOrder();
}
toggleAdditionalTablePrimaryKeyFields();
}
/**
* PUBLIC:
* Used to set the descriptor to always conform in any unit of work query.
*
*/
public void alwaysConformResultsInUnitOfWork() {
setShouldAlwaysConformResultsInUnitOfWork(true);
}
/**
* PUBLIC:
* This method is the equivalent of calling {@link #setShouldAlwaysRefreshCache} with an argument of 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(Vector fields) {
if (!(fields.size() == (getPrimaryKeyFields().size()))) {
return false;
}
for (Enumeration enumFields = fields.elements(); enumFields.hasMoreElements();) {
DatabaseField field = (DatabaseField)enumFields.nextElement();
if (!getPrimaryKeyFields().contains(field)) {
return false;
}
}
return true;
}
/**
* INTERNAL:
* Some attributes have default values defined in Project.
* If such the value for the attribute hasn't been set then the default value is assigned.
*/
protected void assignDefaultValues(AbstractSession session) {
if (this.idValidation == null) {
this.idValidation = session.getProject().getDefaultIdValidation();
}
getCachePolicy().assignDefaultValues(session);
}
/**
* INTERNAL:
* Return the selection criteria used to IN batch fetching.
*/
public Expression buildBatchCriteriaByPK(ExpressionBuilder builder, ObjectLevelReadQuery query) {
int size = getPrimaryKeyFields().size();
if (size > 1) {
// Support composite keys using nested IN.
List fields = new ArrayList<>(size);
for (DatabaseField targetForeignKeyField : primaryKeyFields) {
fields.add(builder.getField(targetForeignKeyField));
}
return query.getSession().getPlatform().buildBatchCriteriaForComplexId(builder, fields);
} else {
return query.getSession().getPlatform().buildBatchCriteria(builder, builder.getField(primaryKeyFields.get(0)));
}
}
/**
* INTERNAL:
* Return a call built from a statement. Subclasses may throw an exception
* if the statement is not appropriate.
*/
public DatasourceCall buildCallFromStatement(SQLStatement statement, DatabaseQuery query, AbstractSession session) {
DatabaseCall call = statement.buildCall(session);
if (isNativeConnectionRequired()) {
call.setIsNativeConnectionRequired(true);
}
return call;
}
/**
* INTERNAL:
* Extract the direct values from the specified field value.
* Return them in a vector.
*/
public Vector buildDirectValuesFromFieldValue(Object fieldValue) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* A DatabaseField is built from the given field name.
*/
// * added 9/7/00 by Les Davis
// * bug fix for null pointer in initialization of mappings in remote session
public DatabaseField buildField(String fieldName) {
DatabaseField field = new DatabaseField(fieldName);
DatabaseTable table;
if (field.hasTableName()) {
table = getTable(field.getTableName());
} else if (getDefaultTable() != null) {
table = getDefaultTable();
} else {
table = getTable(getTableName());
}
field.setTable(table);
return field;
}
/**
* INTERNAL:
* The table of the field is ensured to be unique from the descriptor's tables.
* If the field has no table the default table is assigned.
* This is used only in initialization.
* Fields are ensured to be unique so if the field has already been built it is returned.
*/
public DatabaseField buildField(DatabaseField field) {
return buildField(field, null);
}
public DatabaseField buildField(DatabaseField field, DatabaseTable relationTable) {
DatabaseField builtField = getObjectBuilder().getFieldsMap().get(field);
if (builtField == null) {
builtField = field;
DatabaseTable table;
if (relationTable != null && field.hasTableName() && field.getTableName().equals(relationTable.getName())){
table = relationTable;
} else if (relationTable != null && !field.hasTableName()) {
table = relationTable;
} else if (field.hasTableName()) {
table = getTable(field.getTableName());
} else {
table = getDefaultTable();
}
builtField.setTable(table);
getObjectBuilder().getFieldsMap().put(builtField, builtField);
}
return builtField;
}
/**
* INTERNAL:
* Build the appropriate field value for the specified
* set of direct values.
*/
public Object buildFieldValueFromDirectValues(Vector directValues, String elementDataTypeName, AbstractSession session) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* Build and return the appropriate field value for the specified
* set of foreign keys (i.e. each row has the fields that
* make up a foreign key).
*/
public Object buildFieldValueFromForeignKeys(Vector foreignKeys, String referenceDataTypeName, AbstractSession session) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* Build and return the field value from the specified nested database row.
*/
public Object buildFieldValueFromNestedRow(AbstractRecord nestedRow, AbstractSession session) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* Build and return the appropriate field value for the specified
* set of nested rows.
*/
public Object buildFieldValueFromNestedRows(Vector nestedRows, String structureName, AbstractSession session) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* Build and return the nested database row from the specified field value.
*/
public AbstractRecord buildNestedRowFromFieldValue(Object fieldValue) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* INTERNAL:
* Build and return the nested rows from the specified field value.
*/
public Vector buildNestedRowsFromFieldValue(Object fieldValue, AbstractSession session) throws DatabaseException {
throw DescriptorException.normalDescriptorsDoNotSupportNonRelationalExtensions(this);
}
/**
* To check that tables and fields are present in database
*/
protected void checkDatabase(AbstractSession session) {
if (session.getIntegrityChecker().shouldCheckDatabase()) {
for (Iterator iterator = getTables().iterator(); iterator.hasNext();) {
DatabaseTable table = iterator.next();
if (session.getIntegrityChecker().checkTable(table, session)) {
// To load the fields of database into a vector
List databaseFields = new ArrayList();
List result = session.getAccessor().getColumnInfo(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(row.get("COLUMN_NAME"));
}
}
// To check that the fields of descriptor are present in the database.
for (DatabaseField field : getFields()) {
if (field.getTable().equals(table) && (!databaseFields.contains(field.getName()))) {
session.getIntegrityChecker().handleError(DescriptorException.fieldIsNotPresentInDatabase(this, table.getName(), field.getName()));
}
}
} else {
session.getIntegrityChecker().handleError(DescriptorException.tableIsNotPresentInDatabase(this));
}
}
}
}
/**
* INTERNAL:
* Verify that an aggregate descriptor's inheritance tree
* is full of aggregate descriptors.
*/
public void checkInheritanceTreeAggregateSettings(AbstractSession session, AggregateMapping mapping) throws DescriptorException {
if (!this.hasInheritance()) {
return;
}
if (this.isChildDescriptor()) {
Class> parentClass = this.getInheritancePolicy().getParentClass();
if (parentClass == this.getJavaClass()) {
throw DescriptorException.parentClassIsSelf(this);
}
// recurse up the inheritance tree to the root descriptor
session.getDescriptor(parentClass).checkInheritanceTreeAggregateSettings(session, mapping);
} else {
// we have a root descriptor, now verify it and all its children, grandchildren, etc.
this.checkInheritanceTreeAggregateSettingsForChildren(session, mapping);
}
}
/**
* Verify that an aggregate descriptor's inheritance tree
* is full of aggregate descriptors, cont.
*/
private void checkInheritanceTreeAggregateSettingsForChildren(AbstractSession session, AggregateMapping mapping) throws DescriptorException {
if (!this.isAggregateDescriptor()) {
session.getIntegrityChecker().handleError(DescriptorException.referenceDescriptorIsNotAggregate(this.getJavaClass().getName(), mapping));
}
for (ClassDescriptor childDescriptor : this.getInheritancePolicy().getChildDescriptors()) {
// recurse down the inheritance tree to its leaves
childDescriptor.checkInheritanceTreeAggregateSettingsForChildren(session, mapping);
}
}
/**
* INTERNAL:
* Create multiple table insert order.
* If its a child descriptor then insert order starts
* with the same insert order as in the parent.
* Non-inherited tables ordered to adhere to
* multipleTableForeignKeys:
* the target table (the key in multipleTableForeignKeys map)
* should stand in insert order before any of the source tables
* (members of the corresponding value in multipleTableForeignKeys).
*/
protected void createMultipleTableInsertOrder() {
int nParentTables = 0;
if (isChildDescriptor()) {
nParentTables = getInheritancePolicy().getParentDescriptor().getTables().size();
setMultipleTableInsertOrder(new ArrayList(getInheritancePolicy().getParentDescriptor().getMultipleTableInsertOrder()));
if(nParentTables == getTables().size()) {
// all the tables mapped by the parent - nothing to do.
return;
}
}
if(getMultipleTableForeignKeys().isEmpty()) {
if(nParentTables == 0) {
// no multipleTableForeignKeys specified - keep getTables() order.
setMultipleTableInsertOrder((Vector)getTables().clone());
} else {
// insert order for parent-defined tables has been already copied from parent descriptor,
// add the remaining tables keeping the same order as in getTables()
for(int k = nParentTables; k < getTables().size(); k++) {
getMultipleTableInsertOrder().add(getTables().get(k));
}
}
return;
}
verifyMultipleTablesForeignKeysTables();
// tableComparison[i][j] indicates the order between i and j tables:
// -1 i table before j table;
// +1 i table after j table;
// 0 - not defined (could be either before or after)
int[][] tableComparison = createTableComparison(getTables(), nParentTables);
// Now create insert order of the tables:
// getTables.get(i) table should be
// before getTable.get(j) in insert order if tableComparison[i][j]==-1;
// after getTable.get(j) in insert order if tableComparison[i][j]== 1;
// doesn't matter if tableComparison[i][j]== 0.
createMultipleTableInsertOrderFromComparison(tableComparison, nParentTables);
}
/**
* INTERNAL:
* Verify multiple table insert order provided by the user.
* If its a child descriptor then insert order starts
* with the same insert order as in the parent.
* Non-inherited tables ordered to adhere to
* multipleTableForeignKeys:
* the target table (the key in multipleTableForeignKeys map)
* should stand in insert order before any of the source tables
* (members of the corresponding value in multipleTableForeignKeys).
*/
protected void verifyMultipleTableInsertOrder() {
int nParentTables = 0;
if (isChildDescriptor()) {
nParentTables = getInheritancePolicy().getParentDescriptor().getTables().size();
if(nParentTables + getMultipleTableInsertOrder().size() == getTables().size()) {
// the user specified insert order only for the tables directly mapped by the descriptor,
// the inherited tables order must be the same as in parent descriptor
List 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().indexOf(targetTable) == -1) {
throw DescriptorException.illegalTableNameInMultipleTableForeignKeyField(this, targetTable);
}
Iterator itSourceTables = entry.getValue().iterator();
while(itSourceTables.hasNext()) {
DatabaseTable sourceTable = itSourceTables.next();
if (getTables().indexOf(sourceTable) == -1) {
throw DescriptorException.illegalTableNameInMultipleTableForeignKeyField(this, targetTable);
}
}
}
}
/**
* INTERNAL:
* This helper method creates a matrix that contains insertion order comparison for the tables.
* Comparison is done for indexes from nStart to tables.size()-1.
*/
protected int[][] createTableComparison(List 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(NonSynchronizedVector.newInstance(nTables));
}
for(int k=0; k < nTables - nStart; k++) {
getMultipleTableInsertOrder().add(getTables().get(tableOrder[k] + nStart));
}
} else {
throw DescriptorException.insertOrderCyclicalDependencyBetweenThreeOrMoreTables(this);
}
}
/**
* INTERNAL:
* This helper method recursively puts indexes from 0 to nTables-1 into tableOrder according to tableComparison 2 dim array.
* k is index in tableOrder that currently the method is working on - the method should be called with k = 0.
*/
protected boolean createTableOrder(int k, int nTables, int[] tableOrder, int[][] tableComparison) {
if(k == nTables) {
return true;
}
// array of indexes not yet ordered
int[] iAvailable = new int[nTables-k];
int l = 0;
for(int i=0; i < nTables; i++) {
boolean isUsed = false;
for(int j=0; j 0) {
isSmallest = false;
}
}
}
if(isSmallest) {
// iAvailable[i] is less or equal according to tableComparison to all other remaining indexes - let's try to use it as tableOrder[k]
tableOrder[k] = iAvailable[i];
// now try to fill out the last remaining n - k - 1 elements of tableOrder
bOk = createTableOrder(k + 1, nTables, tableOrder, tableComparison);
}
}
return bOk;
}
/**
* INTERNAL:
* Clones the descriptor
*/
@Override
public Object clone() {
ClassDescriptor clonedDescriptor = null;
// clones itself
try {
clonedDescriptor = (ClassDescriptor)super.clone();
} catch (Exception exception) {
throw new AssertionError(exception);
}
Vector mappingsVector = NonSynchronizedVector.newInstance();
// All the mappings
for (Enumeration mappingsEnum = getMappings().elements(); mappingsEnum.hasMoreElements();) {
DatabaseMapping mapping;
mapping = (DatabaseMapping) mappingsEnum.nextElement().clone();
mapping.setDescriptor(clonedDescriptor);
mappingsVector.addElement(mapping);
}
clonedDescriptor.setMappings(mappingsVector);
Map queryKeys = new HashMap(getQueryKeys().size() + 2);
// All the query keys
for (QueryKey queryKey : getQueryKeys().values()) {
queryKey = (QueryKey)queryKey.clone();
queryKey.setDescriptor(clonedDescriptor);
queryKeys.put(queryKey.getName(), queryKey);
}
clonedDescriptor.setQueryKeys(queryKeys);
// PrimaryKeyFields
List primaryKeyVector = new ArrayList(getPrimaryKeyFields().size());
List primaryKeyFields = getPrimaryKeyFields();
for (int index = 0; index < primaryKeyFields.size(); index++) {
DatabaseField primaryKey = primaryKeyFields.get(index).clone();
primaryKeyVector.add(primaryKey);
}
clonedDescriptor.setPrimaryKeyFields(primaryKeyVector);
// fields.
clonedDescriptor.setFields(NonSynchronizedVector.newInstance());
// Referencing classes
clonedDescriptor.referencingClasses = new HashSet<>(referencingClasses);
// Post-calculate changes
if (this.mappingsPostCalculateChanges != null) {
clonedDescriptor.mappingsPostCalculateChanges = new ArrayList<>();
for (DatabaseMapping databaseMapping : this.mappingsPostCalculateChanges) {
clonedDescriptor.mappingsPostCalculateChanges.add((DatabaseMapping)databaseMapping.clone());
}
}
// Post-calculate on delete
if (this.mappingsPostCalculateChangesOnDeleted != null) {
clonedDescriptor.mappingsPostCalculateChangesOnDeleted = new ArrayList<>();
for (DatabaseMapping databaseMapping : this.mappingsPostCalculateChangesOnDeleted) {
clonedDescriptor.mappingsPostCalculateChangesOnDeleted.add((DatabaseMapping)databaseMapping.clone());
}
}
// The inheritance policy
if (clonedDescriptor.hasInheritance()) {
clonedDescriptor.setInheritancePolicy((InheritancePolicy)getInheritancePolicy().clone());
clonedDescriptor.getInheritancePolicy().setDescriptor(clonedDescriptor);
}
if (clonedDescriptor.hasSerializedObjectPolicy()) {
clonedDescriptor.setSerializedObjectPolicy(getSerializedObjectPolicy().clone());
}
// The returning policy
if (clonedDescriptor.hasReturningPolicy()) {
clonedDescriptor.setReturningPolicy((ReturningPolicy)getReturningPolicy().clone());
clonedDescriptor.getReturningPolicy().setDescriptor(clonedDescriptor);
}
if (clonedDescriptor.hasReturningPolicies()) {
clonedDescriptor.returningPolicies = new ArrayList<>();
for (ReturningPolicy returningPolicy : this.returningPolicies) {
clonedDescriptor.returningPolicies.add((ReturningPolicy)returningPolicy.clone());
}
clonedDescriptor.prepareReturnFields(clonedDescriptor.returningPolicies);
}
// The Object builder
clonedDescriptor.setObjectBuilder((ObjectBuilder)getObjectBuilder().clone());
clonedDescriptor.getObjectBuilder().setDescriptor(clonedDescriptor);
clonedDescriptor.setEventManager((DescriptorEventManager)getEventManager().clone());
clonedDescriptor.getEventManager().setDescriptor(clonedDescriptor);
// The Query manager
clonedDescriptor.setQueryManager((DescriptorQueryManager)getQueryManager().clone());
clonedDescriptor.getQueryManager().setDescriptor(clonedDescriptor);
//fetch group
if (hasFetchGroupManager()) {
clonedDescriptor.setFetchGroupManager((FetchGroupManager)getFetchGroupManager().clone());
}
if (this.cachePolicy != null) {
clonedDescriptor.setCachePolicy(this.cachePolicy.clone());
}
// Bug 3037701 - clone several more elements
if (this.instantiationPolicy != null) {
clonedDescriptor.setInstantiationPolicy((InstantiationPolicy)getInstantiationPolicy().clone());
}
if (this.copyPolicy != null) {
clonedDescriptor.setCopyPolicy((CopyPolicy)getCopyPolicy().clone());
}
if (getOptimisticLockingPolicy() != null) {
clonedDescriptor.setOptimisticLockingPolicy((OptimisticLockingPolicy)getOptimisticLockingPolicy().clone());
}
//bug 5171059 clone change tracking policies as well
clonedDescriptor.setObjectChangePolicy(this.getObjectChangePolicyInternal());
// Clone the tables
Vector tables = NonSynchronizedVector.newInstance(3);
for (DatabaseTable table : getTables()) {
tables.add(table.clone());
}
clonedDescriptor.setTables(tables);
// Clone the default table
if (getDefaultTable() != null) {
clonedDescriptor.setDefaultTable(getDefaultTable().clone());
}
// Clone the CMPPolicy
if (getCMPPolicy() != null) {
clonedDescriptor.setCMPPolicy(getCMPPolicy().clone());
clonedDescriptor.getCMPPolicy().setDescriptor(clonedDescriptor);
}
// Clone the sequence number field.
if (getSequenceNumberField() != null) {
clonedDescriptor.setSequenceNumberField(getSequenceNumberField().clone());
}
// Clone the multitenant policy.
if (hasMultitenantPolicy()) {
clonedDescriptor.setMultitenantPolicy(getMultitenantPolicy().clone(clonedDescriptor));
}
return clonedDescriptor;
}
/**
* INTERNAL:
* Convert all the class-name-based settings in this Descriptor to actual class-based
* settings. This method is used when converting a project that has been built
* with class names to a project with classes.
*/
public void convertClassNamesToClasses(ClassLoader classLoader) {
//Class> redirectorClass = null;
if (getJavaClassName() != null) {
final Class> descriptorClass = PrivilegedAccessHelper.callDoPrivilegedWithException(
() -> org.eclipse.persistence.internal.security.PrivilegedAccessHelper.getClassForName(getJavaClassName(), true, classLoader),
(ex) -> ValidationException.classNotFoundWhileConvertingClassNames(getJavaClassName(), ex)
);
setJavaClass(descriptorClass);
}
if (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 = org.eclipse.persistence.internal.helper.Helper.getShortClassName(getJavaClassName());
}
return alias;
}
/**
* INTERNAL:
* Return all the fields which include all child class fields.
* By default it is initialized to the fields for the current descriptor.
*/
public Vector 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 Vector getReturnFieldsToGenerateInsert() {
return this.returnFieldsToGenerateInsert;
}
/**
* INTERNAL:
* Return fields used to build update statement.
*/
public Vector 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 extends CacheInterceptor> getCacheInterceptorClass() {
return getCachePolicy().getCacheInterceptorClass();
}
/**
* A CacheInterceptor is an adaptor that when overridden and assigned to a Descriptor all interaction
* between EclipseLink and the internal cache for that class will pass through the Interceptor.
* Advanced users could use this interceptor to audit, profile or log cache access. This Interceptor
* could also be used to redirect or augment the TopLink cache with an alternate cache mechanism.
* EclipseLink's configurated IdentityMaps will be passed to the Interceptor constructor.
*
* As with IdentityMaps an entire class inheritance hierarchy will share the same interceptor.
* @see org.eclipse.persistence.sessions.interceptors.CacheInterceptor
*/
public String getCacheInterceptorClassName() {
return getCachePolicy().getCacheInterceptorClassName();
}
/**
* PUBLIC:
* Return the CacheInvalidationPolicy for this descriptor
* For uninitialized cache invalidation policies, this will return a NoExpiryCacheInvalidationPolicy
* @return CacheInvalidationPolicy
* @see org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy
*/
public CacheInvalidationPolicy getCacheInvalidationPolicy() {
if (cacheInvalidationPolicy == null) {
cacheInvalidationPolicy = new NoExpiryCacheInvalidationPolicy();
}
return cacheInvalidationPolicy;
}
/**
* PUBLIC:
* Get a value indicating the type of cache synchronization that will be used on objects of
* this type. Possible values are:
* SEND_OBJECT_CHANGES
* INVALIDATE_CHANGED_OBJECTS
* SEND_NEW_OBJECTS+WITH_CHANGES
* DO_NOT_SEND_CHANGES
*/
public int getCacheSynchronizationType() {
return getCachePolicy().getCacheSynchronizationType();
}
/**
* INTERNAL:
*/
public List getCascadeLockingPolicies() {
if (this.cascadeLockingPolicies == null) {
this.cascadeLockingPolicies = new ArrayList();
}
return cascadeLockingPolicies;
}
/**
* ADVANCED:
* automatically orders database access through the foreign key information provided in 1:1 and 1:m mappings.
* In some case when 1:1 are not defined it may be required to tell the descriptor about a constraint,
* this defines that this descriptor has a foreign key constraint to another class and must be inserted after
* instances of the other class.
*/
public Vector getConstraintDependencies() {
if (constraintDependencies == null) {
constraintDependencies = NonSynchronizedVector.newInstance(1);
}
return constraintDependencies;
}
/**
* INTERNAL:
* Returns the copy policy.
*/
public CopyPolicy getCopyPolicy() {
// Lazy initialize for XML deployment.
if (copyPolicy == null) {
setCopyPolicy(new InstantiationCopyPolicy());
}
return copyPolicy;
}
/**
* INTERNAL:
* Returns the name of a Class that implements CopyPolicy
* Will be instantiated as a copy policy at initialization times
* using the no-args constructor
*/
public String getCopyPolicyClassName(){
return copyPolicyClassName;
}
/**
* INTERNAL:
* The first table in the tables is always treated as default.
*/
public DatabaseTable getDefaultTable() {
return defaultTable;
}
/**
* ADVANCED:
* return the descriptor type (NORMAL by default, others include INTERFACE, AGGREGATE, AGGREGATE COLLECTION)
*/
public int getDescriptorType() {
return descriptorType;
}
/**
* INTERNAL:
* This method is explicitly used by the XML reader.
*/
public String getDescriptorTypeValue() {
if (isAggregateCollectionDescriptor()) {
return "Aggregate collection";
} else if (isAggregateDescriptor()) {
return "Aggregate";
} else if (isDescriptorForInterface()) {
return "Interface";
} else {
// Default.
return "Normal";
}
}
/**
* ADVANCED:
* Return the derives id mappings.
*/
public Collection getDerivesIdMappinps() {
return derivesIdMappings.values();
}
/**
* INTERNAL:
* DescriptorCustomizer is the JPA equivalent of an amendment method.
*/
public String getDescriptorCustomizerClassName(){
return descriptorCustomizerClassName;
}
/**
* PUBLIC:
* Get the event manager for the descriptor. The event manager is responsible
* for managing the pre/post selectors.
*/
public DescriptorEventManager getDescriptorEventManager() {
return getEventManager();
}
/**
* PUBLIC:
* Get the event manager for the descriptor. The event manager is responsible
* for managing the pre/post selectors.
*/
@Override
public DescriptorEventManager getEventManager() {
// Lazy initialize for XML deployment.
if (eventManager == null) {
setEventManager(new org.eclipse.persistence.descriptors.DescriptorEventManager());
}
return eventManager;
}
/**
* INTERNAL:
* Return all the fields
*/
public Vector getFields() {
if (fields == null) {
fields = NonSynchronizedVector.newInstance();
}
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) {
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 (Enumeration mappingsNum = mappings.elements(); mappingsNum.hasMoreElements();) {
DatabaseMapping mapping = mappingsNum.nextElement();
if ((mapping.getAttributeName() != null) && mapping.getAttributeName().equals(attributeName)) {
return mapping;
}
}
return null;
}
/**
* ADVANCED:
* Removes the locally defined mapping associated with a given attribute name.
* This can be used in a amendment method before the descriptor has been initialized.
*/
public DatabaseMapping removeMappingForAttributeName(String attributeName) {
DatabaseMapping mapping = getMappingForAttributeName(attributeName);
getMappings().remove(mapping);
return mapping;
}
/**
* PUBLIC:
* Returns mappings
*/
public Vector getMappings() {
return mappings;
}
/**
* INTERNAL:
* Returns the foreign key relationships used for multiple tables which were specified by the user. Used
* by the Project XML writer to output these associations
*
* @see #adjustMultipleTableInsertOrder()
*/
public Vector getMultipleTableForeignKeyAssociations() {
Vector associations = new Vector(getAdditionalTablePrimaryKeyFields().size() * 2);
Iterator