| /* |
| * Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 1998, 2018 IBM Corporation. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // 05/16/2008-1.0M8 Guy Pelletier |
| // - 218084: Implement metadata merging functionality between mapping files |
| // 09/23/2008-1.1 Guy Pelletier |
| // - 241651: JPA 2.0 Access Type support |
| // 02/06/2009-2.0 Guy Pelletier |
| // - 248293: JPA 2.0 Element Collections (part 2) |
| // 02/25/2009-2.0 Guy Pelletier |
| // - 265359: JPA 2.0 Element Collections - Metadata processing portions |
| // 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 |
| // 05/1/2009-2.0 Guy Pelletier |
| // - 249033: JPA 2.0 Orphan removal |
| // 06/02/2009-2.0 Guy Pelletier |
| // - 278768: JPA 2.0 Association Override Join Table |
| // 06/09/2009-2.0 Guy Pelletier |
| // - 249037: JPA 2.0 persisting list item index |
| // 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 |
| // 11/23/2009-2.0 Guy Pelletier |
| // - 295790: JPA 2.0 adding @MapsId to one entity causes initialization errors in other entities |
| // 03/29/2010-2.1 Guy Pelletier |
| // - 267217: Add Named Access Type to EclipseLink-ORM |
| // 04/27/2010-2.1 Guy Pelletier |
| // - 309856: MappedSuperclasses from XML are not being initialized properly |
| // 06/14/2010-2.2 Guy Pelletier |
| // - 264417: Table generation is incorrect for JoinTables in AssociationOverrides |
| // 07/05/2010-2.1.1 Guy Pelletier |
| // - 317708: Exception thrown when using LAZY fetch on VIRTUAL mapping |
| // 08/04/2010-2.1.1 Guy Pelletier |
| // - 315782: JPA2 derived identity metadata processing validation doesn't account for autoboxing |
| // 08/20/2010-2.2 Guy Pelletier |
| // - 323252: Canonical model generator throws NPE on virtual 1-1 or M-1 mapping |
| // 08/25/2010-2.2 Guy Pelletier |
| // - 309445: CanonicalModelProcessor process all files (minor correction to patch for bug above) |
| // 09/03/2010-2.2 Guy Pelletier |
| // - 317286: DB column lenght not in sync between @Column and @JoinColumn |
| // 01/04/2011-2.3 Guy Pelletier |
| // - 330628: @PrimaryKeyJoinColumn(...) is not working equivalently to @JoinColumn(..., insertable = false, updatable = false) |
| // 01/06/2011-2.3 Guy Pelletier |
| // - 312244: can't map optional one-to-one relationship using @PrimaryKeyJoinColumn |
| // 03/24/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 1) |
| // 11/19/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| // 12/07/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| // 02/20/2013-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support (foreign key metadata support) |
| // 07/09/2018-2.6 Jody Grassel |
| // - 536853: MapsID processing sets up to fail validation |
| |
| package org.eclipse.persistence.internal.jpa.metadata.accessors.mappings; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_FETCH_EAGER; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_ID; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_MAPS_ID; |
| 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 java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Set; |
| |
| 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.jpa.metadata.MetadataDescriptor; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger; |
| import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.ClassAccessor; |
| 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.AssociationOverrideMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.columns.ForeignKeyMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.columns.JoinColumnMetadata; |
| 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.xml.XMLEntityMappings; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.EmbeddableMapping; |
| import org.eclipse.persistence.mappings.ObjectReferenceMapping; |
| import org.eclipse.persistence.mappings.OneToOneMapping; |
| import org.eclipse.persistence.mappings.RelationTableMechanism; |
| import org.eclipse.persistence.mappings.foundation.AbstractDirectMapping; |
| |
| /** |
| * INTERNAL: |
| * A single object relationship 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 TopLink EJB 3.0 Reference Implementation |
| */ |
| public abstract class ObjectAccessor extends RelationshipAccessor { |
| private Boolean m_id; |
| private Boolean m_optional; |
| private List<PrimaryKeyJoinColumnMetadata> m_primaryKeyJoinColumns = new ArrayList<PrimaryKeyJoinColumnMetadata>(); |
| private PrimaryKeyForeignKeyMetadata m_primaryKeyForeignKey; |
| private String m_mapsId; |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| protected ObjectAccessor(String xmlElement) { |
| super(xmlElement); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected ObjectAccessor(MetadataAnnotation annotation, MetadataAnnotatedElement annotatedElement, ClassAccessor classAccessor) { |
| super(annotation, annotatedElement, classAccessor); |
| |
| if (annotation != null) { |
| m_optional = annotation.getAttributeBooleanDefaultTrue("optional"); |
| } |
| |
| // Set the primary key join columns if some are present. |
| // 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 (primaryKeyJoinColumn.hasForeignKey()) { |
| setPrimaryKeyForeignKey(new PrimaryKeyForeignKeyMetadata(primaryKeyJoinColumn.getForeignKey())); |
| } |
| } |
| |
| // Set the mapped by id if one is present. |
| if (isAnnotationPresent(JPA_MAPS_ID)) { |
| // Call getAttributeString with a default because we rely on the |
| // mapsId not being null and its value of "" which means we need to |
| // default. getAttributeString returns null which kills hasMapsId() logic. |
| m_mapsId = getAnnotation(JPA_MAPS_ID).getAttributeString("value", ""); |
| } |
| |
| // Set the derived id if one is specified. |
| m_id = isAnnotationPresent(JPA_ID); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean equals(Object objectToCompare) { |
| if (super.equals(objectToCompare) && objectToCompare instanceof ObjectAccessor) { |
| ObjectAccessor objectAccessor = (ObjectAccessor) objectToCompare; |
| |
| if (! valuesMatch(m_id, objectAccessor.getId())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_optional, objectAccessor.getOptional())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_primaryKeyJoinColumns, objectAccessor.getPrimaryKeyJoinColumns())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_primaryKeyForeignKey, objectAccessor.getPrimaryKeyForeignKey())) { |
| return false; |
| } |
| |
| return valuesMatch(m_mapsId, objectAccessor.getMapsId()); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + (m_id != null ? m_id.hashCode() : 0); |
| result = 31 * result + (m_optional != null ? m_optional.hashCode() : 0); |
| result = 31 * result + (m_primaryKeyJoinColumns != null ? m_primaryKeyJoinColumns.hashCode() : 0); |
| result = 31 * result + (m_primaryKeyForeignKey != null ? m_primaryKeyForeignKey.hashCode() : 0); |
| result = 31 * result + (m_mapsId != null ? m_mapsId.hashCode() : 0); |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true is this accessor is a derived id accessor. |
| */ |
| @Override |
| public boolean derivesId() { |
| return hasId() || hasMapsId(); |
| } |
| |
| /** |
| * INTERNAL: |
| * The attribute type for virtual object accessors is the target entity. |
| */ |
| @Override |
| public String getAttributeType() { |
| return getTargetEntity().getName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the default fetch type for an object mapping. |
| */ |
| @Override |
| public String getDefaultFetchType() { |
| return JPA_FETCH_EAGER; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public Boolean getId(){ |
| return m_id; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getMapsId(){ |
| return m_mapsId; |
| } |
| |
| /** |
| * 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 Boolean getOptional() { |
| return m_optional; |
| } |
| |
| /** |
| * INTERNAL: |
| * If a target entity is specified in metadata, it will be set as the |
| * reference class, otherwise we will use the raw class. |
| */ |
| @Override |
| public MetadataClass getReferenceClass() { |
| if (m_referenceClass == null) { |
| m_referenceClass = getTargetEntity(); |
| |
| if (m_referenceClass.isVoid()) { |
| // Get the reference class from the accessible object and |
| // log the defaulting contextual reference class. |
| m_referenceClass = super.getReferenceClass(); |
| getLogger().logConfigMessage(getLoggingContext(), getAnnotatedElement(), m_referenceClass); |
| } |
| } |
| |
| return m_referenceClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to process primary keys and DerivedIds. |
| */ |
| protected MetadataClass getSimplePKType(){ |
| MetadataDescriptor referenceDescriptor = getReferenceDescriptor(); |
| ClassAccessor referenceAccessor = referenceDescriptor.getClassAccessor(); |
| |
| if (referenceAccessor.hasDerivedId()) { |
| // Referenced object has a derived ID and must be a simple pk type. |
| // Recurse through to get the simple type. |
| return ((ObjectAccessor) referenceDescriptor.getMappingAccessor(referenceDescriptor.getIdAttributeName())).getSimplePKType(); |
| } else { |
| // Validate on their basic mapping. |
| return referenceDescriptor.getMappingAccessor(referenceDescriptor.getIdAttributeName()).getRawClass(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Object accessors don't require a separate attribute-type specification |
| * in XML, instead they can use the reference class to determine the |
| * attribute-type. |
| */ |
| @Override |
| public boolean hasAttributeType() { |
| return getTargetEntity() != null && ! getTargetEntity().isVoid(); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected boolean hasId() { |
| return m_id != null && m_id; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected boolean hasMapsId() { |
| return m_mapsId != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize a ManyToOneMapping. |
| */ |
| protected ObjectReferenceMapping initManyToOneMapping() { |
| // Allow for different descriptor types (EIS) to create different mapping types. |
| ObjectReferenceMapping mapping = getDescriptor().getClassDescriptor().newManyToOneMapping(); |
| processRelationshipMapping(mapping); |
| |
| mapping.setIsOptional(isOptional()); |
| mapping.setDerivesId(derivesId()); |
| |
| // Process the indirection. |
| processIndirection(mapping); |
| |
| // Process a @ReturnInsert and @ReturnUpdate (to log a warning message) |
| processReturnInsertAndUpdate(); |
| |
| return mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize a OneToOneMapping. |
| */ |
| protected ObjectReferenceMapping initOneToOneMapping() { |
| // Allow for different descriptor types (EIS) to create different mapping types. |
| ObjectReferenceMapping mapping = getDescriptor().getClassDescriptor().newOneToOneMapping(); |
| processRelationshipMapping(mapping); |
| |
| mapping.setIsOptional(isOptional()); |
| mapping.setDerivesId(derivesId()); |
| |
| // Process the indirection. |
| processIndirection(mapping); |
| |
| // Process a @ReturnInsert and @ReturnUpdate (to log a warning message) |
| processReturnInsertAndUpdate(); |
| |
| return mapping; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) { |
| super.initXMLObject(accessibleObject, entityMappings); |
| |
| // Initialize lists of ORMetadata objects. |
| initXMLObjects(m_primaryKeyJoinColumns, accessibleObject); |
| |
| // Initialize single objects. |
| initXMLObject(m_primaryKeyForeignKey, accessibleObject); |
| } |
| |
| |
| /** |
| * INTERNAL: |
| * Return true if this accessor represents a 1-1 primary key relationship. |
| */ |
| public boolean isOneToOnePrimaryKeyRelationship() { |
| return ! m_primaryKeyJoinColumns.isEmpty(); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean isOptional() { |
| return m_optional != null && m_optional; |
| } |
| |
| /** |
| * INTERNAL: |
| * Process an association override for either an embedded object mapping, |
| * or a map mapping (element-collection, 1-M and M-M) containing an |
| * embeddable object as the value or key. |
| */ |
| @Override |
| protected void processAssociationOverride(AssociationOverrideMetadata associationOverride, EmbeddableMapping embeddableMapping, MetadataDescriptor owningDescriptor) { |
| if (getMapping().isOneToOneMapping()) { |
| processAssociationOverride(associationOverride, embeddableMapping, owningDescriptor.getPrimaryTable(), owningDescriptor); |
| } else { |
| super.processAssociationOverride(associationOverride, embeddableMapping, owningDescriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process an association override for either an embedded object mapping, |
| * or a map mapping (element-collection, 1-M and M-M) containing an |
| * embeddable object as the value or key. |
| */ |
| protected void processAssociationOverride(AssociationOverrideMetadata associationOverride, EmbeddableMapping embeddableMapping, DatabaseTable defaultTable, MetadataDescriptor owningDescriptor) { |
| // Process and use the association override's joinColumns. Avoid calling |
| // getJoinColumns since, by default, that method looks for an association |
| // override on the descriptor. In this case that has already been taken |
| // care of for use before calling this method. |
| for (JoinColumnMetadata joinColumn : getJoinColumnsAndValidate(associationOverride.getJoinColumns(), getReferenceDescriptor())) { |
| // Look up the primary key field from the referenced column name. |
| DatabaseField pkField = getReferencedField(joinColumn.getReferencedColumnName(), getReferenceDescriptor(), MetadataLogger.PK_COLUMN); |
| |
| DatabaseField fkField = ((OneToOneMapping) getMapping()).getTargetToSourceKeyFields().get(pkField); |
| |
| if (fkField == null) { |
| throw ValidationException.invalidAssociationOverrideReferenceColumnName(pkField.getName(), associationOverride.getName(), embeddableMapping.getAttributeName(), owningDescriptor.getJavaClassName()); |
| } else { |
| // Make sure we have a table set on the association override |
| // field, otherwise use the default table provided. |
| DatabaseField translationFKField = joinColumn.getForeignKeyField(pkField); |
| if (! translationFKField.hasTableName()) { |
| translationFKField.setTable(defaultTable); |
| } |
| |
| embeddableMapping.addFieldTranslation(translationFKField, fkField.getName()); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to process primary keys and DerivedIds. |
| */ |
| protected void processId(OneToOneMapping mapping) { |
| // If this entity has a pk class, we need to validate our ids. |
| MetadataDescriptor referenceDescriptor = getReferenceDescriptor(); |
| String referencePKClassName = referenceDescriptor.getPKClassName(); |
| |
| if (referencePKClassName != null) { |
| // They have a pk class |
| String pkClassName = getDescriptor().getPKClassName(); |
| if (pkClassName == null){ |
| throw ValidationException.invalidCompositePKSpecification(getJavaClass(), null); |
| } |
| |
| if (pkClassName.equals(referencePKClassName)){ |
| // This pk is the reference pk, so all pk attributes are |
| // accounted through this relationship. |
| getOwningDescriptor().getPKClassIDs().clear(); |
| } else { |
| // Validate our pk contains their pk. |
| getOwningDescriptor().validateDerivedPKClassId(getAttributeName(), referencePKClassName, getReferenceClassName()); |
| } |
| } else { |
| MetadataClass type = null; |
| if (referenceDescriptor.getClassAccessor().hasDerivedId()){ |
| // Referenced object has a derived ID but no PK class defined, |
| // so it must be a simple pk type. Recurse through to get the |
| // simple type |
| type = ((ObjectAccessor) referenceDescriptor.getMappingAccessor(referenceDescriptor.getIdAttributeName())).getSimplePKType(); |
| } else { |
| // Validate on their basic mapping. |
| type = referenceDescriptor.getMappingAccessor(referenceDescriptor.getIdAttributeName()).getRawClass(); |
| } |
| |
| getOwningDescriptor().validateDerivedPKClassId(getAttributeName(), getBoxedType(type.getName()), getReferenceClassName()); |
| } |
| |
| // Store the Id attribute name. Used with validation and OrderBy. |
| getOwningDescriptor().addIdAttributeName(getAttributeName()); |
| |
| // Add the primary key fields to the descriptor. |
| for (DatabaseField pkField : mapping.getForeignKeyFields()) { |
| getOwningDescriptor().addPrimaryKeyField(pkField); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the mapping keys from the maps id value. |
| */ |
| protected void processMapsId(OneToOneMapping oneToOneMapping) { |
| EmbeddedIdAccessor embeddedIdAccessor = getDescriptor().getEmbeddedIdAccessor(); |
| |
| if (embeddedIdAccessor == null) { |
| // Case #4: a simple id association |
| MappingAccessor idAccessor = getDescriptor().getMappingAccessor(getDescriptor().getIdAttributeName()); |
| DatabaseMapping idMapping = idAccessor.getMapping(); |
| |
| // Grab the foreign key field and set it as the descriptor's id field. |
| DatabaseField foreignKeyField = oneToOneMapping.getForeignKeyFields().elementAt(0); |
| updatePrimaryKeyField(idAccessor, foreignKeyField); |
| |
| // Update the field on the mapping. |
| ((AbstractDirectMapping) idMapping).setField(foreignKeyField); |
| |
| // Set the primary key mapping as read only. |
| idMapping.setIsReadOnly(true); |
| |
| // Set the maps id mapping. |
| oneToOneMapping.setDerivedIdMapping(idMapping); |
| } else { |
| if (embeddedIdAccessor.getReferenceClassName().equals(getReferenceDescriptor().getPKClassName())) { |
| // Case #5: Parent's id class is the same as dependent's embedded id class |
| // Case #6: Both parent and dependent use same embedded id class. |
| processMapsIdFields(oneToOneMapping, embeddedIdAccessor, embeddedIdAccessor); |
| } else { |
| if (m_mapsId.equals("")) { |
| // User didn't specify a mapsId value. By default the attribute name from this object accessor is used. |
| m_mapsId = getAttributeName(); |
| } |
| |
| // Set the maps id value on the mapping. |
| oneToOneMapping.setMapsIdValue(m_mapsId); |
| MappingAccessor mappingAccessor = embeddedIdAccessor.getReferenceDescriptor().getMappingAccessor(m_mapsId); |
| |
| if (mappingAccessor == null) { |
| throw ValidationException.invalidMappedByIdValue(m_mapsId, getAnnotatedElementName(), embeddedIdAccessor.getReferenceClass()); |
| } else { |
| // Case #1: Dependent's embedded id maps a basic mapping to parent entity. |
| // Case #2: Dependent's embedded id maps the parent's id class |
| // Case #3: Dependent's embedded if maps the parent's embedded id class |
| processMapsIdFields(oneToOneMapping, embeddedIdAccessor, mappingAccessor); |
| } |
| } |
| |
| // Set the maps id mapping. |
| oneToOneMapping.setDerivedIdMapping(embeddedIdAccessor.getMapping()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * We're going to add field name translations where necessary. If the user |
| * specified (erroneously that is) attribute overrides this will override |
| * them. |
| */ |
| protected void processMapsIdFields(OneToOneMapping oneToOneMapping, EmbeddedIdAccessor embeddedIdAccessor, MappingAccessor mapsIdAccessor) { |
| // At this point we have a one to one mapping to the reference class |
| // with specified join columns or defaulted ones. The foreign key |
| // fields are the fields we want to use to map our id fields. |
| for (DatabaseField fkField : oneToOneMapping.getForeignKeyFields()) { |
| if (mapsIdAccessor.isBasic()) { |
| // Case #1: Dependent's embedded id maps a basic mapping to parent entity. |
| |
| // Add the maps id accessor to the embedded id accessor's list |
| // of mappings that need to be set to read only at initialize |
| // time on the cloned aggregate descriptor. |
| embeddedIdAccessor.addMapsIdAccessor(mapsIdAccessor); |
| |
| // Add a field name translation to the mapping. |
| embeddedIdAccessor.updateDerivedIdField((EmbeddableMapping) embeddedIdAccessor.getMapping(), mapsIdAccessor.getAttributeName(), fkField, mapsIdAccessor); |
| } else { |
| if (mapsIdAccessor.isDerivedIdClass()) { |
| // Case #2: Dependent's embedded id maps the parent's id class |
| // Case #3: Dependent's embedded if maps the parent's embedded id class |
| |
| // Add the maps id accessor to the embedded id accessor's |
| // list of mappings that need to be set to read only at |
| // initialize time on the cloned aggregate descriptor. |
| embeddedIdAccessor.addMapsIdAccessor(mapsIdAccessor); |
| } else { |
| // Case #5: Parent's id class is the same as dependent's embedded id class |
| // Case #6: Both parent and dependent use same embedded id class. |
| |
| // Set the mapping to read only. |
| embeddedIdAccessor.getMapping().setIsReadOnly(true); |
| } |
| |
| // For this foreign key relation, get the primary key accessor |
| // from the reference descriptor. |
| DatabaseField referencePKField = oneToOneMapping.getSourceToTargetKeyFields().get(fkField); |
| MappingAccessor referencePKAccessor = getReferenceDescriptor().getPrimaryKeyAccessorForField(referencePKField); |
| |
| // If there is no primary key accessor then the user must have |
| // specified an incorrect reference column name. Throw an exception. |
| if (referencePKAccessor == null) { |
| throw ValidationException.invalidDerivedIdPrimaryKeyField(getReferenceClassName(), referencePKField.getQualifiedName(), getAttributeName(), getJavaClassName()); |
| } else { |
| // The reference primary key accessor will tell us which attribute |
| // accessor we need to map a field name translation for. |
| final Set<MappingAccessor> mappingAccessors = findAllFieldAccessors(mapsIdAccessor.getReferenceDescriptor()); |
| final List<MappingAccessor> accessorsWithTargetAttributeName = new ArrayList<MappingAccessor>(); |
| for (MappingAccessor ma : mappingAccessors) { |
| if (referencePKAccessor.getAttributeName().equals(ma.getAttributeName())) { |
| accessorsWithTargetAttributeName.add(ma); |
| } |
| } |
| MappingAccessor idAccessor = null; |
| if (accessorsWithTargetAttributeName.size() > 1 && accessorsWithTargetAttributeName.contains(referencePKAccessor)) { |
| // The MappingAccessor that is equal() to the referencePKAccessor may still be a different object. |
| idAccessor = accessorsWithTargetAttributeName.get(accessorsWithTargetAttributeName.indexOf(referencePKAccessor)); |
| } else { |
| idAccessor = mapsIdAccessor.getReferenceDescriptor().getMappingAccessor(referencePKAccessor.getAttributeName()); |
| } |
| |
| // Add a field name translation to the mapping. |
| ((EmbeddedAccessor) mapsIdAccessor).updateDerivedIdField((EmbeddableMapping) mapsIdAccessor.getMapping(), idAccessor.getAttributeName(), fkField, idAccessor); |
| } |
| } |
| } |
| } |
| |
| private Set<MappingAccessor> findAllFieldAccessors(MetadataDescriptor md) { |
| final HashSet<MappingAccessor> retSet = new HashSet<MappingAccessor>(); |
| final Collection<MappingAccessor> mappingAccessors = md.getMappingAccessors(); |
| for (MappingAccessor ma : mappingAccessors) { |
| if (ma instanceof EmbeddedAccessor) { |
| retSet.addAll(findAllFieldAccessors(ma.getReferenceDescriptor())); |
| } else { |
| retSet.add(ma); |
| } |
| } |
| |
| return retSet; |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the join columns for the owning side of a one to one mapping. |
| * The default pk and pk field names are used only with single primary key |
| * entities. The processor should never get as far as to use them with |
| * entities that have a composite primary key (validation exception will be |
| * thrown). |
| */ |
| protected void processForeignKeyRelationship(ObjectReferenceMapping mapping) { |
| // If the fk field (name) is not specified, it defaults to the |
| // concatenation of the following: the name of the referencing |
| // relationship property or field of the referencing entity or |
| // embeddable class; "_"; the name of the referenced primary key |
| // column. |
| String defaultFKFieldName = getDefaultAttributeName() + "_" + getReferenceDescriptor().getPrimaryKeyFieldName(); |
| |
| // Get the join columns (directly or through an association override), |
| // init them and validate. |
| List<JoinColumnMetadata> joinColumns = getJoinColumns(getJoinColumns(), getReferenceDescriptor()); |
| |
| // Get the foreign key (directly or through an association override) and |
| // make sure it is initialized for processing. |
| ForeignKeyMetadata foreignKey = getForeignKey(getForeignKey(), getReferenceDescriptor()); |
| |
| // Now process the foreign key relationship metadata. |
| processForeignKeyRelationship(mapping, joinColumns, foreignKey, getReferenceDescriptor(), defaultFKFieldName, getDescriptor().getPrimaryTable()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the primary key join columns for the owning side of a one to one |
| * mapping. The default pk and pk field names are used only with single |
| * primary key entities. The processor should never get as far as to use |
| * them with entities that have a composite primary key (validation |
| * exception will be thrown). |
| */ |
| protected void processOneToOnePrimaryKeyRelationship(OneToOneMapping mapping) { |
| List<PrimaryKeyJoinColumnMetadata> pkJoinColumns = processPrimaryKeyJoinColumns(getPrimaryKeyJoinColumns()); |
| |
| // Add the source foreign key fields to the mapping. |
| for (PrimaryKeyJoinColumnMetadata primaryKeyJoinColumn : pkJoinColumns) { |
| // Look up the primary key field from the referenced column name. |
| DatabaseField pkField = getReferencedField(primaryKeyJoinColumn.getReferencedColumnName(), getReferenceDescriptor(), MetadataLogger.PK_COLUMN); |
| |
| // Look up the foreign key field from the name. |
| DatabaseField fkField = getReferencedField(primaryKeyJoinColumn.getName(), getDescriptor(), MetadataLogger.FK_COLUMN); |
| |
| // Add a source foreign key to the mapping. |
| mapping.addForeignKeyField(fkField, pkField); |
| } |
| |
| // Mark the mapping read only |
| mapping.setIsReadOnly(true); |
| |
| // Set the primary key join column flag. This will also set the mapping |
| // to read only. |
| mapping.setIsOneToOnePrimaryKeyRelationship(true); |
| |
| // Process the primary key foreign key metadata if specified for this |
| // accessor. |
| if (m_primaryKeyForeignKey != null) { |
| m_primaryKeyForeignKey.process(getDescriptor().getPrimaryKeyTable()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process the the correct metadata for the owning side of a one to one |
| * mapping. Note, the order of checking is important, that is, check for |
| * a mapsId first. |
| */ |
| protected void processOwningMappingKeys(OneToOneMapping mapping) { |
| if (derivesId()) { |
| // We need to process the join columns as we normally would. |
| // Then we must update the fields from our derived id accessor. |
| processForeignKeyRelationship(mapping); |
| |
| if (hasMapsId()) { |
| processMapsId(mapping); |
| } else { |
| processId(mapping); |
| } |
| } else if (isOneToOnePrimaryKeyRelationship()) { |
| processOneToOnePrimaryKeyRelationship(mapping); |
| } else if (hasJoinTable()) { |
| mapping.setRelationTableMechanism(new RelationTableMechanism()); |
| processJoinTable(mapping, mapping.getRelationTableMechanism(), getJoinTableMetadata()); |
| } else { |
| processForeignKeyRelationship(mapping); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setId(Boolean id){ |
| m_id = id; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setMapsId(String mapsId){ |
| m_mapsId = mapsId; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setOptional(Boolean isOptional) { |
| m_optional = isOptional; |
| } |
| |
| /** |
| * 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; |
| } |
| } |