blob: f4cd37492b3fd11826807a69114e0170cd993820 [file] [log] [blame]
/*
* Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Guy Pelletier - initial API and implementation from Oracle TopLink
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 05/23/2008-1.0M8 Guy Pelletier
// - 211330: Add attributes-complete support to the EclipseLink-ORM.XML Schema
// 05/30/2008-1.0M8 Guy Pelletier
// - 230213: ValidationException when mapping to attribute in MappedSuperClass
// 06/20/2008-1.0M9 Guy Pelletier
// - 232975: Failure when attribute type is generic
// 07/15/2008-1.0.1 Guy Pelletier
// - 240679: MappedSuperclass Id not picked when on get() method accessor
// 09/23/2008-1.1 Guy Pelletier
// - 241651: JPA 2.0 Access Type support
// 10/01/2008-1.1 Guy Pelletier
// - 249329: To remain JPA 1.0 compliant, any new JPA 2.0 annotations should be referenced by name
// 12/12/2008-1.1 Guy Pelletier
// - 249860: Implement table per class inheritance support.
// 01/28/2009-2.0 Guy Pelletier
// - 248293: JPA 2.0 Element Collections (part 1)
// 02/06/2009-2.0 Guy Pelletier
// - 248293: JPA 2.0 Element Collections (part 2)
// 03/27/2009-2.0 Guy Pelletier
// - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
// 04/03/2009-2.0 Guy Pelletier
// - 241413: JPA 2.0 Add EclipseLink support for Map type attributes
// 04/24/2009-2.0 Guy Pelletier
// - 270011: JPA 2.0 MappedById support
// 04/30/2009-2.0 Michael O'Brien
// - 266912: JPA 2.0 Metamodel API (part of Criteria API)
// 06/16/2009-2.0 Guy Pelletier
// - 277039: JPA 2.0 Cache Usage Settings
// 06/25/2009-2.0 Michael O'Brien
// - 266912: change MappedSuperclass handling in stage2 to pre process accessors
// in support of the custom descriptors holding mappings required by the Metamodel.
// processAccessType() is now public and is overridden in the superclass
// 09/29/2009-2.0 Guy Pelletier
// - 282553: JPA 2.0 JoinTable support for OneToOne and ManyToOne
// 10/21/2009-2.0 Guy Pelletier
// - 290567: mappedbyid support incomplete
// 12/2/2009-2.1 Guy Pelletier
// - 296289: Add current annotation metadata support on mapped superclasses to EclipseLink-ORM.XML Schema
// 12/18/2009-2.1 Guy Pelletier
// - 211323: Add class extractor support to the EclipseLink-ORM.XML Schema
// 04/09/2010-2.1 Guy Pelletier
// - 307050: Add defaults for access methods of a VIRTUAL access type
// 05/04/2010-2.1 Guy Pelletier
// - 309373: Add parent class attribute to EclipseLink-ORM
// 05/14/2010-2.1 Guy Pelletier
// - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml
// 06/01/2010-2.1 Guy Pelletier
// - 315195: Add new property to avoid reading XML during the canonical model generation
// 06/09/2010-2.0.3 Guy Pelletier
// - 313401: shared-cache-mode defaults to NONE when the element value is unrecognized
// 06/14/2010-2.2 Guy Pelletier
// - 264417: Table generation is incorrect for JoinTables in AssociationOverrides
// 06/18/2010-2.2 Guy Pelletier
// - 300458: EclispeLink should throw a more specific exception than NPE
// 06/22/2010-2.2 Guy Pelletier
// - 308729: Persistent Unit deployment exception when mappedsuperclass has no annotations but has lifecycle callbacks
// 07/05/2010-2.1.1 Guy Pelletier
// - 317708: Exception thrown when using LAZY fetch on VIRTUAL mapping
// 08/11/2010-2.2 Guy Pelletier
// - 312123: JPA: Validation error during Id processing on parameterized generic OneToOne Entity relationship from MappedSuperclass
// 09/03/2010-2.2 Guy Pelletier
// - 317286: DB column lenght not in sync between @Column and @JoinColumn
// 09/16/2010-2.2 Guy Pelletier
// - 283028: Add support for letting an @Embeddable extend a @MappedSuperclass
// 12/01/2010-2.2 Guy Pelletier
// - 331234: xml-mapping-metadata-complete overriden by metadata-complete specification
// 01/04/2011-2.3 Guy Pelletier
// - 330628: @PrimaryKeyJoinColumn(...) is not working equivalently to @JoinColumn(..., insertable = false, updatable = false)
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 1)
// 04/05/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 3)
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 8)
// 07/03/2011-2.3.1 Guy Pelletier
// - 348756: m_cascadeOnDelete boolean should be changed to Boolean
// 10/09/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 10/25/2012-2.5 Guy Pelletier
// - 3746888: JPA 2.1 Converter support
// 11/19/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
// 11/28/2012-2.5 Guy Pelletier
// - 374688: JPA 2.1 Converter support
// 11/29/2012-2.5 Guy Pelletier
// - 395406: Fix nightly static weave test errors
// 12/07/2012-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
// 09 Jan 2013-2.5 Gordon Yorke
// - 397772: JPA 2.1 Entity Graph Support
// 02/13/2013-2.5 Guy Pelletier
// - 397772: JPA 2.1 Entity Graph Support (XML support)
// 02/20/2013-2.5 Guy Pelletier
// - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support)
// 05/19/2014-2.6 Tomas Kraus
// - 437578: @Cacheable annotation value is passed to CachePolicy for ENABLE_SELECTIVE and DISABLE_SELECTIVE shared cache mode
package org.eclipse.persistence.internal.jpa.metadata.accessors.classes;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_FIELD;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS_PROPERTY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_CONVERT;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_CONVERTS;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_DISCRIMINATOR_COLUMN;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_DISCRIMINATOR_VALUE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ENTITY;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ENTITY_GRAPH;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ENTITY_GRAPHS;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_INHERITANCE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRIMARY_KEY_JOIN_COLUMN;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_PRIMARY_KEY_JOIN_COLUMNS;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_SECONDARY_TABLE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_SECONDARY_TABLES;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_TABLE;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import org.eclipse.persistence.annotations.CascadeOnDelete;
import org.eclipse.persistence.annotations.ClassExtractor;
import org.eclipse.persistence.annotations.Index;
import org.eclipse.persistence.annotations.Indexes;
import org.eclipse.persistence.annotations.VirtualAccessMethods;
import org.eclipse.persistence.exceptions.ValidationException;
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.jpa.metadata.MetadataDescriptor;
import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.jpa.metadata.MetadataProject;
import org.eclipse.persistence.internal.jpa.metadata.ORMetadata;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataClass;
import org.eclipse.persistence.internal.jpa.metadata.columns.DiscriminatorColumnMetadata;
import org.eclipse.persistence.internal.jpa.metadata.columns.PrimaryKeyForeignKeyMetadata;
import org.eclipse.persistence.internal.jpa.metadata.columns.PrimaryKeyJoinColumnMetadata;
import org.eclipse.persistence.internal.jpa.metadata.converters.ConvertMetadata;
import org.eclipse.persistence.internal.jpa.metadata.graphs.NamedEntityGraphMetadata;
import org.eclipse.persistence.internal.jpa.metadata.inheritance.InheritanceMetadata;
import org.eclipse.persistence.internal.jpa.metadata.listeners.EntityClassListenerMetadata;
import org.eclipse.persistence.internal.jpa.metadata.listeners.EntityListenerMetadata;
import org.eclipse.persistence.internal.jpa.metadata.mappings.AccessMethodsMetadata;
import org.eclipse.persistence.internal.jpa.metadata.tables.IndexMetadata;
import org.eclipse.persistence.internal.jpa.metadata.tables.SecondaryTableMetadata;
import org.eclipse.persistence.internal.jpa.metadata.tables.TableMetadata;
import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings;
/**
* An entity accessor.
*
* Key notes:
* - any metadata mapped from XML to this class must be compared in the
* equals method.
* - any metadata mapped from XML to this class must be handled in the merge
* method. (merging is done at the accessor/mapping level)
* - any metadata mapped from XML to this class must be initialized in the
* initXMLObject method.
* - methods should be preserved in alphabetical order.
*
* @author Guy Pelletier
* @since EclipseLink 1.0
*/
public class EntityAccessor extends MappedSuperclassAccessor {
private Boolean m_cascadeOnDelete;
private InheritanceMetadata m_inheritance;
private DiscriminatorColumnMetadata m_discriminatorColumn;
private PrimaryKeyForeignKeyMetadata m_primaryKeyForeignKey;
private List<ConvertMetadata> m_converts = new ArrayList<ConvertMetadata>();
private List<PrimaryKeyJoinColumnMetadata> m_primaryKeyJoinColumns = new ArrayList<PrimaryKeyJoinColumnMetadata>();
private List<SecondaryTableMetadata> m_secondaryTables = new ArrayList<SecondaryTableMetadata>();
private List<IndexMetadata> m_indexes = new ArrayList<IndexMetadata>();
private List<NamedEntityGraphMetadata> m_namedEntityGraphs = new ArrayList<NamedEntityGraphMetadata>();
private MetadataClass m_classExtractor;
private String m_classExtractorName;
private String m_discriminatorValue;
private String m_entityName;
private TableMetadata m_table;
/**
* INTERNAL:
*/
public EntityAccessor() {
super("<entity>");
}
/**
* INTERNAL:
*/
public EntityAccessor(MetadataAnnotation annotation, MetadataClass cls, MetadataProject project) {
super(annotation, cls, project);
}
/**
* INTERNAL:
* Add multiple fields to the descriptor. Called from either Inheritance
* or SecondaryTable context.
*/
protected void addMultipleTableKeyFields(List<PrimaryKeyJoinColumnMetadata> primaryKeyJoinColumns, DatabaseTable targetTable, String PK_CTX, String FK_CTX) {
// ProcessPrimaryKeyJoinColumns will validate the primary key join
// columns passed in and will return a list of PrimaryKeyJoinColumnMetadata.
for (PrimaryKeyJoinColumnMetadata primaryKeyJoinColumn : processPrimaryKeyJoinColumns(primaryKeyJoinColumns)) {
// Look up the primary key field from the referenced column name.
DatabaseField pkField = getReferencedField(primaryKeyJoinColumn.getReferencedColumnName(), getDescriptor(), PK_CTX);
DatabaseField fkField = primaryKeyJoinColumn.getForeignKeyField(pkField);
setFieldName(fkField, pkField.getName(), FK_CTX);
fkField.setTable(targetTable);
// Add the foreign key field to the descriptor.
getDescriptor().addForeignKeyFieldForMultipleTable(fkField, pkField);
}
}
/**
* INTERNAL:
* Build a list of classes that are decorated with a MappedSuperclass
* annotation or that are tagged as a mapped-superclass in an XML document.
*
* This method will also do a couple other things as well since we are
* traversing the parent classes:
* - Build a map of generic types specified and will be used to resolve
* actual class types for mappings.
* - Will discover and set the inheritance parent and root descriptors
* if this entity is part of an inheritance hierarchy.
* - save mapped-superclass descriptors on the project for later use
* by the Metamodel API
*
* Note: The list is rebuilt every time this method is called since
* it is called both during pre-deploy and deploy where the class loader
* dependencies change.
*/
protected void discoverMappedSuperclassesAndInheritanceParents(boolean addMappedSuperclassAccessors) {
// Clear any previous discovery.
clearMappedSuperclassesAndInheritanceParents();
EntityAccessor currentEntityAccessor = this;
MetadataClass parentClass = getJavaClass().getSuperclass();
List<String> genericTypes = getJavaClass().getGenericType();
// We keep a list of potential subclass accessors to ensure they
// have their root parent descriptor set correctly.
List<EntityAccessor> subclassEntityAccessors = new ArrayList<EntityAccessor>();
subclassEntityAccessors.add(currentEntityAccessor);
if (! parentClass.isObject()) {
while (parentClass != null && ! parentClass.isObject()) {
if (getProject().hasEntity(parentClass)) {
// Our parent is an entity.
EntityAccessor parentEntityAccessor = getProject().getEntityAccessor(parentClass);
// Set the current entity's inheritance parent descriptor.
currentEntityAccessor.getDescriptor().setInheritanceParentDescriptor(parentEntityAccessor.getDescriptor());
// Update the current entity accessor.
currentEntityAccessor = parentEntityAccessor;
// Clear out any previous mapped superclasses and inheritance
// parents that were discovered. We're going to re-discover
// them now.
currentEntityAccessor.clearMappedSuperclassesAndInheritanceParents();
// If we found an entity with inheritance metadata, set the
// root descriptor on its subclasses.
if (currentEntityAccessor.hasInheritance()) {
for (EntityAccessor subclassEntityAccessor : subclassEntityAccessors) {
subclassEntityAccessor.getDescriptor().setInheritanceRootDescriptor(currentEntityAccessor.getDescriptor());
}
// Clear the subclass list, we'll keep looking but the
// inheritance strategy may have changed so we need to
// build a new list of subclasses.
subclassEntityAccessors.clear();
}
// Add the descriptor to the subclass list
subclassEntityAccessors.add(currentEntityAccessor);
} else {
// Our parent might be a mapped superclass, check and add
// as needed.
currentEntityAccessor.addPotentialMappedSuperclass(parentClass, addMappedSuperclassAccessors);
}
// Resolve any generic types from the generic parent onto the
// current entity accessor.
currentEntityAccessor.resolveGenericTypes(genericTypes, parentClass);
// Grab the generic types from the parent class.
genericTypes = parentClass.getGenericType();
// Finally, get the next parent and keep processing ...
parentClass = parentClass.getSuperclass();
}
} else {
// Resolve any generic types we have (we may be an inheritance root).
currentEntityAccessor.resolveGenericTypes(genericTypes, parentClass);
}
// Set our root descriptor of the inheritance hierarchy on all the
// subclass descriptors. The root may not have explicit inheritance
// metadata therefore, make the current descriptor of the entity
// hierarchy the root.
if (! subclassEntityAccessors.isEmpty()) {
for (EntityAccessor subclassEntityAccessor : subclassEntityAccessors) {
if (subclassEntityAccessor != currentEntityAccessor) {
subclassEntityAccessor.getDescriptor().setInheritanceRootDescriptor(currentEntityAccessor.getDescriptor());
}
}
}
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public Boolean getCascadeOnDelete() {
return m_cascadeOnDelete;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getClassExtractorName() {
return m_classExtractorName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<ConvertMetadata> getConverts() {
return m_converts;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public DiscriminatorColumnMetadata getDiscriminatorColumn() {
return m_discriminatorColumn;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getDiscriminatorValue() {
return m_discriminatorValue;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getEntityName() {
return m_entityName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<IndexMetadata> getIndexes() {
return m_indexes;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public InheritanceMetadata getInheritance() {
return m_inheritance;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<NamedEntityGraphMetadata> getNamedEntityGraphs() {
return m_namedEntityGraphs;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public PrimaryKeyForeignKeyMetadata getPrimaryKeyForeignKey() {
return m_primaryKeyForeignKey;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<PrimaryKeyJoinColumnMetadata> getPrimaryKeyJoinColumns() {
return m_primaryKeyJoinColumns;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public List<SecondaryTableMetadata> getSecondaryTables() {
return m_secondaryTables;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public TableMetadata getTable() {
return m_table;
}
/**
* INTERNAL:
* This method is a little involved since a class extractor is mutually
* exclusive with a discriminator column.
*
* Within one xml file it is impossible to have both specified since they
* are within a choice tag. However, if one is specified in the orm.xml
* and the other in the eclipselink-orm.xml, after the merge both will be
* set on this accessor, so we need to check which came from the
* eclipselink-orm.xml because it is the one we need to use.
*/
public boolean hasClassExtractor() {
if (m_classExtractorName != null && m_discriminatorColumn != null) {
// If we have both a classExtractorName and a discriminatorColumn
// then the only way this is possible is if the user has both an
// orm.xml and an eclipselink-orm.xml and one specifies the
// discriminator column and the other specifies the class extractor.
// We must use the one from the eclipselink-orm.xml.
return ! m_discriminatorColumn.loadedFromEclipseLinkXML();
} else if (m_classExtractorName != null) {
// We have a class extractor name and we don't care where it came
// from. It must be used and override any annotations.
return true;
} else if (m_discriminatorColumn != null) {
// We have a discriminator column and we don't care where it came
// from. It must be used and override any annotations.
return false;
} else {
// Nothing was specified in XML. Must look at the annotations now.
if (isAnnotationPresent(ClassExtractor.class) && (isAnnotationPresent(JPA_DISCRIMINATOR_COLUMN) || isAnnotationPresent(JPA_DISCRIMINATOR_VALUE))) {
throw ValidationException.classExtractorCanNotBeSpecifiedWithDiscriminatorMetadata(getJavaClassName());
}
return isAnnotationPresent(ClassExtractor.class);
}
}
/**
* INTERNAL:
* Return true if this class has an inheritance specifications.
*/
public boolean hasInheritance() {
if (m_inheritance == null) {
return isAnnotationPresent(JPA_INHERITANCE);
} else {
return true;
}
}
/**
* INTERNAL:
*/
@Override
public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) {
super.initXMLObject(accessibleObject, entityMappings);
// Initialize simple class objects.
m_classExtractor = initXMLClassName(m_classExtractorName);
// Initialize single objects.
initXMLObject(m_inheritance, accessibleObject);
initXMLObject(m_discriminatorColumn, accessibleObject);
initXMLObject(m_table, accessibleObject);
initXMLObject(m_primaryKeyForeignKey, accessibleObject);
// Initialize lists of ORMetadata objects.
initXMLObjects(m_secondaryTables, accessibleObject);
initXMLObjects(m_primaryKeyJoinColumns, accessibleObject);
initXMLObjects(m_indexes, accessibleObject);
initXMLObjects(m_converts, accessibleObject);
initXMLObjects(m_namedEntityGraphs, accessibleObject);
}
/**
* INTERNAL:
*/
public boolean isCascadeOnDelete() {
return (m_cascadeOnDelete == null) ? isAnnotationPresent(CascadeOnDelete.class) : m_cascadeOnDelete;
}
/**
* INTERNAL:
* Return true if this accessor represents an entity class.
*/
@Override
public boolean isEntityAccessor() {
return true;
}
/**
* INTERNAL:
*/
@Override
public boolean isMappedSuperclass() {
return false;
}
/**
* INTERNAL:
* Entity level merging details.
*/
@Override
public void merge(ORMetadata metadata) {
super.merge(metadata);
EntityAccessor accessor = (EntityAccessor) metadata;
// Simple object merging.
m_cascadeOnDelete = (Boolean) mergeSimpleObjects(m_cascadeOnDelete, accessor.getCascadeOnDelete(), accessor, "<cascade-on-delete>");
m_entityName = (String) mergeSimpleObjects(m_entityName, accessor.getEntityName(), accessor, "@name");
m_discriminatorValue = (String) mergeSimpleObjects(m_discriminatorValue, accessor.getDiscriminatorValue(), accessor, "<discriminator-value>");
m_classExtractorName = (String) mergeSimpleObjects(m_classExtractorName, accessor.getClassExtractorName(), accessor, "<class-extractor>");
// ORMetadata object merging.
m_discriminatorColumn = (DiscriminatorColumnMetadata) mergeORObjects(m_discriminatorColumn, accessor.getDiscriminatorColumn());
m_inheritance = (InheritanceMetadata) mergeORObjects(m_inheritance, accessor.getInheritance());
m_table = (TableMetadata) mergeORObjects(m_table, accessor.getTable());
m_primaryKeyForeignKey = (PrimaryKeyForeignKeyMetadata) mergeORObjects(m_primaryKeyForeignKey, accessor.getPrimaryKeyForeignKey());
// ORMetadata list merging.
m_secondaryTables = mergeORObjectLists(m_secondaryTables, accessor.getSecondaryTables());
m_primaryKeyJoinColumns = mergeORObjectLists(m_primaryKeyJoinColumns, accessor.getPrimaryKeyJoinColumns());
m_indexes = mergeORObjectLists(m_indexes, accessor.getIndexes());
m_converts = mergeORObjectLists(m_converts, accessor.getConverts());
m_namedEntityGraphs = mergeORObjectLists(m_namedEntityGraphs, accessor.getNamedEntityGraphs());
}
/**
* INTERNAL:
* The pre-process method is called during regular deployment and metadata
* processing and will pre-process the items of interest on an entity class.
*
* The order of processing is important, care must be taken if changes must
* be made.
*/
@Override
public void preProcess() {
// If we are not already an inheritance subclass (meaning we were not
// discovered through a subclass entity discovery) then perform the
// discovery process before processing any further. We traverse the
// chain upwards and we can not guarantee which entity will be processed
// first in an inheritance hierarchy.
if (! getDescriptor().isInheritanceSubclass()) {
discoverMappedSuperclassesAndInheritanceParents(true);
}
// If we are an inheritance subclass process out root first.
if (getDescriptor().isInheritanceSubclass()) {
// Ensure our parent accessors are processed first. Top->down.
// An inheritance subclass can no longer blindly inherit a default
// access type from the root of the inheritance hierarchy since
// that root may explicitly set an access type which applies only
// to itself. The first entity in the hierarchy (going down) that
// does not specify an explicit type will be used to determine
// the default access type.
EntityAccessor parentAccessor = (EntityAccessor) getDescriptor().getInheritanceParentDescriptor().getClassAccessor();
if (! parentAccessor.isPreProcessed()) {
parentAccessor.preProcess();
}
}
// Process the correct access type before any other processing.
processAccessType();
// Process a virtual class specification after determining access type.
processVirtualClass();
// Process the default access methods after determining access type.
processAccessMethods();
// Process a @Struct and @EIS annotation to create the correct type of descriptor.
processStruct();
processNoSql();
// Process our parents metadata after processing our own.
super.preProcess();
}
/**
* INTERNAL:
* The pre-process for canonical model method is called (and only called)
* during the canonical model generation. The use of this pre-process allows
* us to remove some items from the regular pre-process that do not apply
* to the canonical model generation.
*
* The order of processing is important, care must be taken if changes must
* be made.
*/
@Override
public void preProcessForCanonicalModel() {
setIsPreProcessed();
// If we are not already an inheritance subclass (meaning we were not
// discovered through a subclass entity discovery) then perform
// the discovery process before processing any further. We traverse
// the chain upwards and we can not guarantee which entity will be
// processed first in an inheritance hierarchy.
if (! getDescriptor().isInheritanceSubclass()) {
discoverMappedSuperclassesAndInheritanceParents(false);
}
// If we are an inheritance subclass process out root first.
if (getDescriptor().isInheritanceSubclass()) {
// Ensure our parent accessors are processed first. Top->down.
// An inheritance subclass can no longer blindly inherit a default
// access type from the root of the inheritance hierarchy since
// that root may explicitly set an access type which applies only
// to itself. The first entity in the hierarchy (going down) that
// does not specify an explicit type will be used to determine
// the default access type.
EntityAccessor parentAccessor = (EntityAccessor) getDescriptor().getInheritanceParentDescriptor().getClassAccessor();
if (! parentAccessor.isPreProcessed()) {
parentAccessor.preProcessForCanonicalModel();
}
}
// Process our parents metadata after processing our own.
super.preProcessForCanonicalModel();
}
/**
* INTERNAL:
* Process the items of interest on an entity class. The order of processing
* is important, care must be taken if changes must be made.
*/
@Override
public void process() {
// Process the entity annotation.
processEntity();
// If we are an inheritance subclass process our root first.
if (getDescriptor().isInheritanceSubclass()) {
ClassAccessor parentAccessor = getDescriptor().getInheritanceParentDescriptor().getClassAccessor();
if (! parentAccessor.isProcessed()) {
parentAccessor.process();
}
}
// Process the Table and Inheritance metadata.
processTableAndInheritance();
// Process the indices metadata.
processIndexes();
// Process the cascade on delete metadata.
processCascadeOnDelete();
// Process the JPA converts metadata.
processConverts();
// Process our parents metadata after processing our own.
super.process();
// Finally, process the mapping accessors on this entity (and all those
// from super classes that apply to us).
processMappingAccessors();
// Process the entity graph metadata.
processEntityGraphs();
}
/**
* INTERNAL:
* For VIRTUAL access we need to look for default access methods that we
* need to use with our mapping attributes.
*/
public void processAccessMethods() {
// If we use virtual access and do not have any access methods
// specified then go look for access methods on a mapped superclass
// or inheritance parent.
if (hasAccessMethods()) {
getDescriptor().setDefaultAccessMethods(getAccessMethods());
} else {
MetadataAnnotation virtualAccessMethods = getAnnotation(VirtualAccessMethods.class);
if (virtualAccessMethods != null){
getDescriptor().setDefaultAccessMethods(new AccessMethodsMetadata(virtualAccessMethods, this));
return;
}
// Go through the mapped superclasses.
for (MappedSuperclassAccessor mappedSuperclass : getMappedSuperclasses()) {
if (mappedSuperclass.hasAccessMethods()) {
getDescriptor().setDefaultAccessMethods(mappedSuperclass.getAccessMethods());
return;
}
virtualAccessMethods = mappedSuperclass.getAnnotation(VirtualAccessMethods.class);
if (virtualAccessMethods != null){
getDescriptor().setDefaultAccessMethods(new AccessMethodsMetadata(virtualAccessMethods, this));
return;
}
}
// Go through the inheritance parents.
if (getDescriptor().isInheritanceSubclass()) {
MetadataDescriptor parentDescriptor = getDescriptor().getInheritanceParentDescriptor();
while (parentDescriptor.isInheritanceSubclass()) {
if (parentDescriptor.getClassAccessor().hasAccessMethods()) {
getDescriptor().setDefaultAccessMethods(parentDescriptor.getClassAccessor().getAccessMethods());
return;
}
virtualAccessMethods = parentDescriptor.getClassAccessor().getAnnotation(VirtualAccessMethods.class);
if (virtualAccessMethods != null){
getDescriptor().setDefaultAccessMethods(new AccessMethodsMetadata(virtualAccessMethods, this));
return;
}
parentDescriptor = parentDescriptor.getInheritanceParentDescriptor();
}
}
}
}
/**
* INTERNAL:
* Figure out the access type for this entity. It works as follows:
* 1 - check for an explicit access type specification
* 2 - check our inheritance parents (ignoring explicit specifications)
* 3 - check our mapped superclasses (ignoring explicit specifications) for
* the location of annotations
* 4 - check the entity itself for the location of annotations
* 5 - check for an xml default from a persistence-unit-metadata-defaults or
* entity-mappings setting.
* 6 - we have exhausted our search, default to FIELD.
*/
@Override
public void processAccessType() {
// This function has been overridden in the MappedSuperclassAccessor
// parent. Do not call this superclass method.
// Step 1 - Check for an explicit setting.
String explicitAccessType = getAccess();
// Step 2, regardless if there is an explicit access type we still
// want to determine the default access type for this entity since
// any embeddable, mapped superclass or id class that depends on it
// will have it available.
String defaultAccessType = null;
// 1 - Check the access types from our parents if we are an inheritance
// sub-class. Inheritance hierarchies are processed top->down so our
// first parent that does not define an explicit access type will have
// the type we would want to default to.
if (getDescriptor().isInheritanceSubclass()) {
MetadataDescriptor parent = getDescriptor().getInheritanceParentDescriptor();
while (parent != null) {
if (! parent.getClassAccessor().hasAccess()) {
defaultAccessType = parent.getDefaultAccess();
break;
}
parent = parent.getInheritanceParentDescriptor();
}
}
// 2 - If there is no inheritance or no inheritance parent that does not
// explicitly define an access type. Let's check this entities mapped
// superclasses now.
if (defaultAccessType == null) {
for (MappedSuperclassAccessor mappedSuperclass : getMappedSuperclasses()) {
if (! mappedSuperclass.hasAccess()) {
if (mappedSuperclass.hasObjectRelationalFieldMappingAnnotationsDefined()) {
defaultAccessType = JPA_ACCESS_FIELD;
} else if (mappedSuperclass.hasObjectRelationalMethodMappingAnnotationsDefined()) {
defaultAccessType = JPA_ACCESS_PROPERTY;
}
break;
}
}
// 3 - If there are no mapped superclasses or no mapped superclasses
// without an explicit access type. Check where the annotations are
// defined on this entity class.
if (defaultAccessType == null) {
if (hasObjectRelationalFieldMappingAnnotationsDefined()) {
defaultAccessType = JPA_ACCESS_FIELD;
} else if (hasObjectRelationalMethodMappingAnnotationsDefined()) {
defaultAccessType = JPA_ACCESS_PROPERTY;
} else {
// 4 - If there are no annotations defined on either the
// fields or properties, check for an xml default from
// persistence-unit-metadata-defaults or entity-mappings.
if (getDescriptor().getDefaultAccess() != null) {
defaultAccessType = getDescriptor().getDefaultAccess();
} else {
// 5 - We've exhausted our search, set the access type
// to FIELD.
defaultAccessType = JPA_ACCESS_FIELD;
}
}
}
}
// Finally set the default access type on the descriptor and log a
// message to the user if we are defaulting the access type for this
// entity to use that default.
getDescriptor().setDefaultAccess(defaultAccessType);
if (explicitAccessType == null) {
getLogger().logConfigMessage(MetadataLogger.ACCESS_TYPE, defaultAccessType, getJavaClass());
}
// This access type setting on the class descriptor will be used to
// weave the class properly.
getDescriptor().setAccessTypeOnClassDescriptor(getAccessType());
}
/**
* INTERNAL:
* Process a caching metadata for this entity accessor logging ignore
* warnings where necessary.
*/
@Override
protected void processCaching() {
if (getProject().isSharedCacheModeAll()) {
if (getDescriptor().isCacheableFalse()) {
// The persistence unit has an ALL caching type and the user
// specified Cacheable(false). Log a warning message that it is
// being ignored. If Cacheable(true) was specified then it is
// redundant and we just carry on.
getLogger().logConfigMessage(MetadataLogger.IGNORE_CACHEABLE_FALSE, getJavaClass());
}
// Process the cache metadata.
processCachingMetadata();
} else if (getProject().isSharedCacheModeNone()) {
if (getDescriptor().isCacheableTrue()) {
// The persistence unit has a NONE caching type and the the user
// specified Cacheable(true). Log a warning message that it is being
// ignored. If Cacheable(false) was specified then it is redundant
// and we just carry on.
getLogger().logConfigMessage(MetadataLogger.IGNORE_CACHEABLE_TRUE, getJavaClass());
}
// Turn off the cache.
getDescriptor().useNoCache();
} else if (getProject().isSharedCacheModeEnableSelective()) {
if (!getDescriptor().isCacheableTrue()) {
// ENABLE_SELECTIVE and Cacheable(false) or no setting, turn off the cache.
getDescriptor().useNoCache();
}
// Cacheable annotation in current class can override default or inherited settings.
getDescriptor().setCacheableInDescriptor();
// ENABLE_SELECTIVE and Cacheable(true), process the cache metadata.
processCachingMetadata();
} else if (getProject().isSharedCacheModeDisableSelective() || getProject().isSharedCacheModeUnspecified()) {
if (getDescriptor().isCacheableFalse()) {
// DISABLE_SELECTIVE and Cacheable(false), turn off cache.
getDescriptor().useNoCache();
}
// Cacheable annotation in current class can override default or inherited settings.
getDescriptor().setCacheableInDescriptor();
// DISABLE_SELECTIVE and Cacheable(true) or no setting, process the cache metadata.
processCachingMetadata();
}
}
/**
* INTERNAL:
* Check if CascadeOnDelete was set on the Entity.
*/
protected void processCascadeOnDelete() {
getDescriptor().getClassDescriptor().setIsCascadeOnDeleteSetOnDatabaseOnSecondaryTables(isCascadeOnDelete());
}
/**
* INTERNAL:
* Return the user defined class extractor class for this entity. Assumes
* hasClassExtractor has been called beforehand (meaning we either have
* an annotation or XML definition.
*/
public String processClassExtractor() {
MetadataAnnotation classExtractor = getAnnotation(ClassExtractor.class);
if (m_classExtractor == null) {
m_classExtractor = getMetadataClass(classExtractor.getAttributeString("value"));
} else {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, classExtractor, getJavaClassName(), getLocation());
}
return m_classExtractor.getName();
}
/**
* INTERNAL:
* Add a convert metadata to the descriptor convert map.
*/
public void processConvert(ConvertMetadata convert) {
if (convert.hasAttributeName()) {
if (convert.getAttributeName().indexOf('.') > -1) {
// We have a dot notation name.
String dotNotationName = convert.getAttributeName();
int dotIndex = dotNotationName.indexOf('.');
String attributeName = dotNotationName.substring(0, dotIndex);
String remainder = dotNotationName.substring(dotIndex + 1);
// Update the convert attribute name for correct convert processing.
convert.setAttributeName(remainder);
getDescriptor().addConvert(attributeName, convert);
} else {
// Simple single name.
String attributeName = convert.getAttributeName();
convert.setAttributeName("");
getDescriptor().addConvert(attributeName, convert);
}
} else {
throw ValidationException.missingConvertAttributeName(getJavaClassName());
}
}
/**
* INTERNAL:
* Process the convert metadata for this entity accessor logging ignore
* warnings where necessary.
*/
public void processConverts() {
if (m_converts.isEmpty()) {
// Look for a Converts annotation.
if (isAnnotationPresent(JPA_CONVERTS)) {
for (Object convert : getAnnotation(JPA_CONVERTS).getAttributeArray("value")) {
processConvert(new ConvertMetadata((MetadataAnnotation) convert, this));
}
} else {
// Look for a Convert annotation
if (isAnnotationPresent(JPA_CONVERT)) {
processConvert(new ConvertMetadata(getAnnotation(JPA_CONVERT), this));
}
}
} else {
if (isAnnotationPresent(JPA_CONVERT)) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, getAnnotation(JPA_CONVERT), getJavaClassName(), getLocation());
}
if (isAnnotationPresent(JPA_CONVERTS)) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, getAnnotation(JPA_CONVERTS), getJavaClassName(), getLocation());
}
for (ConvertMetadata convert : m_converts) {
processConvert(convert);
}
}
}
/**
* INTERNAL:
* Allows for processing DerivedIds. All referenced accessors are processed
* first to ensure the necessary fields are set before this derivedId is processed
*/
@Override
public void processDerivedId(HashSet<ClassAccessor> processing, HashSet<ClassAccessor> processed) {
if (! processed.contains(this)) {
super.processDerivedId(processing, processed);
// Validate we found a primary key.
validatePrimaryKey();
// Primary key has been validated, let's process those items that
// depend on it now.
// Process the SecondaryTable(s) metadata.
processSecondaryTables();
}
}
/**
* INTERNAL:
* Process the discriminator column metadata (defaulting if necessary),
* and return the EclipseLink database field.
*/
public DatabaseField processDiscriminatorColumn() {
MetadataAnnotation discriminatorColumn = getAnnotation(JPA_DISCRIMINATOR_COLUMN);
if (m_discriminatorColumn == null) {
m_discriminatorColumn = new DiscriminatorColumnMetadata(discriminatorColumn, this);
} else {
if (isAnnotationPresent(JPA_DISCRIMINATOR_COLUMN)) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, discriminatorColumn, getJavaClassName(), getLocation());
}
}
return m_discriminatorColumn.process(getDescriptor(), MetadataLogger.INHERITANCE_DISCRIMINATOR_COLUMN);
}
/**
* INTERNAL:
* Process a discriminator value to set the class indicator on the root
* descriptor of the inheritance hierarchy.
*
* If there is no discriminator value, the class indicator defaults to
* the class name.
*/
public String processDiscriminatorValue() {
if (! Modifier.isAbstract(getJavaClass().getModifiers())) {
// Add the indicator to the inheritance root class' descriptor. The
// default is the short class name.
if (m_discriminatorValue == null) {
MetadataAnnotation discriminatorValue = getAnnotation(JPA_DISCRIMINATOR_VALUE);
if (discriminatorValue == null) {
// By default return the alias (i.e. entity name if provided
// otherwise the short java class name)
return getDescriptor().getAlias();
} else {
return discriminatorValue.getAttributeString("value");
}
} else {
return m_discriminatorValue;
}
}
return null;
}
/**
* INTERNAL:
* Process the entity metadata.
*/
protected void processEntity() {
// Process the entity name (alias) and default if necessary.
if (m_entityName == null) {
m_entityName = (getAnnotation(JPA_ENTITY) == null) ? "" : getAnnotation(JPA_ENTITY).getAttributeString("name");
}
if (m_entityName == null || m_entityName.equals("")) {
m_entityName = Helper.getShortClassName(getJavaClassName());
getLogger().logConfigMessage(MetadataLogger.ALIAS, getDescriptor(), m_entityName);
}
getProject().addAlias(m_entityName, getDescriptor());
}
/**
* INTERNAL:
* Process the entity graph metadata on this entity accessor.
*/
protected void processEntityGraphs() {
if (m_namedEntityGraphs.isEmpty()) {
// Look for an NamedEntityGraphs annotation.
if (isAnnotationPresent(JPA_ENTITY_GRAPHS)) {
for (Object entityGraph : getAnnotation(JPA_ENTITY_GRAPHS).getAttributeArray("value")) {
new NamedEntityGraphMetadata((MetadataAnnotation) entityGraph, this).process(this);
}
} else {
// Look for a NamedEntityGraph annotation
if (isAnnotationPresent(JPA_ENTITY_GRAPH)) {
new NamedEntityGraphMetadata(getAnnotation(JPA_ENTITY_GRAPH), this).process(this);
}
}
} else {
// Process the named entity graphs from XML.
if (isAnnotationPresent(JPA_ENTITY_GRAPH)) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, getAnnotation(JPA_ENTITY_GRAPH), getJavaClassName(), getLocation());
}
if (isAnnotationPresent(JPA_ENTITY_GRAPHS)) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, getAnnotation(JPA_ENTITY_GRAPHS), getJavaClassName(), getLocation());
}
for (NamedEntityGraphMetadata entityGraph : m_namedEntityGraphs) {
entityGraph.process(this);
}
}
}
/**
* INTERNAL:
* Process index information for the given metadata descriptor.
*/
protected void processIndexes() {
// TODO: This method is adding annotation metadata to XML metadata. This
// is wrong and does not follow the spec ideology. XML metadata should
// override not merge with annotations.
if (isAnnotationPresent(Index.class)) {
m_indexes.add(new IndexMetadata(getAnnotation(Index.class), this));
}
if (isAnnotationPresent(Indexes.class)) {
for (Object index : getAnnotation(Indexes.class).getAttributeArray("value")) {
m_indexes.add(new IndexMetadata((MetadataAnnotation) index, this));
}
}
for (IndexMetadata indexMetadata : m_indexes) {
indexMetadata.process(getDescriptor(), null);
}
}
/**
* INTERNAL:
* Process the Inheritance metadata for a root of an inheritance
* hierarchy. One may or may not be specified for the entity class that is
* the root of the entity class hierarchy, so we need to default in this
* case. This method should only be called on the root of the inheritance
* hierarchy.
*/
protected void processInheritance() {
// Process the inheritance metadata first. Create one if one does not
// exist.
if (m_inheritance == null) {
m_inheritance = new InheritanceMetadata(getAnnotation(JPA_INHERITANCE), this);
}
m_inheritance.process(getDescriptor());
}
/**
* INTERNAL:
* Process the inheritance metadata for an inheritance subclass. The
* parent descriptor must be provided.
*/
public void processInheritancePrimaryKeyJoinColumns() {
// If there are no primary key join columns specified in XML, look for
// some defined through annotations.
if (m_primaryKeyJoinColumns.isEmpty()) {
// Process all the primary key join columns first.
if (isAnnotationPresent(JPA_PRIMARY_KEY_JOIN_COLUMNS)) {
MetadataAnnotation primaryKeyJoinColumns = getAnnotation(JPA_PRIMARY_KEY_JOIN_COLUMNS);
for (Object primaryKeyJoinColumn : primaryKeyJoinColumns.getAttributeArray("value")) {
m_primaryKeyJoinColumns.add(new PrimaryKeyJoinColumnMetadata((MetadataAnnotation) primaryKeyJoinColumn, this));
}
// Set the primary key foreign key metadata if one is specified.
if (primaryKeyJoinColumns.hasAttribute("foreignKey")) {
setPrimaryKeyForeignKey(new PrimaryKeyForeignKeyMetadata(primaryKeyJoinColumns.getAttributeAnnotation("foreignKey"), this));
}
}
// Process the single primary key join column second.
if (isAnnotationPresent(JPA_PRIMARY_KEY_JOIN_COLUMN)) {
PrimaryKeyJoinColumnMetadata primaryKeyJoinColumn = new PrimaryKeyJoinColumnMetadata(getAnnotation(JPA_PRIMARY_KEY_JOIN_COLUMN), this);
m_primaryKeyJoinColumns.add(primaryKeyJoinColumn);
// Set the primary key foreign key metadata if specified.
if (primaryKeyJoinColumn.hasForeignKey()) {
setPrimaryKeyForeignKey(new PrimaryKeyForeignKeyMetadata(primaryKeyJoinColumn.getForeignKey()));
}
}
}
// Process the primary key join columns into multiple table key fields.
addMultipleTableKeyFields(m_primaryKeyJoinColumns, getDescriptor().getPrimaryTable(), MetadataLogger.INHERITANCE_PK_COLUMN, MetadataLogger.INHERITANCE_FK_COLUMN);
// Process the primary key foreign key.
if (m_primaryKeyForeignKey != null) {
m_primaryKeyForeignKey.process(getDescriptor().getPrimaryTable());
}
}
/**
* INTERNAL:
* Process the listeners for this entity.
*/
public void processListeners(ClassLoader loader) {
// Step 1 - process the default listeners that were defined in XML.
// Default listeners process the class for additional lifecycle
// callback methods that are decorated with annotations.
// NOTE: We add the default listeners regardless if the exclude default
// listeners flag is set. This allows the user to change the exclude
// flag at runtime and have the default listeners available to them.
for (EntityListenerMetadata defaultListener : getProject().getDefaultListeners()) {
// We need to clone the default listeners. Can't re-use the
// same one for all the entities in the persistence unit.
((EntityListenerMetadata) defaultListener.clone()).process(this, loader, true);
}
// Step 2 - process the entity listeners that are defined on the entity
// class and mapped superclasses (taking metadata-complete into
// consideration). Go through the mapped superclasses first, top->down
// only if the exclude superclass listeners flag is not set.
discoverMappedSuperclassesAndInheritanceParents(true);
if (! getDescriptor().excludeSuperclassListeners()) {
int mappedSuperclassesSize = getMappedSuperclasses().size();
for (int i = mappedSuperclassesSize - 1; i >= 0; i--) {
getMappedSuperclasses().get(i).processEntityListeners(loader);
}
}
processEntityListeners(loader);
// Step 3 - process the entity class for lifecycle callback methods. Go
// through the mapped superclasses as well.
new EntityClassListenerMetadata(this).process(getMappedSuperclasses(), loader);
}
/**
* INTERNAL:
* Process the accessors for the given class.
* If we are within a TABLE_PER_CLASS inheritance hierarchy, our parent
* accessors will already have been added at this point.
* @see InheritanceMetadata process()
*/
@Override
public void processMappingAccessors() {
// Process our mapping accessors, and then perform the necessary
// validation checks for this entity.
super.processMappingAccessors();
// Validate the optimistic locking setting.
validateOptimisticLocking();
// Check that we have a simple pk. If it is a derived id, this will be
// run in a second pass
if (! hasDerivedId()){
// Validate we found a primary key.
validatePrimaryKey();
// Primary key has been validated, let's process those items that
// depend on it now.
// Process the SecondaryTable(s) metadata.
processSecondaryTables();
}
}
/**
* INTERNAL:
* Process a MetadataSecondaryTable. Do all the table name defaulting and
* set the correct, fully qualified name on the TopLink DatabaseTable.
*/
protected void processSecondaryTable(SecondaryTableMetadata secondaryTable) {
// Process any table defaults and log warning messages.
processTable(secondaryTable, secondaryTable.getName());
// Add the table to the descriptor.
getDescriptor().addTable(secondaryTable.getDatabaseTable());
// Get the primary key join column(s) and add the multiple table key fields.
addMultipleTableKeyFields(secondaryTable.getPrimaryKeyJoinColumns(), secondaryTable.getDatabaseTable(), MetadataLogger.SECONDARY_TABLE_PK_COLUMN, MetadataLogger.SECONDARY_TABLE_FK_COLUMN);
}
/**
* INTERNAL:
* Process secondary-table(s) for a given entity.
*/
protected void processSecondaryTables() {
// Ignore secondary tables when the descriptor is an EIS descriptor.
if (! getDescriptor().getClassDescriptor().isEISDescriptor()) {
MetadataAnnotation secondaryTable = getAnnotation(JPA_SECONDARY_TABLE);
MetadataAnnotation secondaryTables = getAnnotation(JPA_SECONDARY_TABLES);
if (m_secondaryTables.isEmpty()) {
// Look for a SecondaryTables annotation.
if (secondaryTables != null) {
for (Object table : secondaryTables.getAttributeArray("value")) {
processSecondaryTable(new SecondaryTableMetadata((MetadataAnnotation)table, this));
}
} else {
// Look for a SecondaryTable annotation
if (secondaryTable != null) {
processSecondaryTable(new SecondaryTableMetadata(secondaryTable, this));
}
}
} else {
if (secondaryTable != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, secondaryTable, getJavaClassName(), getLocation());
}
if (secondaryTables != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, secondaryTables, getJavaClassName(), getLocation());
}
for (SecondaryTableMetadata table : m_secondaryTables) {
processSecondaryTable(table);
}
}
}
}
/**
* INTERNAL:
* Process table information for the given metadata descriptor.
*/
protected void processTable() {
MetadataAnnotation table = getAnnotation(JPA_TABLE);
if (m_table == null) {
// Check for a table annotation. If no annotation is defined, the
// table will default.
processTable(new TableMetadata(table, this));
} else {
if (table != null) {
getLogger().logConfigMessage(MetadataLogger.OVERRIDE_ANNOTATION_WITH_XML, table, getJavaClassName(), getLocation());
}
processTable(m_table);
}
}
/**
* INTERNAL:
* Process a MetadataTable. Do all the table name defaulting and set the
* correct, fully qualified name on the TopLink DatabaseTable.
*/
protected void processTable(TableMetadata table) {
// Ignore secondary tables when the descriptor is an EIS descriptor.
if (! getDescriptor().getClassDescriptor().isEISDescriptor()) {
// Process any table defaults and log warning messages.
processTable(table, getDescriptor().getDefaultTableName());
// Set the table on the descriptor.
getDescriptor().setPrimaryTable(table.getDatabaseTable());
}
}
/**
* INTERNAL:
* Process any inheritance specifics. NOTE: Inheritance hierarchies are
* always processed from the top down so it is safe to assume that all
* our parents have already been processed fully. The only exception being
* when a root accessor doesn't know they are a root (defaulting case). In
* this case we'll tell the root accessor to process the inheritance
* metadata before continuing with our own processing.
*/
protected void processTableAndInheritance() {
// If we are an inheritance subclass, ensure our root is processed
// first since it has information its subclasses depend on.
if (getDescriptor().isInheritanceSubclass()) {
MetadataDescriptor rootDescriptor = getDescriptor().getInheritanceRootDescriptor();
EntityAccessor rootAccessor = (EntityAccessor) rootDescriptor.getClassAccessor();
// An entity who didn't know they were the root of an inheritance
// hierarchy (that is, does not define inheritance metadata) must
// process and default the inheritance metadata.
if (! rootAccessor.hasInheritance()) {
rootAccessor.processInheritance();
}
// Process the table metadata for this descriptor if either there
// is an inheritance specification (we're a new root) or if we are
// part of a joined or table per class inheritance strategy.
if (hasInheritance() || ! rootDescriptor.usesSingleTableInheritanceStrategy()) {
processTable();
}
// If this entity has inheritance metadata as well, then the
// inheritance strategy is mixed and we need to process the
// inheritance metadata to ensure this entity's subclasses process
// correctly.
if (hasInheritance()) {
// Process the inheritance metadata.
processInheritance();
} else {
// Process the inheritance details using the inheritance
// metadata from our parent. This will put the right
// inheritance policy on our descriptor.
rootAccessor.getInheritance().process(getDescriptor());
}
} else {
// Process the table metadata if there is some, otherwise default.
processTable();
// If we have inheritance metadata, then process it now. If we are
// an inheritance root class that doesn't know it, a subclass will
// force this processing to occur.
if (hasInheritance()) {
processInheritance();
}
}
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setCascadeOnDelete(Boolean cascadeOnDelete) {
m_cascadeOnDelete = cascadeOnDelete;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setClassExtractorName(String classExtractorName) {
m_classExtractorName = classExtractorName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setConverts(List<ConvertMetadata> converts) {
m_converts = converts;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setDiscriminatorColumn(DiscriminatorColumnMetadata discriminatorColumn) {
m_discriminatorColumn = discriminatorColumn;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setDiscriminatorValue(String discriminatorValue) {
m_discriminatorValue = discriminatorValue;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setEntityName(String entityName) {
m_entityName = entityName;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setIndexes(List<IndexMetadata> indexes) {
m_indexes = indexes;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setInheritance(InheritanceMetadata inheritance) {
m_inheritance = inheritance;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setNamedEntityGraphs(List<NamedEntityGraphMetadata> namedEntityGraphs) {
m_namedEntityGraphs = namedEntityGraphs;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setPrimaryKeyForeignKey(PrimaryKeyForeignKeyMetadata primaryKeyForeignKey) {
m_primaryKeyForeignKey = primaryKeyForeignKey;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setPrimaryKeyJoinColumns(List<PrimaryKeyJoinColumnMetadata> primaryKeyJoinColumns) {
m_primaryKeyJoinColumns = primaryKeyJoinColumns;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setSecondaryTables(List<SecondaryTableMetadata> secondaryTables) {
m_secondaryTables = secondaryTables;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setTable(TableMetadata table) {
m_table = table;
}
/**
* INTERNAL:
* Validate a OptimisticLocking(type=VERSION_COLUMN) setting. That is,
* validate that we found a version field. If not, throw an exception.
*/
protected void validateOptimisticLocking() {
if (getDescriptor().usesVersionColumnOptimisticLocking() && ! getDescriptor().usesOptimisticLocking()) {
throw ValidationException.optimisticLockingVersionElementNotSpecified(getJavaClass());
}
}
/**
* INTERNAL:
* Call this method after a primary key should have been found.
*/
protected void validatePrimaryKey() {
// If this descriptor has a composite primary key, check that all
// our composite primary key attributes were validated.
if (getDescriptor().hasCompositePrimaryKey()) {
if (getDescriptor().pkClassWasNotValidated()) {
throw ValidationException.invalidCompositePKSpecification(getJavaClass(), getDescriptor().getPKClassName());
}
// Log a warning to the user that they have specified multiple id
// fields without an id class specification. If they are using a
// @PrimaryKey specification don't issue the warning.
if (! getDescriptor().hasPKClass() && ! getDescriptor().hasPrimaryKey()) {
getLogger().logWarningMessage(MetadataLogger.MULTIPLE_ID_FIELDS_WITHOUT_ID_CLASS, getJavaClassName());
}
} else {
// Descriptor has a single primary key. Validate an id attribute was
// found, unless we are an inheritance subclass or an aggregate descriptor.
if (! getDescriptor().hasPrimaryKeyFields() && ! getDescriptor().isInheritanceSubclass()) {
throw ValidationException.noPrimaryKeyAnnotationsFound(getJavaClass());
}
}
}
}