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