| /* |
| * 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 |
| // 04/01/2011-2.3 Guy Pelletier |
| // - 337323: Multi-tenant with shared schema support (part 2) |
| // 08/18/2011-2.3.1 Guy Pelletier |
| // - 355093: Add new 'includeCriteria' flag to Multitenant metadata |
| // 09/09/2011-2.3.1 Guy Pelletier |
| // - 356197: Add new VPD type to MultitenantType |
| // 08/01/2012-2.5 Chris Delahunt |
| // - 371950: Metadata caching |
| package org.eclipse.persistence.descriptors; |
| |
| import java.io.*; |
| import java.lang.reflect.*; |
| import java.security.AccessController; |
| import java.security.PrivilegedActionException; |
| import java.util.*; |
| |
| import org.eclipse.persistence.core.descriptors.CoreInheritancePolicy; |
| import org.eclipse.persistence.descriptors.invalidation.CacheInvalidationPolicy; |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.expressions.*; |
| import org.eclipse.persistence.internal.descriptors.OptimisticLockingPolicy; |
| import org.eclipse.persistence.internal.expressions.*; |
| import org.eclipse.persistence.internal.helper.*; |
| import org.eclipse.persistence.internal.queries.*; |
| import org.eclipse.persistence.internal.security.PrivilegedAccessHelper; |
| import org.eclipse.persistence.internal.security.PrivilegedClassForName; |
| import org.eclipse.persistence.internal.security.PrivilegedNewInstanceFromClass; |
| import org.eclipse.persistence.internal.sessions.AbstractSession; |
| import org.eclipse.persistence.internal.sessions.AbstractRecord; |
| import org.eclipse.persistence.mappings.*; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.sessions.remote.*; |
| |
| /** |
| * <p><b>Purpose</b>: Allows customization of an object's inheritance. |
| * The primary supported inheritance model uses a class type indicator |
| * column in the table that stores the object's class type. |
| * The class-to-type mapping is specified on this policy. |
| * The full class name can also be used for the indicator instead of the mapping. |
| * <p>Each subclass can either share their parents table, or in addition add their |
| * own table(s). |
| * <p>For legacy models a customized inheritance class-extractor can be provided. |
| * This allows Java code to be used to compute the class type to use for a row. |
| * When this customized inheritance model is used an only-instances and with-all-subclasses |
| * filter expression may be required for concrete and branch querying. |
| */ |
| public class InheritancePolicy extends CoreInheritancePolicy<AbstractRecord, AbstractSession, ClassDescriptor, DatabaseField> implements Serializable, Cloneable { |
| protected Class parentClass; |
| protected String parentClassName; |
| protected ClassDescriptor parentDescriptor; |
| protected List<ClassDescriptor> childDescriptors; |
| protected DatabaseField classIndicatorField; |
| protected transient Map classIndicatorMapping; |
| protected Map classNameIndicatorMapping; |
| protected transient boolean shouldUseClassNameAsIndicator; |
| protected transient Boolean shouldReadSubclasses; |
| protected DatabaseTable readAllSubclassesView; |
| protected transient List<Object> allChildClassIndicators; |
| protected transient Expression onlyInstancesExpression; |
| protected transient Expression withAllSubclassesExpression; |
| // null if there are no childrenTables, otherwise all tables for reference class plus childrenTables |
| protected transient Vector<DatabaseTable> allTables; |
| // all tables for all subclasses (subclasses of subclasses included), should be in sync with childrenTablesJoinExpressions. |
| protected transient List<DatabaseTable> childrenTables; |
| // join expression for each child table, keyed by the table, should be in sync with childrenTables. |
| protected transient Map<DatabaseTable, Expression> childrenTablesJoinExpressions; |
| // all expressions from childrenTablesJoinExpressions ANDed together |
| protected transient Expression childrenJoinExpression; |
| |
| /** Allow for class extraction method to be specified. */ |
| protected String classExtractorName; |
| protected transient ClassExtractor classExtractor; |
| |
| protected ClassDescriptor descriptor; |
| protected boolean shouldAlwaysUseOuterJoin = false; |
| |
| //CR 4005 |
| protected boolean useDescriptorsToValidateInheritedObjects = false; |
| |
| /** Define if an outer join should be used to read subclasses. */ |
| protected boolean shouldOuterJoinSubclasses = false; |
| |
| // used by the entity-mappings XML writer to determine inheritance strategy |
| protected boolean isJoinedStrategy; |
| |
| /** PERF: Cache root descriptor. */ |
| protected ClassDescriptor rootParentDescriptor; |
| |
| protected boolean describesNonPersistentSubclasses = false; |
| |
| /** |
| * INTERNAL: |
| * Create a new policy. |
| * Only descriptors involved in inheritance should have a policy. |
| */ |
| public InheritancePolicy() { |
| this.classIndicatorMapping = new HashMap<>(10); |
| this.classNameIndicatorMapping = new HashMap<>(10); |
| this.shouldUseClassNameAsIndicator = false; |
| this.allChildClassIndicators = new ArrayList<>(4); |
| this.childDescriptors = new ArrayList<>(4); |
| this.setJoinedStrategy(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Create a new policy. |
| * Only descriptors involved in inheritance should have a policy. |
| */ |
| public InheritancePolicy(ClassDescriptor descriptor) { |
| this(); |
| this.descriptor = descriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * Add child descriptor to the parent descriptor. |
| */ |
| public void addChildDescriptor(ClassDescriptor childDescriptor) { |
| getChildDescriptors().add(childDescriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| * childrenTablesJoinExpressions, childrenTables, allTables and childrenJoinExpression |
| * are created simultaneously and kept in sync. |
| */ |
| protected void addChildTableJoinExpression(DatabaseTable table, Expression expression) { |
| if (this.childrenTablesJoinExpressions == null) { |
| this.childrenTablesJoinExpressions = new HashMap<>(); |
| // childrenTables should've been null, too |
| this.childrenTables = new ArrayList<>(); |
| // allTables should've been null, too |
| this.allTables = new Vector<>(getDescriptor().getTables()); |
| } |
| // Avoid duplicates as two independent subclasses may have the same table. |
| if (!this.childrenTables.contains(table)) { |
| this.childrenTables.add(table); |
| } |
| if (!this.allTables.contains(table)) { |
| this.allTables.add(table); |
| } |
| this.childrenTablesJoinExpressions.put(table, expression); |
| this.childrenJoinExpression = expression.and(this.childrenJoinExpression); |
| } |
| |
| /** |
| * INTERNAL: |
| * call addChildTableJoinExpression on all parents |
| */ |
| public void addChildTableJoinExpressionToAllParents(DatabaseTable table, Expression expression) { |
| ClassDescriptor parentDescriptor = getParentDescriptor(); |
| while(parentDescriptor != null) { |
| InheritancePolicy parentPolicy = parentDescriptor.getInheritancePolicy(); |
| parentPolicy.addChildTableJoinExpression(table, expression); |
| parentDescriptor = parentPolicy.getParentDescriptor(); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Add a class indicator for the root classes subclass. |
| * The indicator is used to determine the class to use for a row read from the database, |
| * and to query only instances of a class from the database. |
| * Every concrete persistent subclass must have a single unique indicator defined for it. |
| * If the root class is concrete then it must also define an indicator. |
| * Only the root class's descriptor of the entire inheritance hierarchy can define the class indicator mapping. |
| */ |
| public void addClassIndicator(Class childClass, Object typeValue) { |
| // Note we should think about supporting null values. |
| // Store as key and value for bi-directional lookup. |
| getClassIndicatorMapping().put(typeValue, childClass); |
| getClassIndicatorMapping().put(childClass, typeValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * Add the class name reference by class name, used by the MW. |
| */ |
| @Override |
| public void addClassNameIndicator(String childClassName, Object typeValue) { |
| getClassNameIndicatorMapping().put(childClassName, typeValue); |
| } |
| |
| /** |
| * INTERNAL: |
| * Add abstract class indicator information to the database row. This is |
| * required when building a row for an insert or an update of a concrete child |
| * descriptor. |
| * This is only used to build a template row. |
| */ |
| public void addClassIndicatorFieldToInsertRow(AbstractRecord databaseRow) { |
| if (hasClassExtractor()) { |
| return; |
| } |
| |
| DatabaseField field = getClassIndicatorField(); |
| databaseRow.put(field, null); |
| } |
| |
| /** |
| * INTERNAL: |
| * Add abstract class indicator information to the database row. This is |
| * required when building a row for an insert or an update of a concrete child |
| * descriptor. |
| */ |
| @Override |
| public void addClassIndicatorFieldToRow(AbstractRecord databaseRow) { |
| if (hasClassExtractor()) { |
| return; |
| } |
| |
| DatabaseField field = getClassIndicatorField(); |
| Object value = getClassIndicatorValue(); |
| |
| databaseRow.put(field, value); |
| } |
| |
| /** |
| * INTERNAL: |
| * Post initialize the child descriptors |
| */ |
| protected void addClassIndicatorTypeToParent(Object indicator) { |
| ClassDescriptor parentDescriptor = getDescriptor().getInheritancePolicy().getParentDescriptor(); |
| |
| if (parentDescriptor.getInheritancePolicy().isChildDescriptor()) { |
| if (parentDescriptor.getInheritancePolicy().shouldReadSubclasses()) { |
| parentDescriptor.getInheritancePolicy().getAllChildClassIndicators().add(indicator); |
| } |
| parentDescriptor.getInheritancePolicy().addClassIndicatorTypeToParent(indicator); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Recursively adds fields to all the parents |
| */ |
| protected void addFieldsToParent(Vector fields) { |
| if (isChildDescriptor()) { |
| if (getParentDescriptor().isInvalid()) { |
| return; |
| } |
| ClassDescriptor parentDescriptor = getParentDescriptor(); |
| if (parentDescriptor.getInheritancePolicy().shouldReadSubclasses()) { |
| Helper.addAllUniqueToVector(parentDescriptor.getAllFields(), fields); |
| } |
| parentDescriptor.getInheritancePolicy().addFieldsToParent(fields); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return a select statement that will be used to query the class indicators required to query. |
| * This is used in the abstract-multiple read. |
| */ |
| public SQLSelectStatement buildClassIndicatorSelectStatement(ObjectLevelReadQuery query) { |
| SQLSelectStatement selectStatement; |
| selectStatement = new SQLSelectStatement(); |
| selectStatement.useDistinct(); |
| selectStatement.addTable(classIndicatorField.getTable()); |
| selectStatement.addField(getClassIndicatorField()); |
| // 2612538 - the default size of Map (32) is appropriate |
| Map clonedExpressions = new IdentityHashMap(); |
| selectStatement.setWhereClause(((ExpressionQueryMechanism)query.getQueryMechanism()).buildBaseSelectionCriteria(false, clonedExpressions)); |
| appendWithAllSubclassesExpression(selectStatement); |
| selectStatement.setTranslationRow(query.getTranslationRow()); |
| if (query.isReadAllQuery() && ((ReadAllQuery)query).hasHierarchicalExpressions()) { |
| ReadAllQuery readAllQuery = (ReadAllQuery)query; |
| selectStatement.setHierarchicalQueryExpressions(readAllQuery.getStartWithExpression(), readAllQuery.getConnectByExpression(), readAllQuery.getOrderSiblingsByExpressions(), readAllQuery.getDirection()); |
| } |
| selectStatement.setHintString(query.getHintString()); |
| |
| selectStatement.normalize(query.getSession(), getDescriptor(), clonedExpressions); |
| |
| return selectStatement; |
| } |
| |
| /** |
| * INTERNAL: |
| * Append the branch with all subclasses expression to the statement. |
| */ |
| public void appendWithAllSubclassesExpression(SQLSelectStatement selectStatement) { |
| if (getWithAllSubclassesExpression() != null) { |
| // For Flashback: Must always rebuild with simple expression on right. |
| if (selectStatement.getWhereClause() == null) { |
| selectStatement.setWhereClause((Expression)getWithAllSubclassesExpression().clone()); |
| } else { |
| selectStatement.setWhereClause(selectStatement.getWhereClause().and(getWithAllSubclassesExpression())); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Build a select statement for all subclasses on the view using the same |
| * selection criteria as the query. |
| */ |
| public SQLSelectStatement buildViewSelectStatement(ObjectLevelReadQuery query) { |
| // 2612538 - the default size of Map (32) is appropriate |
| Map clonedExpressions = new IdentityHashMap(); |
| ExpressionQueryMechanism mechanism = (ExpressionQueryMechanism)query.getQueryMechanism(); |
| |
| // CR#3166555 - Have the mechanism build the statement to avoid duplicating code and ensure that lock-mode, hints, hierarchical, etc. are set. |
| SQLSelectStatement selectStatement = mechanism.buildBaseSelectStatement(false, clonedExpressions); |
| selectStatement.setTables(new ArrayList<>(1)); |
| selectStatement.addTable(getReadAllSubclassesView()); |
| |
| // Case, normal read for branch inheritance class that reads subclasses all in its own table(s). |
| if (getWithAllSubclassesExpression() != null) { |
| Expression branchIndicator = (Expression)getWithAllSubclassesExpression().clone(); |
| if (branchIndicator != null) { |
| selectStatement.setWhereClause(branchIndicator.and(selectStatement.getWhereClause())); |
| } |
| } |
| |
| selectStatement.setFields(mechanism.getSelectionFields(selectStatement, true)); |
| selectStatement.normalizeForView(query.getSession(), getDescriptor(), clonedExpressions); |
| // Allow for joining indexes to be computed to ensure distinct rows |
| if (query.hasJoining()) { |
| query.getJoinedAttributeManager().computeJoiningMappingIndexes(false, query.getSession(), 0); |
| } |
| |
| return selectStatement; |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is invoked only for the abstract descriptors. |
| */ |
| @Override |
| public Class classFromRow(AbstractRecord rowFromDatabase, AbstractSession session) throws DescriptorException { |
| if (hasClassExtractor()) { |
| return getClassExtractor().extractClassFromRow(rowFromDatabase, session); |
| } |
| |
| Object classFieldValue = session.getDatasourcePlatform().getConversionManager().convertObject(rowFromDatabase.get(getClassIndicatorField()), getClassIndicatorField().getType()); |
| |
| if (classFieldValue == null) { |
| throw DescriptorException.missingClassIndicatorField(rowFromDatabase, getDescriptor()); |
| } |
| |
| return classFromValue(classFieldValue, session); |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is used to turn the a raw database field value classFieldValue into a Class object. Used to determine |
| * which class objects to build from database results, and for class type expression |
| */ |
| public Class classFromValue(Object classFieldValue, AbstractSession session) throws DescriptorException { |
| Class concreteClass; |
| if (!shouldUseClassNameAsIndicator()) { |
| concreteClass = (Class)getClassIndicatorMapping().get(classFieldValue); |
| if (concreteClass == null) { |
| throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor()); |
| } |
| } else { |
| try { |
| String className = (String)classFieldValue; |
| //PWK 2.5.1.7 can not use class for name, must go through conversion manager. |
| //Should use the root ClassDescriptor's classloader to avoid loading from a loader other |
| //than the one that loaded the project |
| concreteClass = getDescriptor().getJavaClass().getClassLoader().loadClass(className); |
| if (concreteClass == null) { |
| throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor()); |
| } |
| } catch (ClassNotFoundException e) { |
| throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor()); |
| } catch (ClassCastException e) { |
| throw DescriptorException.missingClassForIndicatorFieldValue(classFieldValue, getDescriptor()); |
| } |
| } |
| |
| return concreteClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Clone the policy |
| */ |
| @Override |
| public Object clone() { |
| InheritancePolicy clone = null; |
| |
| try { |
| clone = (InheritancePolicy)super.clone(); |
| if (hasClassIndicator()) { |
| clone.setClassIndicatorField(clone.getClassIndicatorField().clone()); |
| } |
| } catch (Exception exception) { |
| throw new InternalError("clone failed"); |
| } |
| |
| return clone; |
| } |
| |
| /** |
| * INTERNAL: |
| * Convert all the class-name-based settings in this InheritancePolicy to actual class-based settings. |
| * This method is used when converting a project that has been built with class names to a project with classes. |
| * It will also convert referenced classes to the versions of the classes from the classLoader. |
| */ |
| public void convertClassNamesToClasses(ClassLoader classLoader) { |
| Iterator keysEnum = getClassNameIndicatorMapping().keySet().iterator(); |
| Iterator valuesEnum = getClassNameIndicatorMapping().values().iterator(); |
| // Clear old classes (after class names have been lazy initialized). |
| classIndicatorMapping = new HashMap(); |
| while (keysEnum.hasNext()) { |
| Object key = keysEnum.next(); |
| Object value = valuesEnum.next(); |
| |
| Class<?> theClass = convertClassNameToClass((String) key, classLoader); |
| classIndicatorMapping.put(theClass, value); |
| classIndicatorMapping.put(value, theClass); |
| } |
| |
| // Initialize the parent class name. |
| if (getParentClassName() != null){ |
| setParentClass(convertClassNameToClass(getParentClassName(), classLoader)); |
| } |
| |
| // Initialize the class extractor name. |
| if (classExtractorName != null) { |
| Class<ClassExtractor> classExtractorClass = convertClassNameToClass(classExtractorName, classLoader); |
| |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()) { |
| try { |
| setClassExtractor( AccessController.<ClassExtractor>doPrivileged(new PrivilegedNewInstanceFromClass<>(classExtractorClass))); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(classExtractorName, exception.getException()); |
| } |
| } else { |
| setClassExtractor(PrivilegedAccessHelper.<ClassExtractor>newInstanceFromClass(classExtractorClass)); |
| } |
| } catch (ReflectiveOperationException ex) { |
| throw ValidationException.reflectiveExceptionWhileCreatingClassInstance(classExtractorName, ex); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Convert the given className to an actual class. |
| */ |
| protected <T> Class<T> convertClassNameToClass(String className, ClassLoader classLoader) { |
| try { |
| if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){ |
| try { |
| return AccessController.<Class<T>>doPrivileged(new PrivilegedClassForName<>(className, true, classLoader)); |
| } catch (PrivilegedActionException exception) { |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, (Exception)exception.getCause()); |
| } |
| } else { |
| return PrivilegedAccessHelper.<T>getClassForName(className, true, classLoader); |
| } |
| } catch (ClassNotFoundException exc){ |
| throw ValidationException.classNotFoundWhileConvertingClassNames(className, exc); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor to only read instance of itself when queried. |
| * This is used with inheritance to configure the result of queries. |
| * By default this is true for root inheritance descriptors, and false for all others. |
| */ |
| public void dontReadSubclassesOnQueries() { |
| setShouldReadSubclasses(false); |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor not to use the class' full name as the indicator. |
| * The class indicator is used with inheritance to determine the class from a row. |
| * By default a class indicator mapping is required, this can be set to true if usage of the class name is desired. |
| * The field must be of a large enough size to store the fully qualified class name. |
| */ |
| public void dontUseClassNameAsIndicator() { |
| setShouldUseClassNameAsIndicator(false); |
| } |
| |
| /** |
| * INTERNAL: |
| * Stores class indicators for all child and children's children. |
| * Used for queries on branch classes only. |
| */ |
| protected List<Object> getAllChildClassIndicators() { |
| if (allChildClassIndicators == null) { |
| allChildClassIndicators = new ArrayList<>(4); |
| } |
| return allChildClassIndicators; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns all the child descriptors, even descriptors for subclasses of |
| * subclasses. |
| * Required for bug 3019934. |
| */ |
| @Override |
| public List<ClassDescriptor> getAllChildDescriptors() { |
| // Guess the number of child descriptors... |
| List<ClassDescriptor> allChildDescriptors = new ArrayList<>(this.getAllChildClassIndicators().size()); |
| return getAllChildDescriptors(allChildDescriptors); |
| } |
| |
| /** |
| * INTERNAL: |
| * Recursive subroutine of getAllChildDescriptors. |
| */ |
| protected List<ClassDescriptor> getAllChildDescriptors(List<ClassDescriptor> allChildDescriptors) { |
| for (ClassDescriptor childDescriptor : getChildDescriptors()) { |
| allChildDescriptors.add(childDescriptor); |
| childDescriptor.getInheritancePolicyOrNull().getAllChildDescriptors(allChildDescriptors); |
| } |
| return allChildDescriptors; |
| } |
| |
| /** |
| * INTERNAL: |
| * if reads subclasses, all tables for all read subclasses (indirect included). |
| */ |
| public List<DatabaseTable> getChildrenTables() { |
| return childrenTables; |
| } |
| |
| /** |
| * INTERNAL: |
| * join expression for each child table, keyed by the table |
| */ |
| public Map<DatabaseTable, Expression> getChildrenTablesJoinExpressions() { |
| return childrenTablesJoinExpressions; |
| } |
| |
| /** |
| * INTERNAL: |
| * all expressions from childrenTablesJoinExpressions ANDed together |
| */ |
| public Expression getChildrenJoinExpression() { |
| return childrenJoinExpression; |
| } |
| |
| /** |
| * INTERNAL: |
| * all tables for reference class plus childrenTables |
| */ |
| public Vector<DatabaseTable> getAllTables() { |
| if (allTables == null) { |
| return this.getDescriptor().getTables(); |
| } else { |
| return allTables; |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the immediate child descriptors. Only descriptors from |
| * direct subclasses are returned. |
| */ |
| public List<ClassDescriptor> getChildDescriptors() { |
| return childDescriptors; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return all the classExtractionMethod |
| */ |
| protected Method getClassExtractionMethod() { |
| if (classExtractor instanceof MethodClassExtractor) { |
| return ((MethodClassExtractor)classExtractor).getClassExtractionMethod(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * A class extraction method can be registered with the descriptor to override the default inheritance mechanism. |
| * This allows for a user defined class indicator in place of providing an explicit class indicator field. |
| * The method registered must be a static method on the class which has that descriptor. The method must take a |
| * Record as an argument (for example, a DatabaseRecord), and must return the class to use for that record. |
| * This method will be used to decide which class to instantiate when reading from the database. |
| * It is the application's responsibility to populate any typing information in the database required |
| * to determine the class from the record. |
| * If this method is used, then the class indicator field and mapping cannot be used, and in addition, |
| * the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly. |
| * |
| * @see #setWithAllSubclassesExpression(Expression) |
| * @see #setOnlyInstancesExpression(Expression) |
| */ |
| public String getClassExtractionMethodName() { |
| if (classExtractor instanceof MethodClassExtractor) { |
| return ((MethodClassExtractor)classExtractor).getClassExtractionMethodName(); |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * A class extractor can be registered with the descriptor to override the default inheritance mechanism. |
| * This allows for a user defined class indicator in place of providing an explicit class indicator field. |
| * The instance registered must extend the ClassExtractor class and implement the extractClass(Map) method. |
| * The method must take database row (a Record/Map) as an argument and must return the class to use for that row. |
| * This method will be used to decide which class to instantiate when reading from the database. |
| * It is the application's responsibility to populate any typing information in the database required |
| * to determine the class from the row, such as usage of a direct or transformation mapping for the type fields. |
| * If this method is used then the class indicator field and mapping cannot be used, and in addition, |
| * the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly. |
| * |
| * @see #setWithAllSubclassesExpression(Expression) |
| * @see #setOnlyInstancesExpression(Expression) |
| */ |
| public ClassExtractor getClassExtractor() { |
| return classExtractor; |
| } |
| |
| /** |
| * ADVANCED: |
| * A class extractor can be registered with the descriptor to override the default inheritance mechanism. |
| * This allows for a user defined class indicator in place of providing an explicit class indicator field. |
| * The instance registered must extend the ClassExtractor class and implement the extractClass(Map) method. |
| * The method must take database row (a Record/Map) as an argument and must return the class to use for that row. |
| * This method will be used to decide which class to instantiate when reading from the database. |
| * It is the application's responsibility to populate any typing information in the database required |
| * to determine the class from the row, such as usage of a direct or transformation mapping for the type fields. |
| * If this method is used then the class indicator field and mapping cannot be used, and in addition, |
| * the descriptor's withAllSubclasses and onlyInstances expressions must also be setup correctly. |
| * |
| * @see #setWithAllSubclassesExpression(Expression) |
| * @see #setOnlyInstancesExpression(Expression) |
| */ |
| public void setClassExtractor(ClassExtractor classExtractor) { |
| this.classExtractor = classExtractor; |
| } |
| |
| /** |
| * ADVANCED: |
| * Set the class extractor class name. At descriptor initialize time this |
| * class will be converted to a Class and set as the ClassExtractor. This |
| * method is called from JPA. |
| * |
| * @see #setClassExtractor setClassExtractor for more information on the ClassExtractor class. |
| */ |
| @Override |
| public void setClassExtractorName(String classExtractorName) { |
| this.classExtractorName = classExtractorName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the class indicator associations for XML. |
| * List of class-name/value associations. |
| */ |
| public Vector<Association> getClassIndicatorAssociations() { |
| Vector<Association> associations = new Vector<>(getClassNameIndicatorMapping().size() / 2); |
| Iterator classesEnum = getClassNameIndicatorMapping().keySet().iterator(); |
| Iterator valuesEnum = getClassNameIndicatorMapping().values().iterator(); |
| while (classesEnum.hasNext()) { |
| Object className = classesEnum.next(); |
| |
| // If the project was built in runtime is a class, MW is a string. |
| if (className instanceof Class) { |
| className = ((Class<?>)className).getName(); |
| } |
| Object value = valuesEnum.next(); |
| associations.addElement(new TypedAssociation(className, value)); |
| } |
| |
| return associations; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns field that the class type indicator is store when using inheritance. |
| */ |
| @Override |
| public DatabaseField getClassIndicatorField() { |
| return classIndicatorField; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the class indicator field name. |
| * This is the name of the field in the table that stores what type of object this is. |
| */ |
| @Override |
| public String getClassIndicatorFieldName() { |
| if (getClassIndicatorField() == null) { |
| return null; |
| } else { |
| return getClassIndicatorField().getQualifiedName(); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the association of indicators and classes using specified ConversionManager |
| */ |
| @Override |
| public Map getClassIndicatorMapping() { |
| if (classIndicatorMapping == null) { |
| classIndicatorMapping = new HashMap(10); |
| } |
| return classIndicatorMapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the mapping from class name to indicator, used by MW. |
| */ |
| @Override |
| public Map getClassNameIndicatorMapping() { |
| if (classNameIndicatorMapping.isEmpty() && classIndicatorMapping!=null && !classIndicatorMapping.isEmpty()) { |
| Iterator keysEnum = classIndicatorMapping.keySet().iterator(); |
| Iterator valuesEnum = classIndicatorMapping.values().iterator(); |
| while (keysEnum.hasNext()) { |
| Object key = keysEnum.next(); |
| Object value = valuesEnum.next(); |
| if (key instanceof Class) { |
| String className = ((Class)key).getName(); |
| classNameIndicatorMapping.put(className, value); |
| } |
| } |
| } |
| |
| return classNameIndicatorMapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns value of the abstract class indicator for the Java class. |
| */ |
| protected Object getClassIndicatorValue() { |
| return getClassIndicatorValue(getDescriptor().getJavaClass()); |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the indicator field value for the given class |
| * If no abstract indicator mapping is specified, use the class name. |
| */ |
| protected Object getClassIndicatorValue(Class javaClass) { |
| if (shouldUseClassNameAsIndicator()) { |
| return javaClass.getName(); |
| } else { |
| return getClassIndicatorMapping().get(javaClass); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the descriptor which the policy belongs to. |
| */ |
| @Override |
| public ClassDescriptor getDescriptor() { |
| return descriptor; |
| } |
| |
| /** |
| * ADVANCED: |
| * Determines whether the descriptors using this inheritance policy |
| * should be used as descriptors for subclasses of the classes they |
| * describe if those subclasses do not have their own descriptor |
| * |
| * e.g. If Employee.class has a descriptor and EmployeeSubClass does |
| * not have a descriptor, if describesNonPersistenceSubclasses is true |
| * Employee's descriptor will be used as the descriptor for Employee |
| */ |
| public boolean getDescribesNonPersistentSubclasses(){ |
| return describesNonPersistentSubclasses; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the 'only instances expression'. |
| */ |
| public Expression getOnlyInstancesExpression() { |
| return onlyInstancesExpression; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return the parent class. |
| */ |
| @Override |
| public Class getParentClass() { |
| return parentClass; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the parent class name. |
| */ |
| public String getParentClassName() { |
| if ((parentClassName == null) && (parentClass != null)) { |
| parentClassName = parentClass.getName(); |
| } |
| return parentClassName; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the parent descriptor. |
| */ |
| @Override |
| public ClassDescriptor getParentDescriptor() { |
| return parentDescriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * The view can be used to optimize/customize the query for all subclasses where they have multiple tables. |
| * This view can do the outer join, we require the view because we cannot generate dynamic platform independent SQL |
| * for outer joins (i.e. not possible to do so either). |
| */ |
| public DatabaseTable getReadAllSubclassesView() { |
| return readAllSubclassesView; |
| } |
| |
| /** |
| * ADVANCED: |
| * The view can be used to optimize/customize the query for all subclasses where they have multiple tables. |
| * This view can use outer joins or unions to combine the results of selecting from all of the subclass tables. |
| * If a view is not given then TopLink must make an individual call for each subclass. |
| */ |
| public String getReadAllSubclassesViewName() { |
| if (getReadAllSubclassesView() == null) { |
| return null; |
| } |
| return getReadAllSubclassesView().getName(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the root parent descriptor |
| */ |
| public ClassDescriptor getRootParentDescriptor() { |
| if (this.rootParentDescriptor == null) { |
| if (isRootParentDescriptor()) { |
| this.rootParentDescriptor = getDescriptor(); |
| } else { |
| this.rootParentDescriptor = getParentDescriptor().getInheritancePolicy().getRootParentDescriptor(); |
| } |
| } |
| return rootParentDescriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * use aggregate in inheritance |
| */ |
| public ClassDescriptor getSubclassDescriptor(Class theClass) { |
| if (hasChildren()) { |
| for (Iterator<ClassDescriptor> enumtr = getChildDescriptors().iterator(); enumtr.hasNext();) { |
| ClassDescriptor childDescriptor = enumtr.next(); |
| if (childDescriptor.getJavaClass().equals(theClass)) { |
| return childDescriptor; |
| } else { |
| ClassDescriptor descriptor = childDescriptor.getInheritancePolicy().getSubclassDescriptor(theClass); |
| if (descriptor != null) { |
| return descriptor; |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns descriptor corresponding to the class owning the policy or its subclass - otherwise null. |
| */ |
| public ClassDescriptor getDescriptor(Class theClass) { |
| if(getDescriptor().getJavaClass().equals(theClass)) { |
| return getDescriptor(); |
| } else { |
| return getSubclassDescriptor(theClass); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * return if we should use the descriptor inheritance to determine |
| * if an object can be returned from the identity map or not. |
| */ |
| public boolean getUseDescriptorsToValidateInheritedObjects() { |
| return useDescriptorsToValidateInheritedObjects; |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the Expression which gets all subclasses. |
| */ |
| public Expression getWithAllSubclassesExpression() { |
| return withAllSubclassesExpression; |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if descriptor has children |
| */ |
| public boolean hasChildren() { |
| return !getChildDescriptors().isEmpty(); |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public boolean hasClassExtractor() { |
| return getClassExtractor() != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Checks if the class is involved in inheritance |
| */ |
| public boolean hasClassIndicator() { |
| return getClassIndicatorField() != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if any children of this descriptor require information from another table |
| * not specified at the parent level. |
| */ |
| public boolean hasMultipleTableChild() { |
| return childrenTables != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if a view is used for inheritance reads. |
| */ |
| public boolean hasView() { |
| return getReadAllSubclassesView() != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the inheritance properties of the descriptor once the mappings are initialized. |
| * This is done before formal postInitialize during the end of mapping initialize. |
| */ |
| public void initialize(AbstractSession session) { |
| // Must reset this in the case that a child thinks it wants to read its subclasses. |
| if ((this.shouldReadSubclasses == null) || shouldReadSubclasses()) { |
| setShouldReadSubclasses(!getChildDescriptors().isEmpty()); |
| } |
| |
| if (isChildDescriptor()) { |
| getDescriptor().setMappings(Helper.concatenateVectors(getParentDescriptor().getMappings(), getDescriptor().getMappings())); |
| getDescriptor().setQueryKeys(Helper.concatenateMaps(getParentDescriptor().getQueryKeys(), getDescriptor().getQueryKeys())); |
| addFieldsToParent(getDescriptor().getFields()); |
| // Parents fields must be first for indexing to work. |
| Vector parentsFields = (Vector)getParentDescriptor().getFields().clone(); |
| |
| //bug fix on Oracle duplicate field SQL using "order by" |
| Helper.addAllUniqueToVector(parentsFields, getDescriptor().getFields()); |
| getDescriptor().setFields(parentsFields); |
| |
| if (getClassIndicatorValue() != null) { |
| if (shouldReadSubclasses()) { |
| getAllChildClassIndicators().add(getClassIndicatorValue()); |
| } |
| addClassIndicatorTypeToParent(getClassIndicatorValue()); |
| } |
| |
| initializeOptimisticLocking(); |
| |
| if (!getDescriptor().hasReturningPolicy() && getParentDescriptor().hasReturningPolicy()) { |
| getDescriptor().setReturningPolicy(new ReturningPolicy()); |
| } |
| |
| // create CMPPolicy on child if parent has one and it does not. Then copy individual fields |
| CMPPolicy parentCMPPolicy = getDescriptor().getInheritancePolicy().getParentDescriptor().getCMPPolicy(); |
| if (parentCMPPolicy != null) { |
| CMPPolicy cmpPolicy = getDescriptor().getCMPPolicy(); |
| if (cmpPolicy == null) { |
| cmpPolicy = new CMPPolicy(); |
| getDescriptor().setCMPPolicy(cmpPolicy); |
| } |
| |
| //copy pessimistic locking policy from the parent if this child does not have one |
| if (parentCMPPolicy.hasPessimisticLockingPolicy() && !cmpPolicy.hasPessimisticLockingPolicy()) { |
| cmpPolicy.setPessimisticLockingPolicy((PessimisticLockingPolicy)parentCMPPolicy.getPessimisticLockingPolicy().clone()); |
| } |
| |
| // copy forceUpdate value if not set in child |
| if (cmpPolicy.internalGetForceUpdate() == null) { |
| cmpPolicy.internalSetForceUpdate(parentCMPPolicy.internalGetForceUpdate()); |
| } |
| |
| // copy updateAllFields value if not set in child |
| if (cmpPolicy.internalGetUpdateAllFields() == null) { |
| cmpPolicy.internalSetUpdateAllFields(parentCMPPolicy.internalGetUpdateAllFields()); |
| } |
| } |
| |
| // Inherit the native connection requirement. |
| if (getParentDescriptor().isNativeConnectionRequired()) { |
| getDescriptor().setIsNativeConnectionRequired(true); |
| } |
| |
| if (getDescriptor().getPartitioningPolicy() == null) { |
| getDescriptor().setPartitioningPolicy(getParentDescriptor().getPartitioningPolicy()); |
| } |
| } |
| |
| initializeOnlyInstancesExpression(); |
| initializeWithAllSubclassesExpression(); |
| if (hasView()) { |
| // Set the table qualifier on the inheritance view. |
| if ((session.getDatasourcePlatform().getTableQualifier().length() != 0) && (getReadAllSubclassesView().getTableQualifier().length() == 0)) { |
| getReadAllSubclassesView().setTableQualifier(session.getDatasourcePlatform().getTableQualifier()); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Setup the default classExtractionMethod, or if one was specified by the user make sure it is valid. |
| */ |
| protected void initializeClassExtractor(AbstractSession session) throws DescriptorException { |
| if (getClassExtractor() == null) { |
| if (isChildDescriptor()) { |
| setClassExtractor(getParentDescriptor().getInheritancePolicy().getClassExtractor()); |
| } |
| } else { |
| getClassExtractor().initialize(getDescriptor(), session); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the expression to use to check the specific type field. |
| */ |
| protected void initializeOnlyInstancesExpression() throws DescriptorException { |
| if (getOnlyInstancesExpression() == null) { |
| if (hasClassExtractor()) { |
| return; |
| } |
| Object typeValue = getClassIndicatorValue(); |
| if (typeValue == null) { |
| if (shouldReadSubclasses()) { |
| return;// No indicator is allowed in this case. |
| } |
| |
| throw DescriptorException.valueNotFoundInClassIndicatorMapping(getParentDescriptor(), getDescriptor()); |
| } |
| |
| DatabaseField typeField = getClassIndicatorField(); |
| if (typeField == null) { |
| throw DescriptorException.classIndicatorFieldNotFound(getParentDescriptor(), getDescriptor()); |
| } |
| |
| // cr3546 |
| if (shouldAlwaysUseOuterJoin()) { |
| setOnlyInstancesExpression(new ExpressionBuilder().getField(typeField).equalOuterJoin(typeValue)); |
| } else { |
| setOnlyInstancesExpression(new ExpressionBuilder().getField(typeField).equal(typeValue)); |
| } |
| } |
| |
| // If subclasses are read, this is anded dynamically. |
| if (!shouldReadSubclasses()) { |
| getDescriptor().getQueryManager().setAdditionalJoinExpression(getOnlyInstancesExpression().and(getDescriptor().getQueryManager().getAdditionalJoinExpression())); |
| } |
| } |
| |
| |
| /** |
| * INTERNAL: |
| * Potentially override the optimistic locking behavior |
| */ |
| protected void initializeOptimisticLocking(){ |
| // CR#3214106, do not override if specified in subclass. |
| if (!getDescriptor().usesOptimisticLocking() && getParentDescriptor().usesOptimisticLocking()) { |
| getDescriptor().setOptimisticLockingPolicy((OptimisticLockingPolicy)getParentDescriptor().getOptimisticLockingPolicy().clone()); |
| getDescriptor().getOptimisticLockingPolicy().setDescriptor(getDescriptor()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Potentially override the cache invalidation behavior |
| */ |
| protected void initializeCacheInvalidationPolicy() { |
| CacheInvalidationPolicy parentPolicyClone = (CacheInvalidationPolicy)getParentDescriptor().getCacheInvalidationPolicy().clone(); |
| getDescriptor().setCacheInvalidationPolicy(parentPolicyClone); |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialize the expression to use for queries to the class and its subclasses. |
| */ |
| protected void initializeWithAllSubclassesExpression() throws DescriptorException { |
| if (getWithAllSubclassesExpression() == null) { |
| if (hasClassExtractor()) { |
| return; |
| } |
| if (isChildDescriptor() && shouldReadSubclasses()) { |
| setWithAllSubclassesExpression(new ExpressionBuilder().getField(getClassIndicatorField()).in(getAllChildClassIndicators())); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Check if it is a child descriptor. |
| */ |
| public boolean isChildDescriptor() { |
| return getParentClassName() != null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Indicate whether a single table or joined inheritance strategy is being used. Since we currently do |
| * not support TABLE_PER_CLASS, indicating either joined/not joined is sufficient. |
| * |
| * @return isJoinedStrategy value |
| */ |
| public boolean isJoinedStrategy() { |
| return isJoinedStrategy; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return whether or not is root parent descriptor |
| */ |
| @Override |
| public boolean isRootParentDescriptor() { |
| return getParentDescriptor() == null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Initialized the inheritance properties that cannot be initialized |
| * until after the mappings have been. |
| */ |
| public void postInitialize(AbstractSession session) { |
| if (isChildDescriptor()) { |
| ClassDescriptor parent = getParentDescriptor(); |
| if (getDescriptor().shouldAcquireCascadedLocks()){ |
| parent.setShouldAcquireCascadedLocks(true); |
| } |
| if (getDescriptor().hasRelationships()){ |
| parent.setHasRelationships(true); |
| } |
| while (parent != null) { |
| if (parent.hasMultipleTableConstraintDependecy()) { |
| getDescriptor().setHasMultipleTableConstraintDependecy(true); |
| break; |
| } |
| parent = parent.getInheritancePolicy().getParentDescriptor(); |
| } |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Allow the inheritance properties of the descriptor to be initialized. |
| * The descriptor's parent must first be initialized. |
| */ |
| public void preInitialize(AbstractSession session) throws DescriptorException { |
| // Make sure that parent is already preinitialized. |
| if (isChildDescriptor()) { |
| updateTables(); |
| |
| // Clone the multitenant policy and set on child descriptor. |
| if (getParentDescriptor().hasMultitenantPolicy()) { |
| MultitenantPolicy clonedMultitenantPolicy = getParentDescriptor().getMultitenantPolicy().clone(getDescriptor()); |
| getDescriptor().setMultitenantPolicy(clonedMultitenantPolicy); |
| } |
| |
| setClassIndicatorMapping(getParentDescriptor().getInheritancePolicy().getClassIndicatorMapping()); |
| setShouldUseClassNameAsIndicator(getParentDescriptor().getInheritancePolicy().shouldUseClassNameAsIndicator()); |
| |
| // Initialize properties. |
| getDescriptor().setPrimaryKeyFields(getParentDescriptor().getPrimaryKeyFields()); |
| getDescriptor().setAdditionalTablePrimaryKeyFields(Helper.concatenateMaps(getParentDescriptor().getAdditionalTablePrimaryKeyFields(), getDescriptor().getAdditionalTablePrimaryKeyFields())); |
| |
| Expression localExpression = getDescriptor().getQueryManager().getMultipleTableJoinExpression(); |
| Expression parentExpression = getParentDescriptor().getQueryManager().getMultipleTableJoinExpression(); |
| |
| if (localExpression != null) { |
| getDescriptor().getQueryManager().setInternalMultipleTableJoinExpression(localExpression.and(parentExpression)); |
| } else if (parentExpression != null) { |
| getDescriptor().getQueryManager().setInternalMultipleTableJoinExpression(parentExpression); |
| } |
| |
| Expression localAdditionalExpression = getDescriptor().getQueryManager().getAdditionalJoinExpression(); |
| Expression parentAdditionalExpression = getParentDescriptor().getQueryManager().getAdditionalJoinExpression(); |
| |
| if (localAdditionalExpression != null) { |
| getDescriptor().getQueryManager().setAdditionalJoinExpression(localAdditionalExpression.and(parentAdditionalExpression)); |
| } else if (parentAdditionalExpression != null) { |
| getDescriptor().getQueryManager().setAdditionalJoinExpression(parentAdditionalExpression); |
| } |
| |
| setClassIndicatorField(getParentDescriptor().getInheritancePolicy().getClassIndicatorField()); |
| |
| //if child has sequencing setting, do not bother to call the parent |
| if (!getDescriptor().usesSequenceNumbers()) { |
| getDescriptor().setSequenceNumberField(getParentDescriptor().getSequenceNumberField()); |
| getDescriptor().setSequenceNumberName(getParentDescriptor().getSequenceNumberName()); |
| } |
| } else { |
| // This must be done now before any other initialization occurs. |
| getDescriptor().setInternalDefaultTable(); |
| } |
| |
| initializeClassExtractor(session); |
| |
| if (!isChildDescriptor()) { |
| // build abstract class indicator field. |
| if ((getClassIndicatorField() == null) && (!hasClassExtractor())) { |
| session.getIntegrityChecker().handleError(DescriptorException.classIndicatorFieldNotFound(getDescriptor(), getDescriptor())); |
| } |
| if (getClassIndicatorField() != null) { |
| setClassIndicatorField(getDescriptor().buildField(getClassIndicatorField())); |
| // Determine and set the class indicator classification. |
| if (shouldUseClassNameAsIndicator()) { |
| getClassIndicatorField().setType(ClassConstants.STRING); |
| } else if (!getClassIndicatorMapping().isEmpty()) { |
| Class type = null; |
| Iterator fieldValuesEnum = getClassIndicatorMapping().values().iterator(); |
| while (fieldValuesEnum.hasNext() && (type == null)) { |
| Object value = fieldValuesEnum.next(); |
| if (value.getClass() != getClass().getClass()) { |
| type = value.getClass(); |
| } |
| } |
| getClassIndicatorField().setType(type); |
| } |
| getDescriptor().getFields().addElement(getClassIndicatorField()); |
| } |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor to read instance of itself and its subclasses when queried. |
| * This is used with inheritance to configure the result of queries. |
| * By default this is true for root inheritance descriptors, and false for all others. |
| */ |
| public void readSubclassesOnQueries() { |
| setShouldReadSubclasses(true); |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to initialize a remote descriptor. |
| */ |
| public void remoteInitialization(DistributedSession session) { |
| if (isChildDescriptor()) { |
| if (session.hasCorrespondingDescriptor(getParentDescriptor())) { |
| setParentDescriptor(session.getDescriptor(getParentClass())); |
| } else { |
| session.privilegedAddDescriptor(getParentDescriptor()); |
| getParentDescriptor().remoteInitialization(session); |
| } |
| } |
| |
| Vector tempChildren = new Vector(getChildDescriptors().size()); |
| for (ClassDescriptor childDescriptor : getChildDescriptors()) { |
| if (session.hasCorrespondingDescriptor(childDescriptor)) { |
| tempChildren.addElement(session.getDescriptor(childDescriptor.getJavaClass())); |
| } else { |
| session.privilegedAddDescriptor(childDescriptor); |
| childDescriptor.remoteInitialization(session); |
| tempChildren.addElement(childDescriptor); |
| } |
| } |
| setChildDescriptors(tempChildren); |
| } |
| |
| /** |
| * INTERNAL: |
| * Return if this descriptor has children that define additional tables and needs to read them. |
| * This case requires a special read, because the query cannot be done through a single SQL call with normal joins. |
| */ |
| public boolean requiresMultipleTableSubclassRead() { |
| return hasMultipleTableChild() && shouldReadSubclasses(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Select all rows from a abstract table descriptor. |
| * This is accomplished by selecting for all of the concrete classes and then merging the rows. |
| * This does not optimize using type select, as the type information is not known. |
| * @return vector containing database rows. |
| * @exception DatabaseException - an error has occurred on the database. |
| */ |
| protected Vector selectAllRowUsingCustomMultipleTableSubclassRead(ObjectLevelReadQuery query) throws DatabaseException { |
| Vector rows = new Vector(); |
| // CR#3701077, it must either have a filter only instances expression, or not have subclasses. |
| // This method recurses, so even though this is only called when shouldReadSubclasses is true, it may be false for subclasses. |
| if ((getOnlyInstancesExpression() != null) || (! shouldReadSubclasses())) { |
| ObjectLevelReadQuery concreteQuery = (ObjectLevelReadQuery)query.clone(); |
| concreteQuery.setReferenceClass(getDescriptor().getJavaClass()); |
| concreteQuery.setDescriptor(getDescriptor()); |
| |
| Vector concreteRows = ((ExpressionQueryMechanism)concreteQuery.getQueryMechanism()).selectAllRowsFromConcreteTable(); |
| rows = Helper.concatenateVectors(rows, concreteRows); |
| } |
| |
| // Recursively collect all rows from all concrete children and their children. |
| // If this descriptor did not have a child with its own table, then the concrete select |
| // would have selected them all. |
| if (hasMultipleTableChild() || !shouldReadSubclasses()) { |
| for (ClassDescriptor concreteDescriptor : getChildDescriptors()) { |
| Vector concreteRows = concreteDescriptor.getInheritancePolicy().selectAllRowUsingCustomMultipleTableSubclassRead(query); |
| rows = Helper.concatenateVectors(rows, concreteRows); |
| } |
| } |
| |
| return rows; |
| } |
| |
| /** |
| * INTERNAL: |
| * Select all rows from a abstract table descriptor. |
| * This is accomplished by selecting for all of the concrete classes and then merging the rows. |
| * @return vector containing database rows. |
| * @exception DatabaseException - an error has occurred on the database. |
| */ |
| protected Vector selectAllRowUsingDefaultMultipleTableSubclassRead(ObjectLevelReadQuery query) throws DatabaseException, QueryException { |
| // Get all rows for the given class indicator field |
| // The indicator select is prepared in the original query, so can just be executed. |
| List<AbstractRecord> classIndicators = ((ExpressionQueryMechanism)query.getQueryMechanism()).selectAllRowsFromTable(); |
| |
| List<Class> classes = new ArrayList<>(); |
| Set<Class> uniqueClasses = new HashSet<>(); |
| for (AbstractRecord row : classIndicators) { |
| Class concreteClass = classFromRow(row, query.getSession()); |
| if (!uniqueClasses.contains(concreteClass)) { // Ensure unique (a distinct is used, but may have been disabled) |
| uniqueClasses.add(concreteClass); |
| classes.add(concreteClass); |
| } |
| } |
| |
| Vector rows = new Vector(); |
| // joinedMappingIndexes contains Integer indexes corresponding to the number of fields |
| // to which the query reference class is mapped, for instance: |
| // referenceClass = SmallProject => joinedMappingIndexes(0) = 6; |
| // referenceClass = LargeProject => joinedMappingIndexes(0) = 8; |
| // This information should be preserved in the main query against the parent class, |
| // therefore in this case joinedMappedIndexes contains a Map of classes to Integers: |
| // referenceClass = Project => joinedMappingIndexes(0) = Map {SmallProject -> 6; LargeProject -> 8}. |
| // These maps are populated in the loop below, and set into the main query joinedMappingIndexes. |
| HashMap joinedMappingIndexes = null; |
| if (query.hasJoining()) { |
| joinedMappingIndexes = new HashMap(); |
| } |
| ClassDescriptor rootDescriptor = query.getDescriptor(); |
| for (Class concreteClass : classes) { |
| if (!uniqueClasses.contains(concreteClass)) { |
| continue; |
| } |
| Set<Class> subclasses = new HashSet<>(); |
| uniqueClasses.remove(concreteClass); |
| subclasses.add(concreteClass); |
| ClassDescriptor concreteDescriptor = getDescriptor(concreteClass); |
| if (concreteDescriptor == null) { |
| throw QueryException.noDescriptorForClassFromInheritancePolicy(query, concreteClass); |
| } |
| InheritancePolicy concretePolicy = concreteDescriptor.getInheritancePolicy(); |
| ClassDescriptor parentDescriptor = concretePolicy.getParentDescriptor(); |
| // Find the root most parent with its own table. |
| while ((parentDescriptor != null) && (parentDescriptor != rootDescriptor) |
| && !parentDescriptor.getInheritancePolicy().hasMultipleTableChild() && parentDescriptor.getInheritancePolicy().shouldReadSubclasses()) { |
| concreteDescriptor = parentDescriptor; |
| concreteClass = concreteDescriptor.getJavaClass(); |
| uniqueClasses.remove(concreteClass); |
| subclasses.add(concreteClass); |
| concretePolicy = concreteDescriptor.getInheritancePolicy(); |
| parentDescriptor = concretePolicy.getParentDescriptor(); |
| } |
| // If this class has children select them all. |
| if (concretePolicy.hasChildren() && !concretePolicy.hasMultipleTableChild()) { |
| removeChildren(concreteDescriptor, uniqueClasses, subclasses); |
| } |
| ObjectLevelReadQuery concreteQuery = (ObjectLevelReadQuery)query.clone(); |
| concreteQuery.setReferenceClass(concreteClass); |
| concreteQuery.setDescriptor(concreteDescriptor); |
| Vector concreteRows = ((ExpressionQueryMechanism)concreteQuery.getQueryMechanism()).selectAllRowsFromConcreteTable(); |
| rows = Helper.concatenateVectors(rows, concreteRows); |
| |
| if (joinedMappingIndexes != null) { |
| // Need to set mapping index for each select, as each row size is different. |
| for (Map.Entry entry : concreteQuery.getJoinedAttributeManager().getJoinedMappingIndexes_().entrySet()) { |
| HashMap mappingIndexes = (HashMap)joinedMappingIndexes.get(entry.getKey()); |
| if (mappingIndexes == null) { |
| mappingIndexes = new HashMap(classes.size()); |
| joinedMappingIndexes.put(entry.getKey(), mappingIndexes); |
| } |
| for (Class subclass : subclasses) { |
| mappingIndexes.put(subclass, entry.getValue()); |
| } |
| } |
| } |
| } |
| if (joinedMappingIndexes != null) { |
| query.getJoinedAttributeManager().setJoinedMappingIndexes_(joinedMappingIndexes); |
| } |
| |
| return rows; |
| } |
| |
| /** |
| * Remove all of the subclasses (and so on) from the set of classes. |
| */ |
| protected void removeChildren(ClassDescriptor descriptor, Set<Class> classes, Set<Class> subclasses) { |
| for (ClassDescriptor childDescriptor : descriptor.getInheritancePolicy().getChildDescriptors()) { |
| classes.remove(childDescriptor.getJavaClass()); |
| subclasses.add(childDescriptor.getJavaClass()); |
| removeChildren(childDescriptor, classes, subclasses); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Select all rows from a abstract table descriptor. |
| * This is accomplished by selecting for all of the concrete classes and then merging the rows. |
| * @return vector containing database rows. |
| * @exception DatabaseException - an error has occurred on the database. |
| */ |
| public Vector selectAllRowUsingMultipleTableSubclassRead(ObjectLevelReadQuery query) throws DatabaseException { |
| if (hasClassExtractor()) { |
| return selectAllRowUsingCustomMultipleTableSubclassRead(query); |
| } else { |
| return selectAllRowUsingDefaultMultipleTableSubclassRead(query); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Select one rows from a abstract table descriptor. |
| * This is accomplished by selecting for all of the concrete classes until a row is found. |
| * This does not optimize using type select, as the type information is not known. |
| * @exception DatabaseException - an error has occurred on the database. |
| */ |
| protected AbstractRecord selectOneRowUsingCustomMultipleTableSubclassRead(ReadObjectQuery query) throws DatabaseException { |
| // CR#3701077, it must either have a filter only instances expression, or not have subclasses. |
| // This method recurses, so even though this is only called when shouldReadSubclasses is true, it may be false for subclasses. |
| if ((getOnlyInstancesExpression() != null) || (! shouldReadSubclasses())) { |
| ReadObjectQuery concreteQuery = (ReadObjectQuery)query.clone(); |
| concreteQuery.setReferenceClass(getDescriptor().getJavaClass()); |
| concreteQuery.setDescriptor(getDescriptor()); |
| |
| AbstractRecord row = ((ExpressionQueryMechanism)concreteQuery.getQueryMechanism()).selectOneRowFromConcreteTable(); |
| |
| if (row != null) { |
| return row; |
| } |
| } |
| |
| // Recursively collect all rows from all concrete children and their children. |
| for (ClassDescriptor concreteDescriptor : getChildDescriptors()) { |
| AbstractRecord row = concreteDescriptor.getInheritancePolicy().selectOneRowUsingCustomMultipleTableSubclassRead(query); |
| if (row != null) { |
| return row; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * INTERNAL: |
| * Select one row of any concrete subclass, |
| * This must use two selects, the first retrieves the type field only. |
| */ |
| protected AbstractRecord selectOneRowUsingDefaultMultipleTableSubclassRead(ReadObjectQuery query) throws DatabaseException, QueryException { |
| // Get the row for the given class indicator field |
| // The indicator select is prepared in the original query, so can just be executed. |
| AbstractRecord typeRow = ((ExpressionQueryMechanism)query.getQueryMechanism()).selectOneRowFromTable(); |
| |
| if (typeRow == null) { |
| return null; |
| } |
| |
| Class concreteClass = classFromRow(typeRow, query.getSession()); |
| ClassDescriptor concreteDescriptor = getDescriptor(concreteClass); |
| if (concreteDescriptor == null) { |
| throw QueryException.noDescriptorForClassFromInheritancePolicy(query, concreteClass); |
| } |
| |
| ReadObjectQuery concreteQuery = (ReadObjectQuery)query.clone(); |
| concreteQuery.setReferenceClass(concreteClass); |
| concreteQuery.setDescriptor(concreteDescriptor); |
| |
| AbstractRecord resultRow = ((ExpressionQueryMechanism)concreteQuery.getQueryMechanism()).selectOneRowFromConcreteTable(); |
| |
| return resultRow; |
| } |
| |
| /** |
| * INTERNAL: |
| * Select one row of any concrete subclass, |
| * This must use two selects, the first retrieves the type field only. |
| */ |
| public AbstractRecord selectOneRowUsingMultipleTableSubclassRead(ReadObjectQuery query) throws DatabaseException, QueryException { |
| if (hasClassExtractor()) { |
| return selectOneRowUsingCustomMultipleTableSubclassRead(query); |
| } else { |
| return selectOneRowUsingDefaultMultipleTableSubclassRead(query); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| protected void setAllChildClassIndicators(Vector allChildClassIndicators) { |
| this.allChildClassIndicators = allChildClassIndicators; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setChildDescriptors(List<ClassDescriptor> childDescriptors) { |
| this.childDescriptors = childDescriptors; |
| } |
| |
| /** |
| * ADVANCED: |
| * A class extraction method can be registered with the descriptor to override the default inheritance mechanism. |
| * This allows for a user defined class indicator in place of providing an explicit class indicator field. |
| * The method registered must be a static method on the class which has that descriptor. The method must take Record |
| * as an argument (for example, a DatabaseRecord), and must return the class to use for that record. |
| * This method will be used to decide which class to instantiate when reading from the database. |
| * It is the application's responsibility to populate any typing information in the database required |
| * to determine the class from the record. |
| * If this method is used then the class indicator field and mapping cannot be used, and in addition, |
| * the descriptor's withAllSubclasses and onlyInstances expressions must also be set up correctly. |
| * |
| * @see #setWithAllSubclassesExpression(Expression) |
| * @see #setOnlyInstancesExpression(Expression) |
| */ |
| public void setClassExtractionMethodName(String staticClassClassExtractionMethod) { |
| if ((staticClassClassExtractionMethod == null) || (staticClassClassExtractionMethod.length() == 0)) { |
| return; |
| } |
| if (!(getClassExtractor() instanceof MethodClassExtractor)) { |
| setClassExtractor(new MethodClassExtractor()); |
| } |
| ((MethodClassExtractor)getClassExtractor()).setClassExtractionMethodName(staticClassClassExtractionMethod); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the class indicator associations from reading the deployment XML. |
| */ |
| public void setClassIndicatorAssociations(Vector classIndicatorAssociations) { |
| setClassNameIndicatorMapping(new HashMap(classIndicatorAssociations.size() + 1)); |
| setClassIndicatorMapping(new HashMap((classIndicatorAssociations.size() * 2) + 1)); |
| for (Iterator iterator = classIndicatorAssociations.iterator(); iterator.hasNext();) { |
| Association association = (Association)iterator.next(); |
| Object key = association.getKey(); |
| // Allow for 904 format which stored class name, may not use correct class loader. |
| if (key instanceof String) { |
| key = ConversionManager.getDefaultManager().convertClassNameToClass((String)key); |
| |
| |
| |
| } |
| addClassIndicator((Class)key, association.getValue()); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * To set the class indicator field. |
| * This can be used for advanced field types, such as XML nodes, or to set the field type. |
| */ |
| @Override |
| public void setClassIndicatorField(DatabaseField classIndicatorField) { |
| this.classIndicatorField = classIndicatorField; |
| } |
| |
| /** |
| * PUBLIC: |
| * To set the class indicator field name. |
| * This is the name of the field in the table that stores what type of object this is. |
| */ |
| public void setClassIndicatorFieldName(String fieldName) { |
| if (fieldName == null) { |
| setClassIndicatorField(null); |
| } else { |
| setClassIndicatorField(new DatabaseField(fieldName)); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the association of indicators and classes. |
| * This may be desired to be used by clients in strange inheritance models. |
| */ |
| @Override |
| public void setClassIndicatorMapping(Map classIndicatorMapping) { |
| this.classIndicatorMapping = classIndicatorMapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the class name indicator mapping, used by the MW. |
| */ |
| public void setClassNameIndicatorMapping(Map classNameIndicatorMapping) { |
| this.classNameIndicatorMapping = classNameIndicatorMapping; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the descriptor. |
| */ |
| @Override |
| public void setDescriptor(ClassDescriptor descriptor) { |
| this.descriptor = descriptor; |
| } |
| |
| /** |
| * ADVANCED: |
| * Determines whether the descriptors using this inheritance policy |
| * should be used as descriptors for subclasses of the classes they |
| * describe if those subclasses do not have their own descriptor |
| * |
| * e.g. If Employee.class has a descriptor and EmployeeSubClass does |
| * not have a descriptor, if describesNonPersistenceSubclasses is true |
| * Employee's descriptor will be used as the descriptor for Employee |
| * |
| */ |
| public void setDescribesNonPersistentSubclasses(boolean describesNonPersistentSubclasses){ |
| this.describesNonPersistentSubclasses = describesNonPersistentSubclasses; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to indicate a JOINED inheritance strategy. |
| * |
| */ |
| public void setJoinedStrategy() { |
| isJoinedStrategy = true; |
| } |
| |
| /** |
| * ADVANCED: |
| * Sets the expression used to select instance of the class only. Can be used to customize the |
| * inheritance class indicator expression. |
| */ |
| public void setOnlyInstancesExpression(Expression onlyInstancesExpression) { |
| this.onlyInstancesExpression = onlyInstancesExpression; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the parent class. |
| * A descriptor can inherit from another descriptor through defining it as its parent. |
| * The root descriptor must define a class indicator field and mapping. |
| * All children must share the same table as their parent but can add additional tables. |
| * All children must share the root descriptor primary key. |
| */ |
| public void setParentClass(Class parentClass) { |
| this.parentClass = parentClass; |
| if (parentClass != null) { |
| setParentClassName(parentClass.getName()); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the parent class name, used by MW to avoid referencing the real class for |
| * deployment XML generation. |
| */ |
| @Override |
| public void setParentClassName(String parentClassName) { |
| this.parentClassName = parentClassName; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| public void setParentDescriptor(ClassDescriptor parentDescriptor) { |
| this.parentDescriptor = parentDescriptor; |
| } |
| |
| /** |
| * INTERNAL: |
| * The view can be used to optimize/customize the query for all subclasses where they have multiple tables. |
| * This view can do the outer join, we require the view because we cannot generate dynamic platform independent SQL |
| * for outer joins (i.e. not possible to do so either). |
| */ |
| protected void setReadAllSubclassesView(DatabaseTable readAllSubclassesView) { |
| this.readAllSubclassesView = readAllSubclassesView; |
| } |
| |
| /** |
| * ADVANCED: |
| * The view can be used to optimize/customize the query for all subclasses where they have multiple tables. |
| * This view can use outer joins or unions to combine the results of selecting from all of the subclass tables. |
| * If a view is not given then TopLink must make an individual call for each subclass. |
| */ |
| public void setReadAllSubclassesViewName(String readAllSubclassesViewName) { |
| if (readAllSubclassesViewName == null) { |
| setReadAllSubclassesView(null); |
| } else { |
| setReadAllSubclassesView(new DatabaseTable(readAllSubclassesViewName)); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the descriptor to read instance of itself and its subclasses when queried. |
| * This is used with inheritance to configure the result of queries. |
| * By default this is true for root inheritance descriptors, and false for all others. |
| */ |
| @Override |
| public void setShouldReadSubclasses(Boolean shouldReadSubclasses) { |
| this.shouldReadSubclasses = shouldReadSubclasses; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor to read instance of itself and its subclasses when queried. |
| * This is used with inheritance to configure the result of queries. |
| * By default this is true for root inheritance descriptors, and false for all others. |
| */ |
| public void setShouldReadSubclasses(boolean shouldReadSubclasses) { |
| this.shouldReadSubclasses = shouldReadSubclasses; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if the descriptor uses the classes fully qualified name as the indicator. |
| * The class indicator is used with inheritance to determine the class from a row. |
| * By default a class indicator mapping is required, this can be set to true if usage of the class |
| * name is desired. |
| * The field must be of a large enough size to store the fully qualified class name. |
| */ |
| public void setShouldUseClassNameAsIndicator(boolean shouldUseClassNameAsIndicator) { |
| this.shouldUseClassNameAsIndicator = shouldUseClassNameAsIndicator; |
| } |
| |
| /** |
| * PUBLIC: |
| * Sets the inheritance policy to always use an outer join when querying across a relationship of class. |
| * used when using getAllowingNull(), or anyOfAllowingNone() |
| */ |
| |
| // cr3546 |
| public void setAlwaysUseOuterJoinForClassType(boolean choice) { |
| this.shouldAlwaysUseOuterJoin = choice; |
| } |
| |
| /** |
| * INTERNAL: |
| * Used to indicate a SINGLE_TABLE inheritance strategy. Since only JOINED and SINGLE_TABLE |
| * strategies are supported at this time (no support for TABLE_PER_CLASS) using a |
| * !isJoinedStrategy an an indicator for SINGLE_TABLE is sufficient. |
| * |
| */ |
| public void setSingleTableStrategy() { |
| isJoinedStrategy = false; |
| } |
| |
| /** |
| * INTERNAL: |
| * Sets if we should use the descriptor inheritance to determine |
| * if an object can be returned from the identity map or not. |
| */ |
| public void setUseDescriptorsToValidateInheritedObjects(boolean useDescriptorsToValidateInheritedObjects) { |
| //CR 4005 |
| this.useDescriptorsToValidateInheritedObjects = useDescriptorsToValidateInheritedObjects; |
| } |
| |
| /** |
| * ADVANCED: |
| * Sets the expression to be used for querying for a class and all its subclasses. Can be used |
| * to customize the inheritance class indicator expression. |
| */ |
| public void setWithAllSubclassesExpression(Expression withAllSubclassesExpression) { |
| this.withAllSubclassesExpression = withAllSubclassesExpression; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if this descriptor should read instances of itself and subclasses on queries. |
| */ |
| public boolean shouldReadSubclasses() { |
| if (shouldReadSubclasses == null) { |
| return true; |
| } |
| return shouldReadSubclasses; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return true if this descriptor should read instances of itself and subclasses on queries. |
| */ |
| public Boolean shouldReadSubclassesValue() { |
| return shouldReadSubclasses; |
| } |
| |
| /** |
| * PUBLIC: |
| * returns if the inheritance policy will always use an outerjoin when selecting class type |
| */ |
| |
| // cr3546 |
| public boolean shouldAlwaysUseOuterJoin() { |
| return this.shouldAlwaysUseOuterJoin; |
| } |
| |
| |
| /** |
| * PUBLIC: |
| * Return if an outer join should be used to read subclasses. |
| * By default a separate query is done for each subclass when querying for |
| * a root or branch inheritance class that has subclasses that span multiple tables. |
| */ |
| public boolean shouldOuterJoinSubclasses() { |
| return shouldOuterJoinSubclasses; |
| } |
| |
| /** |
| * PUBLIC: |
| * Set if an outer join should be used to read subclasses. |
| * By default a separate query is done for each subclass when querying for |
| * a root or branch inheritance class that has subclasses that span multiple tables. |
| */ |
| public void setShouldOuterJoinSubclasses(boolean shouldOuterJoinSubclasses) { |
| this.shouldOuterJoinSubclasses = shouldOuterJoinSubclasses; |
| } |
| |
| /** |
| * PUBLIC: |
| * Return true if the descriptor use the classes full name as the indicator. |
| * The class indicator is used with inheritance to determine the class from a row. |
| * By default a class indicator mapping is required, this can be set to true if usage of the class |
| * name is desired. |
| * The field must be of a large enough size to store the fully qualified class name. |
| */ |
| public boolean shouldUseClassNameAsIndicator() { |
| return shouldUseClassNameAsIndicator; |
| } |
| |
| /** |
| * INTERNAL: |
| */ |
| @Override |
| public String toString() { |
| return Helper.getShortClassName(getClass()) + "(" + getDescriptor() + ")"; |
| } |
| |
| /** |
| * INTERNAL: |
| * set the tables on the child descriptor |
| * overridden in org.eclipse.persistence.internal.oxm.QNameInheritancePolicy |
| */ |
| protected void updateTables(){ |
| // Unique is required because the builder can add the same table many times. |
| Vector<DatabaseTable> childTables = getDescriptor().getTables(); |
| Vector<DatabaseTable> parentTables = getParentDescriptor().getTables(); |
| Vector<DatabaseTable> uniqueTables = Helper.concatenateUniqueVectors(parentTables, childTables); |
| getDescriptor().setTables(uniqueTables); |
| |
| // After filtering out any duplicate tables, set the default table |
| // if one is not already set. This must be done now before any other |
| // initialization occurs. In a joined strategy case, the default |
| // table will be at an index greater than 0. Which is where |
| // setDefaultTable() assumes it is. Therefore, we need to send the |
| // actual default table instead. |
| if (childTables.isEmpty()) { |
| getDescriptor().setInternalDefaultTable(); |
| } else { |
| getDescriptor().setInternalDefaultTable(uniqueTables.get(uniqueTables.indexOf(childTables.get(0)))); |
| } |
| } |
| |
| |
| /** |
| * PUBLIC: |
| * Set the descriptor to use the classes full name as the indicator. |
| * The class indicator is used with inheritance to determine the class from a row. |
| * By default a class indicator mapping is required, this can be set to true if usage of the class |
| * name is desired. |
| * The field must be of a large enough size to store the fully qualified class name. |
| */ |
| public void useClassNameAsIndicator() { |
| setShouldUseClassNameAsIndicator(true); |
| } |
| } |