| /* |
| * 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 |
| // 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 |
| // 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 |
| // 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/06/2009-2.0 Guy Pelletier |
| // - 286317: UniqueConstraint xml element is changing (plus couple other fixes, see bug) |
| // 11/25/2009-2.0 Guy Pelletier |
| // - 288955: EclipseLink 2.0.0.v20090821-r4934 (M7) throws EclipseLink-80/41 exceptions if InheritanceType.TABLE_PER_CLASS is used |
| // 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/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 |
| // 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 |
| // 03/24/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 1) |
| // 07/03/2011-2.3.1 Guy Pelletier |
| // - 348756: m_cascadeOnDelete boolean should be changed to Boolean |
| // 05/17/2012-2.3.3 Guy Pelletier |
| // - 379829: NPE Thrown with OneToOne Relationship |
| // 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 |
| package org.eclipse.persistence.internal.jpa.metadata.accessors.mappings; |
| |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_COLUMN; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_FETCH_LAZY; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_JOIN_COLUMN; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_JOIN_COLUMNS; |
| import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_JOIN_TABLE; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Vector; |
| |
| import org.eclipse.persistence.annotations.BatchFetch; |
| import org.eclipse.persistence.annotations.CascadeOnDelete; |
| import org.eclipse.persistence.annotations.Convert; |
| import org.eclipse.persistence.annotations.JoinFetch; |
| import org.eclipse.persistence.annotations.Noncacheable; |
| import org.eclipse.persistence.annotations.PrivateOwned; |
| import org.eclipse.persistence.exceptions.ValidationException; |
| import org.eclipse.persistence.indirection.ValueHolderInterface; |
| 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.MetadataProcessor; |
| import org.eclipse.persistence.internal.jpa.metadata.MetadataProject; |
| 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.ForeignKeyMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.columns.JoinColumnMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.columns.JoinFieldMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.mappings.BatchFetchMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.mappings.CascadeMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.tables.JoinTableMetadata; |
| import org.eclipse.persistence.internal.jpa.metadata.xml.XMLEntityMappings; |
| import org.eclipse.persistence.mappings.DatabaseMapping; |
| import org.eclipse.persistence.mappings.ForeignReferenceMapping; |
| import org.eclipse.persistence.mappings.RelationTableMechanism; |
| |
| /** |
| * INTERNAL: |
| * A relational 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 RelationshipAccessor extends MappingAccessor { |
| private Boolean m_orphanRemoval; |
| private Boolean m_cascadeOnDelete; |
| private Boolean m_nonCacheable; |
| private Boolean m_privateOwned; |
| |
| private BatchFetchMetadata m_batchFetch; |
| private CascadeMetadata m_cascade; |
| private ForeignKeyMetadata m_foreignKey; |
| private JoinTableMetadata m_joinTable; |
| protected MetadataClass m_referenceClass; |
| private MetadataClass m_targetEntity; |
| |
| private List<JoinColumnMetadata> m_joinColumns = new ArrayList<JoinColumnMetadata>(); |
| private List<JoinFieldMetadata> m_joinFields = new ArrayList<JoinFieldMetadata>(); |
| |
| private String m_fetch; |
| private String m_mappedBy; |
| private String m_joinFetch; |
| private String m_targetEntityName; |
| |
| /** |
| * INTERNAL: |
| */ |
| protected RelationshipAccessor(String xmlElement) { |
| super(xmlElement); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected RelationshipAccessor(MetadataAnnotation annotation, MetadataAccessibleObject accessibleObject, ClassAccessor classAccessor) { |
| super(annotation, accessibleObject, classAccessor); |
| |
| m_fetch = (annotation == null) ? getDefaultFetchType() : annotation.getAttributeString("fetch"); |
| m_targetEntity = getMetadataClass((annotation == null) ? "void" : annotation.getAttributeString("targetEntity")); |
| m_cascade = (annotation == null) ? null : new CascadeMetadata(annotation.getAttributeArray("cascade"), this); |
| |
| // Set the join fetch if one is present. |
| if (isAnnotationPresent(JoinFetch.class)) { |
| m_joinFetch = getAnnotation(JoinFetch.class).getAttributeString("value"); |
| } |
| |
| // Set the batch fetch if one is present. |
| if (isAnnotationPresent(BatchFetch.class)) { |
| m_batchFetch = new BatchFetchMetadata(getAnnotation(BatchFetch.class), this); |
| } |
| |
| // Set the join columns if some are present. |
| // Process all the join columns first. |
| if (isAnnotationPresent(JPA_JOIN_COLUMNS)) { |
| MetadataAnnotation joinColumns = getAnnotation(JPA_JOIN_COLUMNS); |
| for (Object joinColumn : joinColumns.getAttributeArray("value")) { |
| m_joinColumns.add(new JoinColumnMetadata((MetadataAnnotation) joinColumn, this)); |
| } |
| |
| // Set the foreign key metadata if one is specified. |
| if (joinColumns.hasAttribute("foreignKey")) { |
| setForeignKey(new ForeignKeyMetadata(joinColumns.getAttributeAnnotation("foreignKey"), this)); |
| } |
| } |
| |
| // Process the single key join column second. |
| if (isAnnotationPresent(JPA_JOIN_COLUMN)) { |
| JoinColumnMetadata joinColumn = new JoinColumnMetadata(getAnnotation(JPA_JOIN_COLUMN), this); |
| m_joinColumns.add(joinColumn); |
| |
| // Set the foreign key metadata. |
| setForeignKey(joinColumn.getForeignKey()); |
| } |
| |
| // Set the join fields if some are present. |
| if (isAnnotationPresent("org.eclipse.persistence.nosql.annotations.JoinFields")) { |
| for (Object joinColumn : getAnnotation("org.eclipse.persistence.nosql.annotations.JoinFields").getAttributeArray("value")) { |
| m_joinColumns.add(new JoinColumnMetadata((MetadataAnnotation) joinColumn, this)); |
| } |
| } |
| |
| // Process EIS/NoSQL join field. |
| if (isAnnotationPresent("org.eclipse.persistence.nosql.annotations.JoinField")) { |
| m_joinColumns.add(new JoinColumnMetadata(getAnnotation("org.eclipse.persistence.nosql.annotations.JoinField"), this)); |
| } |
| |
| // Set the join table if one is present. |
| if (isAnnotationPresent(JPA_JOIN_TABLE)) { |
| m_joinTable = new JoinTableMetadata(getAnnotation(JPA_JOIN_TABLE), this); |
| } |
| |
| // Set the private owned if one is present. |
| m_privateOwned = isAnnotationPresent(PrivateOwned.class); |
| |
| // Set the cascade on delete if one is present. |
| m_cascadeOnDelete = isAnnotationPresent(CascadeOnDelete.class); |
| |
| // Set the non cacheable if one is present. |
| m_nonCacheable = isAnnotationPresent(Noncacheable.class); |
| } |
| |
| /** |
| * INTERNAL: |
| * |
| * Add the relation key fields to a many to many mapping. |
| */ |
| protected void addJoinTableRelationKeyFields(List<JoinColumnMetadata> joinColumns, RelationTableMechanism mechanism, String defaultFieldName, MetadataDescriptor descriptor, boolean isSource) { |
| // Set the right context level. |
| String PK_CTX, FK_CTX; |
| if (isSource) { |
| PK_CTX = MetadataLogger.SOURCE_PK_COLUMN; |
| FK_CTX = MetadataLogger.SOURCE_FK_COLUMN; |
| } else { |
| PK_CTX = MetadataLogger.TARGET_PK_COLUMN; |
| FK_CTX = MetadataLogger.TARGET_FK_COLUMN; |
| } |
| |
| for (JoinColumnMetadata joinColumn : joinColumns) { |
| // Look up the primary key field from the referenced column name. |
| DatabaseField pkField = getReferencedField(joinColumn.getReferencedColumnName(), descriptor, PK_CTX); |
| |
| // If the fk field (name) is not specified, it defaults to the |
| // name of the referencing relationship property or field of the |
| // referencing entity + "_" + the name of the referenced primary |
| // key column. If there is no such referencing relationship |
| // property or field in the entity (i.e., a join table is used), |
| // the join column name is formed as the concatenation of the |
| // following: the name of the entity + "_" + the name of the |
| // referenced primary key column. |
| DatabaseField fkField = joinColumn.getForeignKeyField(pkField); |
| String defaultFKFieldName = defaultFieldName + "_" + descriptor.getPrimaryKeyFieldName(); |
| setFieldName(fkField, defaultFKFieldName, FK_CTX); |
| |
| // Target table name here is the join table name. |
| // If the user had specified a different table name in the join |
| // column, it is ignored. Perhaps an error or warning should be |
| // fired off. |
| fkField.setTable(mechanism.getRelationTable()); |
| |
| // Add a target relation key to the mapping. |
| if (isSource) { |
| mechanism.addSourceRelationKeyField(fkField, pkField); |
| } else { |
| mechanism.addTargetRelationKeyField(fkField, pkField); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean equals(Object objectToCompare) { |
| if (super.equals(objectToCompare) && objectToCompare instanceof RelationshipAccessor) { |
| RelationshipAccessor relationshipAccessor = (RelationshipAccessor) objectToCompare; |
| |
| if (! valuesMatch(m_orphanRemoval, relationshipAccessor.getOrphanRemoval())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_privateOwned, relationshipAccessor.getPrivateOwned())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_nonCacheable, relationshipAccessor.getNonCacheable())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_cascade, relationshipAccessor.getCascade())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_mappedBy, relationshipAccessor.getMappedBy())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_fetch, relationshipAccessor.getFetch())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_joinFetch, relationshipAccessor.getJoinFetch())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_batchFetch, relationshipAccessor.getBatchFetch())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_joinTable, relationshipAccessor.getJoinTable())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_joinColumns, relationshipAccessor.getJoinColumns())) { |
| return false; |
| } |
| |
| if (! valuesMatch(m_foreignKey, relationshipAccessor.getForeignKey())) { |
| return false; |
| } |
| |
| return valuesMatch(m_targetEntityName, relationshipAccessor.getTargetEntityName()); |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public int hashCode() { |
| int result = super.hashCode(); |
| result = 31 * result + (m_orphanRemoval != null ? m_orphanRemoval.hashCode() : 0); |
| result = 31 * result + (m_nonCacheable != null ? m_nonCacheable.hashCode() : 0); |
| result = 31 * result + (m_privateOwned != null ? m_privateOwned.hashCode() : 0); |
| result = 31 * result + (m_batchFetch != null ? m_batchFetch.hashCode() : 0); |
| result = 31 * result + (m_cascade != null ? m_cascade.hashCode() : 0); |
| result = 31 * result + (m_foreignKey != null ? m_foreignKey.hashCode() : 0); |
| result = 31 * result + (m_joinTable != null ? m_joinTable.hashCode() : 0); |
| result = 31 * result + (m_joinColumns != null ? m_joinColumns.hashCode() : 0); |
| result = 31 * result + (m_fetch != null ? m_fetch.hashCode() : 0); |
| result = 31 * result + (m_mappedBy != null ? m_mappedBy.hashCode() : 0); |
| result = 31 * result + (m_joinFetch != null ? m_joinFetch.hashCode() : 0); |
| result = 31 * result + (m_targetEntityName != null ? m_targetEntityName.hashCode() : 0); |
| return result; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public BatchFetchMetadata getBatchFetch() { |
| return m_batchFetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public Boolean getCascadeOnDelete() { |
| return m_cascadeOnDelete; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public CascadeMetadata getCascade() { |
| return m_cascade; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public abstract String getDefaultFetchType(); |
| |
| /** |
| * INTERNAL: |
| * Return the default table to hold the foreign key of a MapKey when |
| * and Entity is used as the MapKey |
| */ |
| @Override |
| protected DatabaseTable getDefaultTableForEntityMapKey(){ |
| if (getJoinTable() != null) { |
| return getJoinTable().getDatabaseTable(); |
| } else { |
| return super.getDefaultTableForEntityMapKey(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getFetch() { |
| return m_fetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public ForeignKeyMetadata getForeignKey() { |
| return m_foreignKey; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<JoinColumnMetadata> getJoinColumns() { |
| return m_joinColumns; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public List<JoinFieldMetadata> getJoinFields() { |
| return m_joinFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getJoinFetch() { |
| return m_joinFetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public JoinTableMetadata getJoinTable() { |
| return m_joinTable; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method will return the join table metadata to be processed with |
| * this relationship accessor. It will first check for a join table from |
| * an association override, followed by a join table defined directly on |
| * the accessor. If neither is present, a join table metadata is defaulted. |
| */ |
| protected JoinTableMetadata getJoinTableMetadata() { |
| if (getDescriptor().hasAssociationOverrideFor(getAttributeName())) { |
| return getDescriptor().getAssociationOverrideFor(getAttributeName()).getJoinTable(); |
| } else { |
| if (m_joinTable == null) { |
| m_joinTable = new JoinTableMetadata(getClassAccessor()); |
| } |
| |
| return m_joinTable; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the logging context for this accessor. |
| */ |
| protected abstract String getLoggingContext(); |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getMappedBy() { |
| return m_mappedBy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public Boolean getNonCacheable() { |
| return m_nonCacheable; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public Boolean getOrphanRemoval() { |
| return m_orphanRemoval; |
| } |
| |
| /** |
| * INTERNAL: |
| * Method to return an owner mapping. It will tell the owner class to |
| * process itself if it hasn't already done so. Assumes that a mapped by |
| * value has been specified and that a check against mappedBy has been |
| * done. |
| */ |
| protected DatabaseMapping getOwningMapping() { |
| MetadataDescriptor ownerDescriptor = getReferenceDescriptor(); |
| MappingAccessor mappingAccessor = ownerDescriptor.getMappingAccessor(getMappedBy()); |
| |
| // If no mapping was found, there is an error in the mappedBy field, |
| // therefore, throw an exception. |
| if (mappingAccessor == null) { |
| throw ValidationException.noMappedByAttributeFound(ownerDescriptor.getJavaClass(), getMappedBy(), getJavaClass(), getAttributeName()); |
| } else if (mappingAccessor.isRelationship()) { |
| RelationshipAccessor relationshipAccessor = (RelationshipAccessor) mappingAccessor; |
| |
| // Check that we don't have circular mappedBy values which will |
| // cause an infinite loop. |
| String mappedBy = relationshipAccessor.getMappedBy(); |
| |
| if (mappedBy != null && mappedBy.equals(getAttributeName())) { |
| throw ValidationException.circularMappedByReferences(getJavaClass(), getAttributeName(), getJavaClass(), getMappedBy()); |
| } |
| |
| // Make sure the mapping accessor is processed. |
| if (! mappingAccessor.isProcessed()) { |
| mappingAccessor.process(); |
| } |
| |
| if (this.getMapping() != null |
| && this.getMapping().isForeignReferenceMapping()) { |
| ((ForeignReferenceMapping) this.getMapping()) |
| .setMappedBy(mappedBy); |
| } |
| } |
| |
| return mappingAccessor.getMapping(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public Boolean getPrivateOwned() { |
| return m_privateOwned; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the reference metadata descriptor for this accessor. |
| * This method does additional checks to make sure that the target |
| * entity is indeed an entity class. |
| */ |
| @Override |
| public MetadataDescriptor getReferenceDescriptor() { |
| MetadataDescriptor referenceDescriptor; |
| |
| // When processing metamodel mapped superclasses, we don't have the |
| // luxury of a full type context as we would during regular metadata |
| // processing (e.g. to figure out generic types). The metamodel mapped |
| // superclass descriptors must therefore rely on a real child descriptor |
| // to extract this information and process correctly. When a type is |
| // unknown, the reference class name currently defaults to java.lang.string. |
| // @see MetadataAnnotatedElement getRawClass(MetadataDescriptor) |
| if (getDescriptor().isMappedSuperclass() && getReferenceClassName().equals(MetadataAnnotatedElement.DEFAULT_RAW_CLASS) || getReferenceClass().isVoid()) { |
| MappingAccessor childMappingAccessor = getDescriptor().getMetamodelMappedSuperclassChildDescriptor().getMappingAccessor(getAttributeName()); |
| referenceDescriptor = childMappingAccessor.getReferenceDescriptor(); |
| |
| if (referenceDescriptor.isInheritanceSubclass()) { |
| referenceDescriptor = referenceDescriptor.getInheritanceRootDescriptor(); |
| } |
| } else { |
| ClassAccessor accessor = getProject().getAccessor(getReferenceClassName()); |
| referenceDescriptor = (accessor != null) ? accessor.getDescriptor() : null; |
| if (referenceDescriptor == null) { |
| MetadataProcessor compositeProcessor = getProject().getCompositeProcessor(); |
| if (compositeProcessor != null) { |
| for (MetadataProject pearProject : compositeProcessor.getPearProjects(getProject())) { |
| accessor = pearProject.getAccessor(getReferenceClassName()); |
| if (accessor != null) { |
| referenceDescriptor = accessor.getDescriptor(); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| // Validate the reference descriptor is valid. |
| if (referenceDescriptor == null || referenceDescriptor.isEmbeddable() || referenceDescriptor.isEmbeddableCollection()) { |
| throw ValidationException.nonEntityTargetInRelationship(getJavaClass(), getReferenceClass(), getAnnotatedElement()); |
| } |
| |
| return referenceDescriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the target entity for this accessor. |
| */ |
| public MetadataClass getTargetEntity() { |
| return m_targetEntity; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public String getTargetEntityName() { |
| return m_targetEntityName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if a join table exists for this accessor (either directly |
| * set or through an association override). |
| */ |
| protected boolean hasJoinTable() { |
| return m_joinTable != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if this accessor is the non owning side of the relationship, |
| * that is, has a mapped by value. |
| */ |
| public boolean hasMappedBy() { |
| return getMappedBy() != null && ! getMappedBy().equals(""); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public void initXMLObject(MetadataAccessibleObject accessibleObject, XMLEntityMappings entityMappings) { |
| super.initXMLObject(accessibleObject, entityMappings); |
| |
| if (m_joinFields != null) { |
| m_joinColumns.addAll(m_joinFields); |
| } |
| |
| // Initialize lists of objects. |
| initXMLObjects(m_joinColumns, accessibleObject); |
| |
| // Initialize single objects. |
| initXMLObject(m_joinTable, accessibleObject); |
| initXMLObject(m_cascade, accessibleObject); |
| initXMLObject(m_foreignKey, accessibleObject); |
| |
| // Initialize the target entity name we read from XML. |
| m_targetEntity = initXMLClassName(m_targetEntityName); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public boolean isCascadeOnDelete() { |
| return m_cascadeOnDelete != null && m_cascadeOnDelete; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if the accessor should be lazy fetched. |
| */ |
| public boolean isLazy() { |
| String fetchType = getFetch(); |
| |
| if (fetchType == null) { |
| fetchType = getDefaultFetchType(); |
| } |
| |
| return fetchType.equals(JPA_FETCH_LAZY); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public boolean isNonCacheable() { |
| return m_nonCacheable != null && m_nonCacheable; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true is this relationship employs orphanRemoval. |
| */ |
| protected boolean isOrphanRemoval() { |
| return m_orphanRemoval != null && m_orphanRemoval; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public boolean isPrivateOwned() { |
| return m_privateOwned != null && m_privateOwned; |
| } |
| |
| /** |
| * INTERNAL: |
| * If somehow we are processing a class that was weaved to have value |
| * holders, we should ignore the processing of this mapping. |
| */ |
| public boolean isValueHolderInterface() { |
| return getTargetEntity().getName().equals(ValueHolderInterface.class.getName()) || (getTargetEntity().getName().equals(void.class.getName()) && getReferenceClass().getName().equals(ValueHolderInterface.class.getName())); |
| } |
| |
| /** |
| * INTERNAL: |
| * Common validation done by all relationship accessors. |
| */ |
| @Override |
| public void process() { |
| // The processing of this accessor may have been fast tracked through a |
| // non-owning relationship. If so, no processing is required. |
| if (! isProcessed()) { |
| // If a Column annotation is specified then throw an exception. |
| if (isAnnotationPresent(JPA_COLUMN)) { |
| throw ValidationException.invalidColumnAnnotationOnRelationship(getJavaClass(), getAttributeName()); |
| } |
| |
| // If a Convert annotation is specified then throw an exception. |
| if (isAnnotationPresent(Convert.class)) { |
| throw ValidationException.invalidMappingForConverter(getJavaClass(), getAttributeName()); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the batch fetch type on the foreign reference mapping. |
| */ |
| protected void processBatchFetch(ForeignReferenceMapping mapping) { |
| if (m_batchFetch != null) { |
| m_batchFetch.process(mapping); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void processCascadeTypes(ForeignReferenceMapping mapping) { |
| if (m_cascade != null) { |
| m_cascade.process(mapping); |
| } |
| |
| // Apply the persistence unit default cascade-persist if necessary. |
| if (getDescriptor().isCascadePersist() && ! mapping.isCascadePersist()) { |
| mapping.setCascadePersist(true); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process a MetadataJoinTable. |
| */ |
| protected void processJoinTable(ForeignReferenceMapping mapping, RelationTableMechanism mechanism, JoinTableMetadata joinTable) { |
| // Build the default table name |
| String defaultName = getOwningDescriptor().getPrimaryTableName() + "_" + getReferenceDescriptor().getPrimaryTableName(); |
| |
| // Process any table defaults and log warning messages. |
| processTable(joinTable, defaultName); |
| |
| // Set the table on the mapping. |
| mechanism.setRelationTable(joinTable.getDatabaseTable()); |
| |
| // Add all the joinColumns (source foreign keys) to the mapping. |
| String defaultSourceFieldName; |
| if (getReferenceDescriptor().hasBiDirectionalManyToManyAccessorFor(getJavaClassName(), getAttributeName())) { |
| defaultSourceFieldName = getReferenceDescriptor().getBiDirectionalManyToManyAccessor(getJavaClassName(), getAttributeName()).getAttributeName(); |
| } else { |
| defaultSourceFieldName = getOwningDescriptor().getAlias(); |
| } |
| addJoinTableRelationKeyFields(getJoinColumnsAndValidate(joinTable.getJoinColumns(), getOwningDescriptor()), mechanism, defaultSourceFieldName, getOwningDescriptor(), true); |
| |
| // Add all the inverseJoinColumns (target foreign keys) to the mapping. |
| String defaultTargetFieldName = getAttributeName(); |
| addJoinTableRelationKeyFields(getJoinColumnsAndValidate(joinTable.getInverseJoinColumns(), getReferenceDescriptor()), mechanism, defaultTargetFieldName, getReferenceDescriptor(), false); |
| |
| // The spec. requires pessimistic lock to be extend-able to JoinTable. |
| mapping.setShouldExtendPessimisticLockScope(true); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void processMappedByRelationTable(RelationTableMechanism ownerMechanism, RelationTableMechanism mechanism) { |
| // Set the relation table name from the owner. |
| mechanism.setRelationTable(ownerMechanism.getRelationTable()); |
| |
| // In a table per class inheritance we need to update the target |
| // keys before setting them to mapping's source key fields. |
| if (getDescriptor().usesTablePerClassInheritanceStrategy()) { |
| // Update the target key fields. |
| Vector<DatabaseField> targetKeyFields = new Vector<DatabaseField>(); |
| for (DatabaseField targetKeyField : ownerMechanism.getTargetKeyFields()) { |
| DatabaseField newTargetKeyField = targetKeyField.clone(); |
| newTargetKeyField.setTable(getDescriptor().getPrimaryTable()); |
| targetKeyFields.add(newTargetKeyField); |
| } |
| |
| mechanism.setSourceKeyFields(targetKeyFields); |
| } else { |
| // Add all the source foreign keys we found on the owner. |
| mechanism.setSourceKeyFields(ownerMechanism.getTargetKeyFields()); |
| } |
| |
| // Add all the source relation key fields. |
| mechanism.setSourceRelationKeyFields(ownerMechanism.getTargetRelationKeyFields()); |
| |
| // Add all the target foreign keys we found on the owner. |
| mechanism.setTargetKeyFields(ownerMechanism.getSourceKeyFields()); |
| mechanism.setTargetRelationKeyFields(ownerMechanism.getSourceRelationKeyFields()); |
| } |
| |
| /** |
| * INTERNAL: |
| * This method should be called for all mappings even though they may |
| * not support. The reason is that we want to log a message for those |
| * mappings that try to employ a private owned setting when it is not |
| * supported on their mapping. |
| * |
| * Order of checking is as follows: |
| * 1 - check for orphanRemoval first. Through meta data, this can only |
| * be true for 1-1, 1-M and V1-1 |
| * 2 - check for isPrivateOwned. Do no check the variable directly |
| * as the isPrivateOwned method is overridden in those classes that do |
| * not support it (to check if the user decorated the mapping with a |
| * private owned and log a warning message that we are ignoring it.) |
| */ |
| protected void processOrphanRemoval(ForeignReferenceMapping mapping) { |
| if (isOrphanRemoval()) { |
| mapping.setIsPrivateOwned(true); |
| mapping.setCascadeRemove(true); |
| } else { |
| mapping.setIsPrivateOwned(isPrivateOwned()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Process settings common to ForeignReferenceMapping. |
| */ |
| protected void processRelationshipMapping(ForeignReferenceMapping mapping) { |
| // Set the mapping, this must be done first. |
| setMapping(mapping); |
| |
| mapping.setIsLazy(isLazy()); |
| mapping.setAttributeName(getAttributeName()); |
| mapping.setReferenceClassName(getReferenceClassName()); |
| mapping.setIsCascadeOnDeleteSetOnDatabase(isCascadeOnDelete()); |
| |
| // Process join fetch type. |
| processJoinFetch(getJoinFetch(), mapping); |
| |
| // Process the batch fetch if specified. |
| processBatchFetch(mapping); |
| |
| // Process the orphanRemoval or PrivateOwned |
| processOrphanRemoval(mapping); |
| |
| // Will check for PROPERTY access |
| setAccessorMethods(mapping); |
| |
| // Process the cascade types. |
| processCascadeTypes(mapping); |
| |
| // Process any partitioning policies if specified. |
| processPartitioning(); |
| |
| // Process a non-cacheable setting. |
| mapping.setIsCacheable(!isNonCacheable()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the getter and setter access methods for this accessor. |
| */ |
| @Override |
| protected void setAccessorMethods(DatabaseMapping mapping) { |
| super.setAccessorMethods(mapping); |
| |
| // If we have property access and the owning class has field access, |
| // mark the mapping to weave transient field value holders (if it so |
| // applies at weaving time). Setting the accessor methods previously |
| // told us the type of access in turn indicating if we needed to weave |
| // transient value holder fields on the class. With JPA 2.0 and the |
| // possibility of mixed access types this assumption no longer applies. |
| ((ForeignReferenceMapping) mapping).setRequiresTransientWeavedFields(usesPropertyAccess() && ! getClassAccessor().usesPropertyAccess()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setBatchFetch(BatchFetchMetadata batchFetch) { |
| m_batchFetch = batchFetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setCascade(CascadeMetadata cascade) { |
| m_cascade = cascade; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setCascadeOnDelete(Boolean cascadeOnDelete) { |
| m_cascadeOnDelete = cascadeOnDelete; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setFetch(String fetch) { |
| m_fetch = fetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setForeignKey(ForeignKeyMetadata foreignKey) { |
| m_foreignKey = foreignKey; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setJoinColumns(List<JoinColumnMetadata> joinColumns) { |
| m_joinColumns = joinColumns; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setJoinFields(List<JoinFieldMetadata> joinFields) { |
| m_joinFields = joinFields; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setJoinFetch(String joinFetch) { |
| m_joinFetch = joinFetch; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setJoinTable(JoinTableMetadata joinTable) { |
| m_joinTable = joinTable; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setMappedBy(String mappedBy) { |
| m_mappedBy = mappedBy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setNonCacheable(Boolean noncacheable) { |
| m_nonCacheable = noncacheable; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setOrphanRemoval(Boolean orphanRemoval) { |
| m_orphanRemoval = orphanRemoval; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setPrivateOwned(Boolean privateOwned) { |
| m_privateOwned = privateOwned; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setTargetEntity(MetadataClass targetEntity) { |
| m_targetEntity = targetEntity; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used for OX mapping. |
| */ |
| public void setTargetEntityName(String targetEntityName) { |
| m_targetEntityName = targetEntityName; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| protected boolean usesIndirection() { |
| // If eager weaving is enabled, indirection is always used. |
| if (getProject().isWeavingEagerEnabled()) { |
| return true; |
| } |
| |
| return isLazy(); |
| } |
| } |