blob: f08f78a413249344184d343477793250f813e4b7 [file] [log] [blame]
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
// Contributors:
// Guy Pelletier (Oracle), February 28, 2007
// - New file introduced for bug 217880.
// 05/16/2008-1.0M8 Guy Pelletier
// - 218084: Implement metadata merging functionality between mapping files
// 12/12/2008-1.1 Guy Pelletier
// - 249860: Implement table per class inheritance support.
// 12/18/2009-2.1 Guy Pelletier
// - 211323: Add class extractor support to the EclipseLink-ORM.XML Schema
// 03/24/2011-2.3 Guy Pelletier
// - 337323: Multi-tenant with shared schema support (part 1)
package org.eclipse.persistence.internal.jpa.metadata.inheritance;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_INHERITANCE_JOINED;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_INHERITANCE_SINGLE_TABLE;
import static org.eclipse.persistence.internal.jpa.metadata.MetadataConstants.JPA_INHERITANCE_TABLE_PER_CLASS;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.descriptors.TablePerClassPolicy;
import org.eclipse.persistence.internal.jpa.metadata.MetadataDescriptor;
import org.eclipse.persistence.internal.jpa.metadata.MetadataLogger;
import org.eclipse.persistence.internal.jpa.metadata.ORMetadata;
import org.eclipse.persistence.internal.jpa.metadata.accessors.MetadataAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.classes.MappedSuperclassAccessor;
import org.eclipse.persistence.internal.jpa.metadata.accessors.objects.MetadataAnnotation;
/**
* Object to represent inheritance metadata. The processing of this metadata
* to its related class descriptors should be performed by this class.
*
* Key notes:
* - any metadata mapped from XML to this class must be compared in the
* equals method.
* - when loading from annotations, the constructor accepts the metadata
* accessor this metadata was loaded from. Used it to look up any
* 'companion' annotation needed for processing.
* - methods should be preserved in alphabetical order.
*
* @author Guy Pelletier
* @since EclipseLink 1.0
*/
public class InheritanceMetadata extends ORMetadata {
private String m_strategy;
/**
* INTERNAL:
* Used for XML loading.
*/
public InheritanceMetadata() {
super("<inheritance>");
}
/**
* INTERNAL:
* Used for annotation loading.
*/
public InheritanceMetadata(MetadataAnnotation inheritance, MetadataAccessor accessor) {
super(inheritance, accessor);
if (inheritance != null) {
m_strategy = inheritance.getAttributeString("strategy");
}
}
/**
* INTERNAL:
* Set the class extractor class name on the inheritance policy.
*/
protected void addClassExtractor(MetadataDescriptor descriptor, EntityAccessor accessor) {
descriptor.getClassDescriptor().getInheritancePolicy().setClassExtractorName(accessor.processClassExtractor());
}
/**
* INTERNAL:
* Recursive method.
*/
protected void addClassIndicator(MetadataDescriptor descriptor, EntityAccessor accessor) {
if (descriptor.isInheritanceSubclass()) {
addClassIndicator(descriptor.getInheritanceRootDescriptor(), accessor);
} else {
// Get the discriminator value from the accessor (this will do any
// defaulting if necessary and log a message). If the discriminator
// value is null then presumably the inheritance subclass is
// abstract and should be not be added to the class name indicator
// list.
String discriminatorValue = accessor.processDiscriminatorValue();
if (discriminatorValue != null) {
if (descriptor.getClassDescriptor().getInheritancePolicy().getClassIndicatorField().getType() == Integer.class){
try {
descriptor.getClassDescriptor().getInheritancePolicy().addClassNameIndicator(accessor.getJavaClassName(), Integer.valueOf(discriminatorValue));
} catch (NumberFormatException exc){
accessor.getLogger().logWarningMessage(MetadataLogger.WARNING_INCORRECT_DISCRIMINATOR_FORMAT, accessor.getJavaClassName(), discriminatorValue);
}
} else {
descriptor.getClassDescriptor().getInheritancePolicy().addClassNameIndicator(accessor.getJavaClassName(), discriminatorValue);
}
}
}
}
/**
* INTERNAL:
*/
protected void addClassIndicatorField(MetadataDescriptor descriptor, EntityAccessor accessor) {
descriptor.getClassDescriptor().getInheritancePolicy().setClassIndicatorField(accessor.processDiscriminatorColumn());
}
/**
* INTERNAL:
* Recursive method to traverse table per class inheritance hierarchy and
* grab all the 'inherited' accessors for subclasses of the hierarchy.
*
* What we know:
* - All parent classes will already have been processed. Inheritance
* hierarchies are processed top-&gt;down.
* - Always go through the given descriptors pointer to its class accessor,
* as we can not rely on the reloaded accessors for inheritance checks,
* mapped superclasses etc. Use the descriptors provided.
* - When adding accessors from superclasses to an inheritance subclasses
* descriptor, they must be reloaded/cloned and cannot be shared.
* Otherwise the processing of those accessor will only be performed once
* by their 'real' owning entity accessor.
*/
public void addTablePerClassParentMappings(MetadataDescriptor startingDescriptor, MetadataDescriptor realDescriptor) {
EntityAccessor reloadedParentEntity = null;
MetadataDescriptor realParentDescriptor = null;
// If we are an inheritance subclass, recursively call up to the root
// entity so that we can grab a copy of all our inherited mapping
// accessors. Copies of our parent accessors are done by reloading the
// parent entities through OX (if they were originally loaded from XML).
// This is our way of cloning. The reloaded accessors are rebuilt using
// the startingDescriptor context, that is where we want to add the
// accessors.
if (realDescriptor.isInheritanceSubclass() && realDescriptor.getInheritanceRootDescriptor().usesTablePerClassInheritanceStrategy()) {
realParentDescriptor = realDescriptor.getInheritanceParentDescriptor();
reloadedParentEntity = reloadEntity((EntityAccessor) realParentDescriptor.getClassAccessor(), startingDescriptor);
addTablePerClassParentMappings(startingDescriptor, realParentDescriptor);
}
// If we are the starting entity, the processing of our mapped
// superclass and our accessors will be done when we process our
// immediate accessors. Also, our immediate mapped superclasses will
// have other metadata for us to process (and not just the addition of
// accessors). See EntityAccesor process() and processClassMetadata().
if (reloadedParentEntity != null) {
// Be sure to reload the mapped superclass from the 'real' entity
// accessor which has already discovered the list.
EntityAccessor realParentEntityAccessor = (EntityAccessor) realParentDescriptor.getClassAccessor();
for (MappedSuperclassAccessor mappedSuperclass : realParentEntityAccessor.getMappedSuperclasses()) {
// Reload the mapped superclass and add its accessors.
reloadMappedSuperclass(mappedSuperclass, startingDescriptor).addAccessors();
}
// Add the mapping accessors from the reloaded entity.
reloadedParentEntity.addAccessors();
}
}
/**
* INTERNAL:
*/
@Override
public boolean equals(Object objectToCompare) {
if (objectToCompare instanceof InheritanceMetadata) {
return valuesMatch(m_strategy, ((InheritanceMetadata) objectToCompare).getStrategy());
}
return false;
}
@Override
public int hashCode() {
return m_strategy != null ? m_strategy.hashCode() : 0;
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public String getStrategy() {
return m_strategy;
}
/**
* INTERNAL:
* The process method method will be called with the descriptor from
* every entity in the hierarchy.
*/
public void process(MetadataDescriptor descriptor) {
EntityAccessor accessor = (EntityAccessor) descriptor.getClassAccessor();
// Set the correct inheritance policy.
if (m_strategy != null && m_strategy.equals(JPA_INHERITANCE_TABLE_PER_CLASS)) {
setTablePerClassInheritancePolicy(descriptor);
} else {
setInheritancePolicy(descriptor);
}
// Process an inheritance subclass.
if (descriptor.isInheritanceSubclass()) {
MetadataDescriptor rootDescriptor = descriptor.getInheritanceRootDescriptor();
EntityAccessor rootAccessor = (EntityAccessor) rootDescriptor.getClassAccessor();
if (rootDescriptor.usesTablePerClassInheritanceStrategy()) {
MetadataDescriptor parentDescriptor = descriptor.getInheritanceParentDescriptor();
descriptor.getClassDescriptor().getTablePerClassPolicy().addParentDescriptor(parentDescriptor.getClassDescriptor());
parentDescriptor.getClassDescriptor().getTablePerClassPolicy().addChildDescriptor(descriptor.getClassDescriptor());
} else {
// Set the parent class on the inheritance policy.
descriptor.getClassDescriptor().getInheritancePolicy().setParentClassName(descriptor.getInheritanceParentDescriptor().getJavaClassName());
}
// If we have inheritance defined then we are a root parent and the
// strategy should be changing meaning we have double the metadata
// to process.
// Note: if the strategy does not change, then we ignore the
// inheritance hierarchy and continue as if we were a simple
// inheritance subclass.
if (accessor.hasInheritance() && ! equals(rootAccessor.getInheritance())) {
// Process the inheritance root metadata.
processInheritanceRoot(descriptor, accessor);
} else {
// Process the inheritance sub class metadata.
processInheritanceSubclass(descriptor, accessor, rootAccessor);
}
// If the root descriptor has an id class, we need to set the same
// id class on our descriptor.
if (rootDescriptor.hasCompositePrimaryKey()) {
descriptor.setPKClass(rootDescriptor.getPKClass());
}
} else {
// Process the inheritance root metadata.
processInheritanceRoot(descriptor, accessor);
}
}
/**
* INTERNAL:
* Process the inheritance metadata of a root class.
*/
protected void processInheritanceRoot(MetadataDescriptor descriptor, EntityAccessor accessor) {
// If we are an inheritance subclass, then the strategy must be changing
// and we therefore have a little more work to do.
if (descriptor.isInheritanceSubclass()) {
if (descriptor.getInheritanceRootDescriptor().usesTablePerClassInheritanceStrategy()) {
// If our parent used a TABLE_PER_CLASS strategy then we need to
// add the table per class mappings from our parents, including
// their mapped superclasses, to our list of accessors.
addTablePerClassParentMappings(descriptor, descriptor);
} else {
// Indicators are used with all strategies except for
// TABLE_PER_CLASS. However, if the parent used anything but
// TPC we need to process primary key join columns since the
// strategies are changing and we need to link up the classes.
accessor.processInheritancePrimaryKeyJoinColumns();
}
}
// Indicators are used with all strategies except for TABLE_PER_CLASS.
if (! usesTablePerClassStrategy()) {
if (accessor.hasClassExtractor()) {
addClassExtractor(descriptor, accessor);
} else {
addClassIndicatorField(descriptor, accessor);
addClassIndicator(descriptor, accessor);
}
}
}
/**
* INTERNAL:
* Process the inheritance metadata of a sub class.
*/
protected void processInheritanceSubclass(MetadataDescriptor descriptor, EntityAccessor accessor, EntityAccessor rootAccessor) {
if (usesTablePerClassStrategy()) {
// Go through our parents, including their mapped superclasses,
// and add their accessors to our list of accessors.
addTablePerClassParentMappings(descriptor, descriptor);
} else {
// If the root accessor doesn't have a class extractor then add
// the class indicator.
if (! rootAccessor.hasClassExtractor()) {
addClassIndicator(rootAccessor.getDescriptor(), accessor);
}
// Process join columns if necessary.
if (rootAccessor.getInheritance().usesJoinedStrategy()) {
accessor.processInheritancePrimaryKeyJoinColumns();
}
}
}
/**
* INTERNAL:
* Recursive method.
*/
protected void setInheritancePolicy(MetadataDescriptor descriptor) {
if (m_strategy == null && ! descriptor.isInheritanceSubclass()) {
// TODO: Log a defaulting message
}
descriptor.getClassDescriptor().setInheritancePolicy(new InheritancePolicy(descriptor.getClassDescriptor()));
}
/**
* INTERNAL:
* Used for OX mapping.
*/
public void setStrategy(String strategy) {
m_strategy = strategy;
}
/**
* INTERNAL:
*/
protected void setTablePerClassInheritancePolicy(MetadataDescriptor descriptor) {
descriptor.getClassDescriptor().setTablePerClassPolicy(new TablePerClassPolicy(descriptor.getClassDescriptor()));
}
/**
* INTERNAL:
*/
public boolean usesJoinedStrategy() {
return m_strategy != null && m_strategy.equals(JPA_INHERITANCE_JOINED);
}
/**
* INTERNAL:
*/
public boolean usesSingleTableStrategy() {
return m_strategy == null || m_strategy.equals(JPA_INHERITANCE_SINGLE_TABLE);
}
/**
* INTERNAL:
*/
public boolean usesTablePerClassStrategy() {
return m_strategy != null && m_strategy.equals(JPA_INHERITANCE_TABLE_PER_CLASS);
}
}