| /* |
| * 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: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 05/16/2008-1.0M8 Guy Pelletier |
| // - 218084: Implement metadata merging functionality between mapping files |
| // 08/27/2008-1.1 Guy Pelletier |
| // - 211329: Add sequencing on non-id attribute(s) support to the EclipseLink-ORM.XML Schema |
| // 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. |
| // 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/24/2009-2.0 Guy Pelletier |
| // - 270011: JPA 2.0 MappedById support |
| // 09/29/2009-2.0 Guy Pelletier |
| // - 282553: JPA 2.0 JoinTable support for OneToOne and ManyToOne |
| // 03/08/2010-2.1 Guy Pelletier |
| // - 303632: Add attribute-type for mapping attributes to EclipseLink-ORM |
| // 03/29/2010-2.1 Guy Pelletier |
| // - 267217: Add Named Access Type to EclipseLink-ORM |
| // 04/09/2010-2.1 Guy Pelletier |
| // - 307050: Add defaults for access methods of a VIRTUAL access type |
| // 04/27/2010-2.1 Guy Pelletier |
| // - 309856: MappedSuperclasses from XML are not being initialized properly |
| // 05/14/2010-2.1 Guy Pelletier |
| // - 253083: Add support for dynamic persistence using ORM.xml/eclipselink-orm.xml |
| // 08/19/2010-2.2 Guy Pelletier |
| // - 282733: Add plural converter annotations |
| // 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) |
| // 01/25/2011-2.3 Guy Pelletier |
| // - 333913: @OrderBy and <order-by/> without arguments should order by primary |
| // 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) |
| // 07/11/2011-2.4 Guy Pelletier |
| // - 343632: Can't map a compound constraint because of exception: |
| // The reference column name [y] mapped on the element [field x] |
| // does not correspond to a valid field on the mapping reference |
| // 11/10/2011-2.4 Guy Pelletier |
| // - 357474: Address primaryKey option from tenant discriminator column |
| // 11/19/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| // 09/02/2019-3.0 Alexandre Jacob |
| // - 527415: Fix code when locale is tr, az or lt |
| package org.eclipse.persistence.internal.jpa.metadata.accessors; |
| |
| import java.lang.annotation.Annotation; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| |
| import org.eclipse.persistence.annotations.Converter; |
| import org.eclipse.persistence.annotations.Converters; |
| import org.eclipse.persistence.annotations.HashPartitioning; |
| import org.eclipse.persistence.annotations.ObjectTypeConverter; |
| import org.eclipse.persistence.annotations.ObjectTypeConverters; |
| import org.eclipse.persistence.annotations.Partitioned; |
| import org.eclipse.persistence.annotations.Partitioning; |
| import org.eclipse.persistence.annotations.PinnedPartitioning; |
| import org.eclipse.persistence.annotations.RangePartitioning; |
| import org.eclipse.persistence.annotations.ReplicationPartitioning; |
| import org.eclipse.persistence.annotations.SerializedConverter; |
| import org.eclipse.persistence.annotations.SerializedConverters; |
| import org.eclipse.persistence.annotations.StructConverter; |
| import org.eclipse.persistence.annotations.StructConverters; |
| import org.eclipse.persistence.annotations.TypeConverter; |
| import org.eclipse.persistence.annotations.TypeConverters; |
| import org.eclipse.persistence.annotations.UnionPartitioning; |
| import org.eclipse.persistence.annotations.ValuePartitioning; |
| import org.eclipse.persistence.annotations.RoundRobinPartitioning; |
| import org.eclipse.persistence.descriptors.SingleTableMultitenantPolicy; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| |
| import org.eclipse.persistence.internal.helper.DatabaseField; |
| import org.eclipse.persistence.internal.helper.Helper; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.mappings.MappingAccessor; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAccessibleObject; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotatedElement; |
| 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.PrimaryKeyJoinColumnMetadata; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.converters.ConverterMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.converters.ObjectTypeConverterMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.converters.SerializedConverterMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.converters.StructConverterMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.converters.TypeConverterMetadata; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.mappings.AccessMethodsMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.PartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.HashPartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.PinnedPartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.RangePartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.ReplicationPartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.RoundRobinPartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.UnionPartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.partitioning.ValuePartitioningMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.tables.TableMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings; |
| |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataProject; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor; |
| import org.eclipse.persistence.internal.jpa.metadata.ORMetadata; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ACCESS; |
| |
| /** |
| * INTERNAL: |
| * Common metadata accessor level for mappings and classes. |
| * |
| * 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 TopLink EJB 3.0 Reference Implementation |
| */ |
| public abstract class MetadataAccessor extends ORMetadata { |
| private AccessMethodsMetadata m_accessMethods; |
| |
| private List<ConverterMetadata> m_converters = new ArrayList<ConverterMetadata>(); |
| private List<ObjectTypeConverterMetadata> m_objectTypeConverters = new ArrayList<ObjectTypeConverterMetadata>(); |
| private List<StructConverterMetadata> m_structConverters = new ArrayList<StructConverterMetadata>(); |
| private List<TypeConverterMetadata> m_typeConverters = new ArrayList<TypeConverterMetadata>(); |
| private List<SerializedConverterMetadata> m_serializedConverters = new ArrayList<SerializedConverterMetadata>(); |
| private List<PropertyMetadata> m_properties = new ArrayList<PropertyMetadata>(); |
| |
| private MetadataDescriptor m_descriptor; |
| |
| private PartitioningMetadata m_partitioning; |
| private ReplicationPartitioningMetadata m_replicationPartitioning; |
| private RoundRobinPartitioningMetadata m_roundRobinPartitioning; |
| private PinnedPartitioningMetadata m_pinnedPartitioning; |
| private RangePartitioningMetadata m_rangePartitioning; |
| private ValuePartitioningMetadata m_valuePartitioning; |
| private UnionPartitioningMetadata m_unionPartitioning; |
| private HashPartitioningMetadata m_hashPartitioning; |
| private String m_partitioned; |
| |
| private String m_access; |
| private String m_name; |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public MetadataAccessor(String xmlElement) { |
| super(xmlElement); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public MetadataAccessor(MetadataAnnotation annotation, MetadataAccessibleObject accessibleObject, MetadataDescriptor descriptor, MetadataProject project) { |
| super(annotation, accessibleObject, project); |
| setDescriptor(descriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean equals(Object objectToCompare) { |
| if (objectToCompare instanceof MetadataAccessor) { |
| MetadataAccessor accessor = (MetadataAccessor) objectToCompare; |
| |
| if (! valuesMatch(m_accessMethods, accessor.getAccessMethods())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_converters, accessor.getConverters())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_objectTypeConverters, accessor.getObjectTypeConverters())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_structConverters, accessor.getStructConverters())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_typeConverters, accessor.getTypeConverters())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_serializedConverters, accessor.getSerializedConverters())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_properties, accessor.getProperties())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_access, accessor.getAccess())) { |
| return false; |
| } |
| |
| return valuesMatch(m_name, accessor.getName()); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = m_accessMethods != null ? m_accessMethods.hashCode() : 0; |
| result = 31 * result + (m_converters != null ? m_converters.hashCode() : 0); |
| result = 31 * result + (m_objectTypeConverters != null ? m_objectTypeConverters.hashCode() : 0); |
| result = 31 * result + (m_structConverters != null ? m_structConverters.hashCode() : 0); |
| result = 31 * result + (m_typeConverters != null ? m_typeConverters.hashCode() : 0); |
| result = 31 * result + (m_serializedConverters != null ? m_serializedConverters.hashCode() : 0); |
| result = 31 * result + (m_properties != null ? m_properties.hashCode() : 0); |
| result = 31 * result + (m_access != null ? m_access.hashCode() : 0); |
| result = 31 * result + (m_name != null ? m_name.hashCode() : 0); |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getAccess() { |
| return m_access; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the accessible object for this accessor. |
| */ |
| @Override |
| public MetadataAnnotatedElement getAccessibleObject() { |
| return (MetadataAnnotatedElement) super.getAccessibleObject(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public AccessMethodsMetadata getAccessMethods() { |
| return m_accessMethods; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the annotated element for this accessor. |
| */ |
| public MetadataAnnotatedElement getAnnotatedElement() { |
| return getAccessibleObject(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the annotated element name for this accessor. |
| */ |
| public String getAnnotatedElementName() { |
| return getAnnotatedElement().toString(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the annotation if it exists. This method should only be called |
| * for non JPA annotations as loading those annotations classes is ok (and |
| * available). |
| * |
| * JPA annotations should be referenced only by name as to not introduce a |
| * compile dependency. |
| */ |
| public MetadataAnnotation getAnnotation(Class annotation) { |
| return getAnnotation(annotation.getName()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the annotation if it exists. |
| */ |
| protected abstract MetadataAnnotation getAnnotation(String annotation); |
| |
| /** |
| * INTERNAL: |
| * Return the attribute name for this accessor. |
| */ |
| public String getAttributeName() { |
| return getAccessibleObject().getAttributeName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<ConverterMetadata> getConverters() { |
| return m_converters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the upper cased attribute name for this accessor. Used when |
| * defaulting. |
| */ |
| protected String getDefaultAttributeName() { |
| return (getProject().useDelimitedIdentifier()) ? getAttributeName() : getAttributeName().toUpperCase(Locale.ROOT); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the MetadataDescriptor for this accessor. |
| */ |
| public MetadataDescriptor getDescriptor() { |
| return m_descriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the java class tied to this class accessor's descriptor. |
| */ |
| public MetadataClass getDescriptorJavaClass() { |
| return m_descriptor.getJavaClass(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public HashPartitioningMetadata getHashPartitioning() { |
| return m_hashPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * To satisfy the abstract getIdentifier() method from ORMetadata. |
| */ |
| @Override |
| public String getIdentifier() { |
| return getName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the java class associated with this accessor's descriptor. |
| */ |
| public MetadataClass getJavaClass() { |
| return m_descriptor.getJavaClass(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the java class that defines this accessor. |
| */ |
| protected String getJavaClassName() { |
| return getJavaClass().getName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getName() { |
| return m_name; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<ObjectTypeConverterMetadata> getObjectTypeConverters() { |
| return m_objectTypeConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getPartitioned() { |
| return m_partitioned; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public PartitioningMetadata getPartitioning() { |
| return m_partitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public PinnedPartitioningMetadata getPinnedPartitioning() { |
| return m_pinnedPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<PropertyMetadata> getProperties() { |
| return m_properties; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public RangePartitioningMetadata getRangePartitioning() { |
| return m_rangePartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the referenced field. If the referencedColumnName is not |
| * specified, it will default to the primary key of the referenced table. |
| */ |
| protected DatabaseField getReferencedField(String referencedColumnName, MetadataDescriptor referenceDescriptor, String context) { |
| return getReferencedField(referencedColumnName, referenceDescriptor, context, false); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the referenced field. If the referencedColumnName is not |
| * specified, it will default to the primary key of the referenced table. |
| */ |
| protected DatabaseField getReferencedField(String referencedColumnName, MetadataDescriptor referenceDescriptor, String context, boolean isForAggregateCollection) { |
| DatabaseField referenceField; |
| |
| if (referencedColumnName == null || referencedColumnName.equals("")) { |
| referenceField = referenceDescriptor.getPrimaryKeyField(); |
| |
| // <hack> If we are an inheritance subclass in a joined strategy, |
| // for an aggregate collection, we need to return the multi table |
| // primary key field if there is one. All other mappings seem to be |
| // happy with the primary key field ... this seems to be a bug with |
| // AggregateCollectionMapping in that it doesn't resolve the |
| // primary key fields correctly </hack> |
| // TODO: have a look at this again at some point. |
| if (referenceDescriptor.isInheritanceSubclass() && isForAggregateCollection) { |
| referenceField = referenceDescriptor.getPrimaryKeyJoinColumnAssociationField(referenceField); |
| } |
| |
| // Log the defaulting field name based on the given context. |
| getLogger().logConfigMessage(context, getAnnotatedElementName(), referenceField.getName()); |
| } else { |
| // Let's try to look up the referenced field using the referenced |
| // column name. |
| referenceField = referenceDescriptor.getField(referencedColumnName); |
| |
| if (referenceField == null) { |
| // Ok so we still haven't found the referenced field. We will |
| // need to take the delimeters and the upper cassing flag into |
| // consideration. To do that, we must use a DatabaseField since |
| // the logic resides there. It would be nice to avoid creating |
| // database fields to look up actual referenced fields but for |
| // now this will do (better than what we had before :-) ). |
| DatabaseField lookupField = new DatabaseField(); |
| setFieldName(lookupField, referencedColumnName); |
| referenceField = referenceDescriptor.getField(lookupField.getNameForComparisons()); |
| |
| if (referenceField == null) { |
| // So here's the thing, there are a few reasons why we |
| // wouldn't have a reference field at this point: |
| // |
| // 1 - if we are processing a mapping accessor (e.g. element |
| // collection) for a mapped superclass descriptor |
| // (metamodel) where the mapped superclass may or may not |
| // define a source ID field. So we just don't know and can't |
| // know in this context) |
| // |
| // 2 - the user has mapped a reference column to a non id |
| // field. See bug 343632 for more information. It's an |
| // example of going beyond the spec since the user has |
| // mapped a reference column name to another relationship |
| // mapping. We can't lookup the field for this mapping since |
| // we can't guaranty that its accessor has been processed. |
| // Basic (id) accessors are all processed in stage one, |
| // relationship accessors are processed only in stage 3 |
| // after we know all the basics (ids) have been processed. |
| // |
| // 3 - the field just doesn't exist. In this case, the user |
| // will eventually get a DB error on persist. |
| // |
| // By returning the lookup field, what do we lose? Well, we |
| // could lose some field definitions (precision, length, |
| // scale) which would generate different DDL (if turned on). |
| // |
| // What do we gain? We continue to do our best to lookup |
| // reference fields and keep them in sync. We do that |
| // correctly up to what the spec requires. Anything beyond |
| // that we just assume the field is valid and return it (as |
| // we did in the past, before bug 317286) and users can map |
| // to non-id fields. |
| // |
| referenceField = lookupField; |
| referenceField.setTable(referenceDescriptor.getPrimaryKeyTable()); |
| |
| // Log warning to the user that we will use the referenced |
| // column name they provided. |
| getLogger().logWarningMessage(MetadataLogger.REFERENCED_COLUMN_NOT_FOUND, referencedColumnName, getAnnotatedElement()); |
| } |
| } |
| } |
| |
| return referenceField; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public ReplicationPartitioningMetadata getReplicationPartitioning() { |
| return m_replicationPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public RoundRobinPartitioningMetadata getRoundRobinPartitioning() { |
| return m_roundRobinPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<StructConverterMetadata> getStructConverters() { |
| return m_structConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<TypeConverterMetadata> getTypeConverters() { |
| return m_typeConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<SerializedConverterMetadata> getSerializedConverters() { |
| return m_serializedConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public UnionPartitioningMetadata getUnionPartitioning() { |
| return m_unionPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the upper case java class that defines this accessor. |
| */ |
| protected String getUpperCaseShortJavaClassName() { |
| return Helper.getShortClassName(getJavaClassName()).toUpperCase(Locale.ROOT); |
| } |
| |
| /** |
| * INTERNAL: |
| * Helper method to return a string value if specified, otherwise returns |
| * the default value. |
| */ |
| protected Integer getValue(Integer value, Integer defaultValue) { |
| return org.eclipse.persistence.internal.jpa.metadata.MetadataHelper.getValue(value, defaultValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * Helper method to return a string value if specified, otherwise returns |
| * the default value. |
| */ |
| protected String getValue(String value, String defaultValue) { |
| return org.eclipse.persistence.internal.jpa.metadata.MetadataHelper.getValue(value, defaultValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public ValuePartitioningMetadata getValuePartitioning() { |
| return m_valuePartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean hasAccess() { |
| return m_access != null; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean hasAccessMethods() { |
| return m_accessMethods != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Called from annotation and xml initialization. |
| */ |
| public void initAccess() { |
| // Look for an annotation as long as an access type hasn't already been |
| // loaded from XML (meaning m_access will not be null at this point) |
| if (m_access == null) { |
| MetadataAnnotation access = getAnnotation(JPA_ACCESS); |
| if (access != null) { |
| setAccess(access.getAttributeString("value")); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This method should be subclassed in those methods that need to do |
| * extra initialization. |
| */ |
| public void initXMLAccessor(MetadataDescriptor descriptor, MetadataProject project) { |
| setProject(project); |
| setDescriptor(descriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) { |
| super.initXMLObject(accessibleObject, entityMappings); |
| |
| // Initialize single objects. |
| initXMLObject(m_accessMethods, accessibleObject); |
| |
| initXMLObject(m_partitioning, accessibleObject); |
| initXMLObject(m_replicationPartitioning, accessibleObject); |
| initXMLObject(m_roundRobinPartitioning, accessibleObject); |
| initXMLObject(m_pinnedPartitioning, accessibleObject); |
| initXMLObject(m_rangePartitioning, accessibleObject); |
| initXMLObject(m_valuePartitioning, accessibleObject); |
| initXMLObject(m_hashPartitioning, accessibleObject); |
| initXMLObject(m_unionPartitioning, accessibleObject); |
| |
| // Initialize lists of objects. |
| initXMLObjects(m_converters, accessibleObject); |
| initXMLObjects(m_objectTypeConverters, accessibleObject); |
| initXMLObjects(m_structConverters, accessibleObject); |
| initXMLObjects(m_typeConverters, accessibleObject); |
| initXMLObjects(m_serializedConverters, accessibleObject); |
| initXMLObjects(m_properties, accessibleObject); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if the annotation exists. This method should only be called |
| * for non native annotations (i.e. JPA) as loading native annotations |
| * classes is ok since we know they are available from the jar. |
| * |
| * JPA annotations should be referenced only by name as to not introduce a |
| * compile dependency. |
| */ |
| public boolean isAnnotationPresent(Class<? extends Annotation> annotation) { |
| return isAnnotationPresent(annotation.getName()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the annotation if it exists. |
| */ |
| public abstract boolean isAnnotationPresent(String annotation); |
| |
| /** |
| * Subclasses must handle this flag. |
| */ |
| public abstract boolean isProcessed(); |
| |
| /** |
| * INTERNAL: |
| * We currently limit this merging to the ClassAccessor level. |
| */ |
| @Override |
| public void merge(ORMetadata metadata) { |
| MetadataAccessor accessor = (MetadataAccessor) metadata; |
| |
| // Simple object merging. |
| m_access = (String) mergeSimpleObjects(m_access, accessor.getAccess(), accessor, "@access"); |
| |
| // ORMetadata object merging. |
| m_accessMethods = (AccessMethodsMetadata) mergeORObjects(m_accessMethods, accessor.getAccessMethods()); |
| |
| // ORMetadata list merging. |
| m_converters = mergeORObjectLists(m_converters, accessor.getConverters()); |
| m_objectTypeConverters = mergeORObjectLists(m_objectTypeConverters, accessor.getObjectTypeConverters()); |
| m_structConverters = mergeORObjectLists(m_structConverters, accessor.getStructConverters()); |
| m_typeConverters = mergeORObjectLists(m_typeConverters, accessor.getTypeConverters()); |
| m_serializedConverters = mergeORObjectLists(m_serializedConverters, accessor.getSerializedConverters()); |
| m_properties = mergeORObjectLists(m_properties, accessor.getProperties()); |
| |
| m_partitioned = (String) mergeSimpleObjects(m_partitioned, accessor.getPartitioned(), accessor, "<partitioned>"); |
| m_partitioning = (PartitioningMetadata) mergeORObjects(m_partitioning, accessor.getPartitioning()); |
| m_replicationPartitioning = (ReplicationPartitioningMetadata) mergeORObjects(m_replicationPartitioning, accessor.getReplicationPartitioning()); |
| m_roundRobinPartitioning = (RoundRobinPartitioningMetadata) mergeORObjects(m_roundRobinPartitioning, accessor.getRoundRobinPartitioning()); |
| m_pinnedPartitioning = (PinnedPartitioningMetadata) mergeORObjects(m_pinnedPartitioning, accessor.getPinnedPartitioning()); |
| m_rangePartitioning = (RangePartitioningMetadata) mergeORObjects(m_rangePartitioning, accessor.getRangePartitioning()); |
| m_valuePartitioning = (ValuePartitioningMetadata) mergeORObjects(m_valuePartitioning, accessor.getValuePartitioning()); |
| m_hashPartitioning = (HashPartitioningMetadata) mergeORObjects(m_hashPartitioning, accessor.getHashPartitioning()); |
| m_unionPartitioning = (UnionPartitioningMetadata) mergeORObjects(m_unionPartitioning, accessor.getUnionPartitioning()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Every accessor knows how to process themselves since they have all the |
| * information they need. |
| */ |
| public abstract void process(); |
| |
| /** |
| * INTERNAL: |
| * Process and add the globally defined converters to the project. |
| */ |
| public void processConverters() { |
| // Process the custom converters if defined. |
| processCustomConverters(); |
| |
| // Process the object type converters if defined. |
| processObjectTypeConverters(); |
| |
| // Process the type converters if defined. |
| processTypeConverters(); |
| |
| // Process the serialized converters if defined. |
| processSerializedConverters(); |
| |
| // Process the struct converters if defined |
| processStructConverters(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the XML defined converters and check for a Converter annotation. |
| */ |
| protected void processCustomConverters() { |
| // Check for XML defined converters. |
| for (ConverterMetadata converter : m_converters) { |
| getProject().addConverter(converter); |
| } |
| |
| // Check for a Converters annotation |
| MetadataAnnotation converters = getAnnotation(Converters.class); |
| if (converters != null) { |
| for (Object converter : converters.getAttributeArray("value")) { |
| getProject().addConverter(new ConverterMetadata((MetadataAnnotation) converter, this)); |
| } |
| } |
| |
| // Check for a Converter annotation. |
| MetadataAnnotation converter = getAnnotation(Converter.class); |
| if (converter != null) { |
| getProject().addConverter(new ConverterMetadata(converter, this)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the XML defined object type converters and check for an |
| * ObjectTypeConverter annotation. |
| */ |
| protected void processObjectTypeConverters() { |
| // Check for XML defined object type converters. |
| for (ObjectTypeConverterMetadata objectTypeConverter : m_objectTypeConverters) { |
| getProject().addConverter(objectTypeConverter); |
| } |
| |
| // Check for an ObjectTypeConverters annotation |
| if (isAnnotationPresent(ObjectTypeConverters.class)) { |
| for (Object objectTypeConverter : getAnnotation(ObjectTypeConverters.class).getAttributeArray("value")) { |
| getProject().addConverter(new ObjectTypeConverterMetadata((MetadataAnnotation) objectTypeConverter, this)); |
| } |
| } |
| |
| // Check for an ObjectTypeConverter annotation. |
| if (isAnnotationPresent(ObjectTypeConverter.class)) { |
| getProject().addConverter(new ObjectTypeConverterMetadata(getAnnotation(ObjectTypeConverter.class), this)); |
| } |
| } |
| |
| /** |
| * Set the policy on the descriptor or mapping. |
| */ |
| public void processPartitioned(String name) { |
| if (this instanceof ClassAccessor) { |
| if (getDescriptor().getClassDescriptor().getPartitioningPolicy() != null) { |
| // We must be processing a mapped superclass setting for an |
| // entity that has its own setting. Ignore it and log a warning. |
| getLogger().logConfigMessage(MetadataLogger.IGNORE_MAPPED_SUPERCLASS_ANNOTATION, Partitioned.class, getJavaClass(), getDescriptor().getJavaClass()); |
| } |
| getDescriptor().getClassDescriptor().setPartitioningPolicyName(name); |
| } else if (this instanceof MappingAccessor) { |
| ((ForeignReferenceMapping)((MappingAccessor)this).getMapping()).setPartitioningPolicyName(name); |
| } |
| } |
| |
| /** |
| * Process the partitioning policies defined on this element. |
| */ |
| protected void processPartitioning() { |
| boolean found = false; |
| // Check for XML defined partitioning. |
| if (m_replicationPartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_replicationPartitioning); |
| } |
| if (m_roundRobinPartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_roundRobinPartitioning); |
| } |
| if (m_partitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_partitioning); |
| } |
| if (m_rangePartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_rangePartitioning); |
| } |
| if (m_valuePartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_valuePartitioning); |
| } |
| if (m_hashPartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_hashPartitioning); |
| } |
| if (m_unionPartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_unionPartitioning); |
| } |
| if (m_pinnedPartitioning != null) { |
| found = true; |
| getProject().addPartitioningPolicy(m_pinnedPartitioning); |
| } |
| |
| // Check for partitioning annotations. |
| MetadataAnnotation annotation = getAnnotation(Partitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new PartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(ReplicationPartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new ReplicationPartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(RoundRobinPartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new RoundRobinPartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(UnionPartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new UnionPartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(RangePartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new RangePartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(ValuePartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new ValuePartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(ValuePartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new ValuePartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(PinnedPartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new PinnedPartitioningMetadata(annotation, this)); |
| } |
| annotation = getAnnotation(HashPartitioning.class); |
| if (annotation != null) { |
| found = true; |
| getProject().addPartitioningPolicy(new HashPartitioningMetadata(annotation, this)); |
| } |
| boolean processed = false; |
| if (m_partitioned != null) { |
| processed = true; |
| processPartitioned(m_partitioned); |
| } |
| annotation = getAnnotation(Partitioned.class); |
| if (!processed && annotation != null) { |
| processed = true; |
| processPartitioned(annotation.getAttributeString("value")); |
| } |
| if (found && !processed) { |
| getLogger().logWarningMessage(MetadataLogger.WARNING_PARTIONED_NOT_SET, getJavaClass(), getAccessibleObject()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the primary key join columms for this accessors annotated element. |
| */ |
| protected List<PrimaryKeyJoinColumnMetadata> processPrimaryKeyJoinColumns(List<PrimaryKeyJoinColumnMetadata> primaryKeyJoinColumns) { |
| // If the primary key join columns were not specified (that is empty), |
| // this call will add any defaulted columns as necessary. |
| if (primaryKeyJoinColumns.isEmpty()) { |
| if (getDescriptor().hasCompositePrimaryKey()) { |
| // Add a default one for each part of the composite primary |
| // key. Foreign and primary key to have the same name. |
| for (DatabaseField primaryKeyField : getDescriptor().getPrimaryKeyFields()) { |
| PrimaryKeyJoinColumnMetadata primaryKeyJoinColumn = new PrimaryKeyJoinColumnMetadata(getProject()); |
| primaryKeyJoinColumn.setReferencedColumnName(primaryKeyField.getName()); |
| primaryKeyJoinColumn.setName(primaryKeyField.getName()); |
| primaryKeyJoinColumns.add(primaryKeyJoinColumn); |
| } |
| } else { |
| // Add a default one for the single case, not setting any |
| // foreign and primary key names. They will default based |
| // on which accessor is using them. |
| primaryKeyJoinColumns.add(new PrimaryKeyJoinColumnMetadata(getProject())); |
| } |
| } else { |
| // If we are a multitenant entity and the number of primary key join |
| // columns does not equal the number of primary key fields we must |
| // add the multitenant primary key tenant discriminator fields. |
| if (getDescriptor().hasSingleTableMultitenant() && primaryKeyJoinColumns.size() != getDescriptor().getPrimaryKeyFields().size()) { |
| SingleTableMultitenantPolicy policy = (SingleTableMultitenantPolicy) getDescriptor().getClassDescriptor().getMultitenantPolicy(); |
| Map<DatabaseField, String> tenantFields = policy.getTenantDiscriminatorFields(); |
| |
| // Go through the key sets ... |
| for (DatabaseField tenantField : tenantFields.keySet()) { |
| if (tenantField.isPrimaryKey()) { |
| PrimaryKeyJoinColumnMetadata primaryKeyJoinColumn = new PrimaryKeyJoinColumnMetadata(); |
| primaryKeyJoinColumn.setName(tenantField.getName()); |
| primaryKeyJoinColumn.setReferencedColumnName(tenantField.getName()); |
| primaryKeyJoinColumn.setProject(getProject()); |
| primaryKeyJoinColumns.add(primaryKeyJoinColumn); |
| } |
| } |
| } |
| } |
| |
| // Now validate what we have. |
| if (getDescriptor().hasCompositePrimaryKey()) { |
| // All the primary and foreign key field names should be specified. |
| // The number of join columns need not match the number of primary |
| // key columns since we allow joining to partials. |
| for (PrimaryKeyJoinColumnMetadata pkJoinColumn : primaryKeyJoinColumns) { |
| if (pkJoinColumn.isPrimaryKeyFieldNotSpecified() || pkJoinColumn.isForeignKeyFieldNotSpecified()) { |
| throw ValidationException.incompletePrimaryKeyJoinColumnsSpecified(getAnnotatedElement()); |
| } |
| } |
| } else { |
| if (primaryKeyJoinColumns.size() > 1) { |
| throw ValidationException.excessivePrimaryKeyJoinColumnsSpecified(getAnnotatedElement()); |
| } |
| } |
| |
| return primaryKeyJoinColumns; |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the XML defined struct converters and check for a StructConverter |
| * annotation. |
| */ |
| protected void processStructConverters() { |
| // Check for XML defined struct converters. |
| for (StructConverterMetadata structConverter : m_structConverters) { |
| getProject().addConverter(structConverter); |
| } |
| |
| // Check for a StructConverters annotation |
| if (isAnnotationPresent(StructConverters.class)) { |
| for (Object structConverter : getAnnotation(StructConverters.class).getAttributeArray("value")) { |
| getProject().addConverter(new StructConverterMetadata((MetadataAnnotation) structConverter, this)); |
| } |
| } |
| |
| // Check for a StructConverter annotation. |
| if (isAnnotationPresent(StructConverter.class)) { |
| getProject().addConverter(new StructConverterMetadata(getAnnotation(StructConverter.class), this)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Common table processing for table, secondary table, join table and |
| * collection table. |
| */ |
| protected void processTable(TableMetadata table, String defaultName) { |
| // Process the default values. |
| getProject().processTable(table, defaultName, m_descriptor.getDefaultCatalog(), m_descriptor.getDefaultSchema(), this); |
| } |
| |
| /** |
| * INTERNAL: |
| * Process a the XML defined type converters and check for a TypeConverter |
| * annotation. |
| */ |
| protected void processTypeConverters() { |
| // Check for XML defined type converters. |
| for (TypeConverterMetadata typeConverter : m_typeConverters) { |
| getProject().addConverter(typeConverter); |
| } |
| |
| // Check for a TypeConverters annotation |
| if (isAnnotationPresent(TypeConverters.class)) { |
| for (Object typeConverter : getAnnotation(TypeConverters.class).getAttributeArray("value")) { |
| getProject().addConverter(new TypeConverterMetadata((MetadataAnnotation) typeConverter, this)); |
| } |
| } |
| |
| // Check for an TypeConverter annotation. |
| if (isAnnotationPresent(TypeConverter.class)) { |
| getProject().addConverter(new TypeConverterMetadata(getAnnotation(TypeConverter.class), this)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process a the XML defined serialized converters and check for a SerializedConverter |
| * annotation. |
| */ |
| protected void processSerializedConverters() { |
| // Check for XML defined serialized converters. |
| for (SerializedConverterMetadata serializedConverter : m_serializedConverters) { |
| getProject().addConverter(serializedConverter); |
| } |
| |
| // Check for a SerializedConverters annotation |
| if (isAnnotationPresent(SerializedConverters.class)) { |
| for (Object serializedConverter : getAnnotation(SerializedConverters.class).getAttributeArray("value")) { |
| getProject().addConverter(new SerializedConverterMetadata((MetadataAnnotation) serializedConverter, this)); |
| } |
| } |
| |
| // Check for an TypeConverter annotation. |
| if (isAnnotationPresent(SerializedConverter.class)) { |
| getProject().addConverter(new SerializedConverterMetadata(getAnnotation(SerializedConverter.class), this)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setAccess(String access) { |
| m_access = access; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setAccessMethods(AccessMethodsMetadata accessMethods){ |
| m_accessMethods = accessMethods; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setConverters(List<ConverterMetadata> converters) { |
| m_converters = converters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the metadata descriptor for this accessor. When setting the |
| * descriptor on entities, the owning descriptor is set to this descriptor. |
| */ |
| public void setDescriptor(MetadataDescriptor descriptor) { |
| m_descriptor = descriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setHashPartitioning(HashPartitioningMetadata hashPartitioning) { |
| m_hashPartitioning = hashPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setName(String name) { |
| m_name = name; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setObjectTypeConverters(List<ObjectTypeConverterMetadata> objectTypeConverters) { |
| m_objectTypeConverters = objectTypeConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPartitioned(String partitioned) { |
| m_partitioned = partitioned; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPartitioning(PartitioningMetadata partitioning) { |
| m_partitioning = partitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPinnedPartitioning(PinnedPartitioningMetadata pinnedPartitioning) { |
| m_pinnedPartitioning = pinnedPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setProperties(List<PropertyMetadata> properties) { |
| m_properties = properties; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setRangePartitioning(RangePartitioningMetadata rangePartitioning) { |
| m_rangePartitioning = rangePartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setReplicationPartitioning(ReplicationPartitioningMetadata replicationPartitioning) { |
| m_replicationPartitioning = replicationPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setRoundRobinPartitioning(RoundRobinPartitioningMetadata roundRobinPartitioning) { |
| m_roundRobinPartitioning = roundRobinPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setStructConverters(List<StructConverterMetadata> structConverters) { |
| m_structConverters = structConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setTypeConverters(List<TypeConverterMetadata> typeConverters) { |
| m_typeConverters = typeConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setSerializedConverters(List<SerializedConverterMetadata> serializedConverters) { |
| m_serializedConverters = serializedConverters; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setUnionPartitioning(UnionPartitioningMetadata unionPartitioning) { |
| m_unionPartitioning = unionPartitioning; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setValuePartitioning(ValuePartitioningMetadata valuePartitioning) { |
| m_valuePartitioning = valuePartitioning; |
| } |
| } |
| |