All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.store.rdbms.table.ClassTable Maven / Gradle / Ivy

There is a newer version: 6.0.8
Show newest version
/**********************************************************************
Copyright (c) 2002 Kelly Grizzle (TJDO) and others. All rights reserved. 
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
2003 Andy Jefferson - added localiser
2003 Andy Jefferson - replaced TableMetadata with identifier.
2003 Erik Bengtson - refactored the datastore identity together with the SQLIdentifier class
2003 Erik Bengtson - added OptimisticMapping
2003 Andy Jefferson - coding standards
2004 Andy Jefferson - merged with JDOBaseTable
2004 Andy Jefferson - changed to store map of fieldMappings keyed by absolute field num. Added start point for inheritance strategy handling
2004 Andy Jefferson - changed consumer to set highest field number based on actual highest (to allow for inheritance strategies)
2004 Andy Jefferson - added DiscriminatorMapping
2004 Andy Jefferson - enabled use of Long/String datastore identity column
2004 Andy Jefferson - added capability to handle 1-N inverse unidirectional FKs
2004 Andy Jefferson - removed the majority of the value-strategy code - done elsewhere
    ...
 **********************************************************************/
package org.datanucleus.store.rdbms.table;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.PropertyNames;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.ColumnMetaDataContainer;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.ExtensionMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.ForeignKeyAction;
import org.datanucleus.metadata.ForeignKeyMetaData;
import org.datanucleus.metadata.IdentityStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.IndexMetaData;
import org.datanucleus.metadata.InheritanceStrategy;
import org.datanucleus.metadata.InterfaceMetaData;
import org.datanucleus.metadata.JoinMetaData;
import org.datanucleus.metadata.MetaData;
import org.datanucleus.metadata.OrderMetaData;
import org.datanucleus.metadata.PrimaryKeyMetaData;
import org.datanucleus.metadata.PropertyMetaData;
import org.datanucleus.metadata.RelationType;
import org.datanucleus.metadata.UniqueMetaData;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.metadata.VersionStrategy;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.rdbms.adapter.DatastoreAdapter;
import org.datanucleus.store.rdbms.exceptions.ClassDefinitionException;
import org.datanucleus.store.rdbms.exceptions.DuplicateColumnException;
import org.datanucleus.store.rdbms.exceptions.NoSuchPersistentFieldException;
import org.datanucleus.store.rdbms.exceptions.NoTableManagedException;
import org.datanucleus.store.rdbms.JDBCUtils;
import org.datanucleus.store.rdbms.RDBMSPropertyNames;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.identifier.DatastoreIdentifier;
import org.datanucleus.store.rdbms.identifier.IdentifierFactory;
import org.datanucleus.store.rdbms.identifier.IdentifierType;
import org.datanucleus.store.rdbms.key.CandidateKey;
import org.datanucleus.store.rdbms.key.ForeignKey;
import org.datanucleus.store.rdbms.key.Index;
import org.datanucleus.store.rdbms.key.PrimaryKey;
import org.datanucleus.store.rdbms.mapping.CorrespondentColumnsMapper;
import org.datanucleus.store.rdbms.mapping.MappingConsumer;
import org.datanucleus.store.rdbms.mapping.MappingManager;
import org.datanucleus.store.rdbms.mapping.datastore.DatastoreMapping;
import org.datanucleus.store.rdbms.mapping.java.DiscriminatorMapping;
import org.datanucleus.store.rdbms.mapping.java.EmbeddedPCMapping;
import org.datanucleus.store.rdbms.mapping.java.IndexMapping;
import org.datanucleus.store.rdbms.mapping.java.IntegerMapping;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.mapping.java.LongMapping;
import org.datanucleus.store.rdbms.mapping.java.PersistableMapping;
import org.datanucleus.store.rdbms.mapping.java.ReferenceMapping;
import org.datanucleus.store.rdbms.mapping.java.SerialisedMapping;
import org.datanucleus.store.rdbms.mapping.java.VersionLongMapping;
import org.datanucleus.store.rdbms.mapping.java.VersionTimestampMapping;
import org.datanucleus.store.rdbms.schema.SQLTypeInfo;
import org.datanucleus.store.types.SCOUtils;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.MacroString;
import org.datanucleus.util.NucleusLogger;

/**
 * Table representing a Java class (or classes) as a first class object (FCO).
 * Uses the inheritance strategy to control whether this represents multiple classes
 * or just the one class.
 * 

Mappings

* This class adds some additional mappings over what the superclass provides. Here we add *
    *
  • externalFkMappings - any mappings for Collections that have no associated * field in this class, providing the foreign key column(s)
  • *
  • externalOrderMappings - mappings for any ordering column used by Lists for ordering * elements of this class
  • *
  • externalFkDiscriminatorMappings - mappings for any discriminator column used when sharing * external foreign keys to distinguish the element owner field
  • *
*

Classes

* A table can represent multiple classes. It has a nominal owner which is the class * that has an inheritance strategy of "new-table". All classes that utilise this table * have their MetaData stored in this object. *

Secondary Tables

* This class represents a "primary" table. That is, the main table where objects of a * class are persisted. It can have several "secondary" tables where some of the classes * fields are stored at persistence. */ public class ClassTable extends AbstractClassTable implements DatastoreClass { /** * MetaData for the principal class being stored here. * In inheritance situations where multiple classes share the same table, this will * be the class which uses "new-table" strategy. */ private final ClassMetaData cmd; /** MetaData for all classes being managed here. */ private final Collection managedClassMetaData = new HashSet(); /** Callbacks that have been applied keyed by the managed class. */ private final Map> callbacksAppliedForManagedClass = new HashMap>(); /** Table above this table storing superclass information (if any). */ private ClassTable supertable; /** Secondary tables for this table (if any). */ private Map secondaryTables; /** * Mappings for FK Collections/Lists not managed by this class (1-N unidirectional). * Keyed by the metadata of the owner field/property. */ private Map externalFkMappings; /** * Mappings for FK Collections/Lists relation discriminators not managed by this class (1-N unidirectional). * Keyed by the metadata of the owner field/property. */ private Map externalFkDiscriminatorMappings; /** * Mappings for FK Lists order columns not managed by this class (1-N unidirectional). * Keyed by the metadata of the owner field/property. */ private Map externalOrderMappings; /** User defined table schema **/ private MacroString tableDef; /** DDL statement for creating the table, if using user defined table schema. */ private String createStatementDDL; Map candidateKeysByMapField = new HashMap(); /** Set of unmapped "Column" objects that have no associated field (and hence RDBMSMapping). */ Set unmappedColumns = null; /** * Constructor. * @param tableName Table name SQL identifier * @param storeMgr Store Manager to manage this table * @param cmd MetaData for the class. */ public ClassTable(DatastoreIdentifier tableName, RDBMSStoreManager storeMgr, ClassMetaData cmd) { super(tableName, storeMgr); this.cmd = cmd; // Check if this is a valid class to map to its own table if (cmd.getInheritanceMetaData().getStrategy() != InheritanceStrategy.NEW_TABLE && cmd.getInheritanceMetaData().getStrategy() != InheritanceStrategy.COMPLETE_TABLE) { throw new NucleusUserException(LOCALISER.msg("057003", cmd.getFullClassName(), cmd.getInheritanceMetaData().getStrategy().toString())).setFatal(); } highestMemberNumber = cmd.getNoOfManagedMembers() + cmd.getNoOfInheritedManagedMembers(); // Extract the table definition from MetaData, if exists String tableImpStr = cmd.getValueForExtension("ddl-imports"); String tableDefStr = null; if (dba.getVendorID() != null) { tableDefStr = cmd.getValueForExtension("ddl-definition" + '-' + dba.getVendorID()); } if (tableDefStr == null) { tableDefStr = cmd.getValueForExtension("ddl-definition"); } if (tableDefStr != null) { tableDef = new MacroString(cmd.getFullClassName(), tableImpStr, tableDefStr); } } /** * Pre-initialize. * We require any supertable, and the PK to be ready before we start initialisation. * @param clr the ClassLoaderResolver */ public void preInitialize(final ClassLoaderResolver clr) { assertIsPKUninitialized(); if (cmd.getInheritanceMetaData().getStrategy() != InheritanceStrategy.COMPLETE_TABLE) { // Inheritance strategy may imply having a supertable, so identify it supertable = getSupertable(cmd, clr); if (supertable != null && !supertable.isInitialized() && !supertable.isPKInitialized()) { // Make sure that the supertable is preinitialised before we think about initialising here supertable.preInitialize(clr); } } // Initialise the PK field(s) if (!isPKInitialized()) { initializePK(clr); } } /** * Method to initialise the table. * This adds the columns based on the MetaData representation for the class being represented by this table. * @param clr The ClassLoaderResolver */ public void initialize(ClassLoaderResolver clr) { // if already initialized, we have nothing further to do here if (isInitialized()) { return; } // initialize any supertable first - this will ensure that any resources // we may inherit from that table are initialized at the point at which we may need them if (supertable != null) { supertable.initialize(clr); } // Add the fields for this class (and any other superclasses that we need to manage the // fields for (inheritance-strategy="subclass-table" in the superclass) initializeForClass(cmd, clr); MappingManager mapMgr = storeMgr.getMappingManager(); // Add Version where specified in MetaData // TODO If there is a superclass table that has a version we should omit from here even if in MetaData // See "getTableWithDiscriminator()" for the logic versionMetaData = cmd.getVersionMetaDataForTable(); if (versionMetaData != null && versionMetaData.getFieldName() == null) { if (versionMetaData.getVersionStrategy() == VersionStrategy.NONE) { // No optimistic locking but the idiot wants a column for that :-) versionMapping = new VersionLongMapping(this, mapMgr.getMapping(Long.class)); } else if (versionMetaData.getVersionStrategy() == VersionStrategy.VERSION_NUMBER) { versionMapping = new VersionLongMapping(this, mapMgr.getMapping(Long.class)); } else if (versionMetaData.getVersionStrategy() == VersionStrategy.DATE_TIME) { if (!dba.supportsOption(DatastoreAdapter.DATETIME_STORES_MILLISECS)) { // TODO Localise this throw new NucleusException("Class " + cmd.getFullClassName() + " is defined " + "to use date-time versioning, yet this datastore doesnt support storing " + "milliseconds in DATETIME/TIMESTAMP columns. Use version-number"); } versionMapping = new VersionTimestampMapping(this, mapMgr.getMapping(Timestamp.class)); } } // Add Discriminator where specified in MetaData DiscriminatorMetaData dismd = cmd.getDiscriminatorMetaDataForTable(); if (dismd != null) { discriminatorMetaData = dismd; if (storeMgr.getBooleanProperty(RDBMSPropertyNames.PROPERTY_RDBMS_DISCRIM_PER_SUBCLASS_TABLE)) { // Backwards compatibility only. Creates discriminator in all subclass tables even though not needed // TODO Remove this in the future discriminatorMapping = DiscriminatorMapping.createDiscriminatorMapping(this, dismd); } else { // Create discriminator column only in top most table that needs it ClassTable tableWithDiscrim = getTableWithDiscriminator(); if (tableWithDiscrim == this) { // No superclass with a discriminator so add it in this table discriminatorMapping = DiscriminatorMapping.createDiscriminatorMapping(this, dismd); } } } // Add Multi-tenancy discriminator if applicable // TODO Only put on root table (i.e "if (supertable != null)" then omit) if (storeMgr.getStringProperty(PropertyNames.PROPERTY_TENANT_ID) != null) { if ("true".equalsIgnoreCase(cmd.getValueForExtension("multitenancy-disable"))) { // Don't bother with multitenancy for this class } else { ColumnMetaData colmd = new ColumnMetaData(); if (cmd.hasExtension("multitenancy-column-name")) { colmd.setName(cmd.getValueForExtension("multitenancy-column-name")); } if (cmd.hasExtension("multitenancy-jdbc-type")) { colmd.setJdbcType(cmd.getValueForExtension("multitenancy-jdbc-type")); } if (cmd.hasExtension("multitenancy-column-length")) { colmd.setLength(cmd.getValueForExtension("multitenancy-column-length")); } addMultitenancyMapping(colmd); } } // Initialise any SecondaryTables if (secondaryTables != null) { Set secondaryTableNames = secondaryTables.keySet(); Iterator secondaryTableNamesIter = secondaryTableNames.iterator(); while (secondaryTableNamesIter.hasNext()) { String secondaryTableName = (String)secondaryTableNamesIter.next(); SecondaryTable second = secondaryTables.get(secondaryTableName); if (!second.isInitialized()) { second.initialize(clr); } } } if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(LOCALISER.msg("057023", this)); } storeMgr.registerTableInitialized(this); state = TABLE_STATE_INITIALIZED; } /** * Post initilize. For things that must be set after all classes have been initialized before * @param clr the ClassLoaderResolver */ public void postInitialize(final ClassLoaderResolver clr) { assertIsInitialized(); runCallBacks(clr); if (tableDef != null) { createStatementDDL = tableDef.substituteMacros(new MacroString.MacroHandler() { public void onIdentifierMacro(MacroString.IdentifierMacro im) { storeMgr.resolveIdentifierMacro(im, clr); } public void onParameterMacro(MacroString.ParameterMacro pm) { throw new NucleusUserException(LOCALISER.msg("057033", cmd.getFullClassName(), pm)); } }, clr ); } } /** Name of class currently being processed in manageClass (if any). */ protected transient String managingClassCurrent = null; /** Flag to run the callbacks after the current class is managed fully. */ protected boolean runCallbacksAfterManageClass = false; /** * Method that adds the specified class to be managed by this table. * Will provide mapping of all persistent fields to their underlying columns, map all necessary * identity fields, and manage all "unmapped" columns that have no associated field. * where the columns are defined for each mapping. * @param theCmd ClassMetaData for the class to be managed * @param clr The ClassLoaderResolver */ public void manageClass(AbstractClassMetaData theCmd, ClassLoaderResolver clr) { if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(LOCALISER.msg("057024", toString(), theCmd.getFullClassName(), theCmd.getInheritanceMetaData().getStrategy().toString())); } managingClassCurrent = theCmd.getFullClassName(); managedClassMetaData.add(theCmd); // Manage all fields of this class and all fields of superclasses that this is overriding manageMembers(theCmd, clr, theCmd.getManagedMembers()); manageMembers(theCmd, clr, theCmd.getOverriddenMembers()); // Manage all "unmapped" columns (that have no field) manageUnmappedColumns(theCmd, clr); managingClassCurrent = null; if (runCallbacksAfterManageClass) { // We need to run the callbacks now that this class is fully managed runCallBacks(clr); runCallbacksAfterManageClass = false; } } /** * Accessor for the names of all classes managed by this table. * @return Names of the classes managed (stored) here */ public String[] getManagedClasses() { String[] classNames = new String[managedClassMetaData.size()]; Iterator iter = managedClassMetaData.iterator(); int i = 0; while (iter.hasNext()) { classNames[i++] = (iter.next()).getFullClassName(); } return classNames; } /** * Goes through all specified members for the specified class and adds a mapping for each. * Ignores primary-key fields which are added elsewhere. * @param theCmd ClassMetaData for the class to be managed * @param clr The ClassLoaderResolver * @param mmds the fields/properties to manage */ private void manageMembers(AbstractClassMetaData theCmd, ClassLoaderResolver clr, AbstractMemberMetaData[] mmds) { // Go through the fields for this class and add columns for them for (int fieldNumber=0; fieldNumber 0) { // Apply this set of ColumnMetaData to the existing mapping int colnum = 0; IdentifierFactory idFactory = getStoreManager().getIdentifierFactory(); for (int i=0;icolumn mapping debug message StringBuilder columnsStr = new StringBuilder(); for (int i=0;i 0) { columnsStr.append(","); } columnsStr.append(fieldMapping.getDatastoreMapping(i).getColumn()); } if (fieldMapping.getNumberOfDatastoreMappings() == 0) { columnsStr.append("[none]"); } StringBuilder datastoreMappingTypes = new StringBuilder(); for (int i=0;i 0) { datastoreMappingTypes.append(','); } datastoreMappingTypes.append(fieldMapping.getDatastoreMapping(i).getClass().getName()); } NucleusLogger.DATASTORE_SCHEMA.debug(LOCALISER.msg("057010", mmd.getFullFieldName(), columnsStr.toString(), fieldMapping.getClass().getName(), datastoreMappingTypes.toString())); } } } } else { // Manage the field if not already managed (may already exist if overriding a superclass field) if (mmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) { boolean isPrimary = true; if (mmd.getTable() != null && mmd.getJoinMetaData() == null) { // Field has a table specified and is not a 1-N with join table // so is mapped to a secondary table isPrimary = false; } if (isPrimary) { // Add the field to this table JavaTypeMapping fieldMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_FIELD); if (theCmd != cmd && theCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUPERCLASS_TABLE && fieldMapping.getNumberOfDatastoreMappings() > 0) { // Field is for a subclass and so column(s) has to either allow nulls, or have default int numCols = fieldMapping.getNumberOfDatastoreMappings(); for (int colNum = 0;colNum < numCols; colNum++) { Column col = fieldMapping.getDatastoreMapping(colNum).getColumn(); if (col.getDefaultValue() == null && !col.isNullable()) { // Column needs to be nullable NucleusLogger.DATASTORE_SCHEMA.debug("Member " + mmd.getFullFieldName() + " uses superclass-table yet the field is not marked as nullable " + " nor does it have a default value, so setting the column as nullable"); col.setNullable(); } } } addMemberMapping(fieldMapping); } else { // Add the field to the appropriate secondary table if (secondaryTables == null) { secondaryTables = new HashMap(); } SecondaryTable secTable = secondaryTables.get(mmd.getTable()); if (secTable == null) { // Secondary table doesnt exist yet so create it to users specifications. JoinMetaData[] joinmds = theCmd.getJoinMetaData(); JoinMetaData joinmd = null; if (joinmds != null) { for (int j=0;j 0) { // Collection/array of interface type so apply callback to all implementation types AbstractClassMetaData[] elementCmds = new AbstractClassMetaData[implClassNames.length]; for (int i=0;i 0) { Iterator colsIter = cols.iterator(); while (colsIter.hasNext()) { ColumnMetaData colmd = (ColumnMetaData)colsIter.next(); // Create a column with the specified name and jdbc-type if (colmd.getJdbcType().equals("VARCHAR") && colmd.getLength() == null) { colmd.setLength(storeMgr.getIntProperty(RDBMSPropertyNames.PROPERTY_RDBMS_STRING_DEFAULT_LENGTH)); } IdentifierFactory idFactory = getStoreManager().getIdentifierFactory(); DatastoreIdentifier colIdentifier = idFactory.newIdentifier(IdentifierType.COLUMN, colmd.getName()); Column col = addColumn(null, colIdentifier, null, colmd); SQLTypeInfo sqlTypeInfo = storeMgr.getSQLTypeInfoForJDBCType( JDBCUtils.getJDBCTypeForName(colmd.getJdbcType())); col.setTypeInfo(sqlTypeInfo); if (unmappedColumns == null) { unmappedColumns = new HashSet(); } if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(LOCALISER.msg("057011", col.toString(), colmd.getJdbcType())); } unmappedColumns.add(col); } } } /** * Accessor for whether this table manages the specified class * @param className Name of the class * @return Whether it is managed by this table */ public boolean managesClass(String className) { if (className == null) { return false; } Iterator iter = managedClassMetaData.iterator(); while (iter.hasNext()) { AbstractClassMetaData managedCmd = iter.next(); if (managedCmd.getFullClassName().equals(className)) { return true; } } return false; } /** * Method to initialise the table primary key field(s). * @param clr The ClassLoaderResolver */ protected void initializePK(ClassLoaderResolver clr) { assertIsPKUninitialized(); AbstractMemberMetaData[] membersToAdd = new AbstractMemberMetaData[cmd.getNoOfPrimaryKeyMembers()]; // Initialise Primary Key mappings for application id with PK fields in this class int pkFieldNum=0; int fieldCount = cmd.getNoOfManagedMembers(); boolean hasPrimaryKeyInThisClass = false; if (cmd.getNoOfPrimaryKeyMembers() > 0) { pkMappings = new JavaTypeMapping[cmd.getNoOfPrimaryKeyMembers()]; if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) { // COMPLETE-TABLE so use root class metadata and add PK members // TODO Does this allow for overridden PK field info ? AbstractClassMetaData baseCmd = cmd.getBaseAbstractClassMetaData(); fieldCount = baseCmd.getNoOfManagedMembers(); for (int relFieldNum = 0; relFieldNum < fieldCount; ++relFieldNum) { AbstractMemberMetaData mmd = baseCmd.getMetaDataForManagedMemberAtRelativePosition(relFieldNum); if (mmd.isPrimaryKey()) { if (mmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) { membersToAdd[pkFieldNum++] = mmd; hasPrimaryKeyInThisClass = true; } else if (mmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) { throw new NucleusException(LOCALISER.msg("057006", mmd.getName())).setFatal(); } // Check if auto-increment and that it is supported by this RDBMS if ((mmd.getValueStrategy() == IdentityStrategy.IDENTITY) && !dba.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS)) { throw new NucleusException(LOCALISER.msg("057020", cmd.getFullClassName(), mmd.getName())).setFatal(); } } } } else { for (int relFieldNum=0; relFieldNum... colContainer = cmd.getInheritanceMetaData().getJoinMetaData(); } if (colContainer == null) { // Try via ... colContainer = cmd.getPrimaryKeyMetaData(); } addApplicationIdUsingClassTableId(colContainer, superTable, clr, cmd); } else { // No supertable to copy, so find superclass with PK fields and create new mappings and columns AbstractClassMetaData pkCmd = getClassWithPrimaryKeyForClass(cmd.getSuperAbstractClassMetaData(), clr); if (pkCmd != null) { pkMappings = new JavaTypeMapping[pkCmd.getNoOfPrimaryKeyMembers()]; pkFieldNum = 0; fieldCount = pkCmd.getNoOfInheritedManagedMembers() + pkCmd.getNoOfManagedMembers(); for (int absFieldNum = 0; absFieldNum < fieldCount; ++absFieldNum) { AbstractMemberMetaData fmd = pkCmd.getMetaDataForManagedMemberAtAbsolutePosition(absFieldNum); if (fmd.isPrimaryKey()) { AbstractMemberMetaData overriddenFmd = cmd.getOverriddenMember(fmd.getName()); if (overriddenFmd != null) { // PK field is overridden so use the overriding definition fmd = overriddenFmd; } if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT) { membersToAdd[pkFieldNum++] = fmd; } else if (fmd.getPersistenceModifier() != FieldPersistenceModifier.TRANSACTIONAL) { throw new NucleusException(LOCALISER.msg("057006",fmd.getName())).setFatal(); } } } } } } else if (cmd.getIdentityType() == IdentityType.DATASTORE) { // datastore-identity ColumnMetaData colmd = null; if (cmd.getIdentityMetaData() != null && cmd.getIdentityMetaData().getColumnMetaData() != null) { // Try via ... colmd = cmd.getIdentityMetaData().getColumnMetaData(); } if (colmd == null) { // Try via ... if (cmd.getPrimaryKeyMetaData() != null && cmd.getPrimaryKeyMetaData().getColumnMetaData() != null && cmd.getPrimaryKeyMetaData().getColumnMetaData().length > 0) { colmd = cmd.getPrimaryKeyMetaData().getColumnMetaData()[0]; } } addDatastoreId(colmd, null, cmd); } else if (cmd.getIdentityType() == IdentityType.NONDURABLE) { // Do nothing since no identity! } } //add field mappings in the end, so we compute all columns after the post initialize for (int i=0; i 0 && cmd.getSuperAbstractClassMetaData().getNoOfPrimaryKeyMembers() == 0) { // This class has PK members but the superclass doesn't, so return this return cmd; } return getClassWithPrimaryKeyForClass(cmd.getSuperAbstractClassMetaData(), clr); } /** * Method to initialise this table to include all fields in the specified class. * This is used to recurse up the hierarchy so that we include all immediate superclasses * that have "subclass-table" specified as their inheritance strategy. If we encounter the parent of * this class with other than "subclass-table" we stop the process. * @param theCmd The ClassMetaData for the class */ private void initializeForClass(AbstractClassMetaData theCmd, ClassLoaderResolver clr) { String columnOrdering = storeMgr.getStringProperty(RDBMSPropertyNames.PROPERTY_RDBMS_TABLE_COLUMN_ORDER); if (columnOrdering.equalsIgnoreCase("superclass-first")) { // Superclasses persisted into this table AbstractClassMetaData parentCmd = theCmd.getSuperAbstractClassMetaData(); if (parentCmd != null) { if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) { // Managed class requires all superclasses managed here too initializeForClass(parentCmd, clr); } else if (parentCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) { // Superclass uses "subclass-table" so needs managing here initializeForClass(parentCmd, clr); } } // Owning class manageClass(theCmd, clr); } else { // Owning class manageClass(theCmd, clr); // Superclasses persisted into this table AbstractClassMetaData parentCmd = theCmd.getSuperAbstractClassMetaData(); if (parentCmd != null) { if (cmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.COMPLETE_TABLE) { // Managed class requires all superclasses managed here too initializeForClass(parentCmd, clr); } else if (parentCmd.getInheritanceMetaData().getStrategy() == InheritanceStrategy.SUBCLASS_TABLE) { // Superclass uses "subclass-table" so needs managing here initializeForClass(parentCmd, clr); } } } } /** * Execute the callbacks for the classes that this table maps to. * @param clr ClassLoader resolver */ private void runCallBacks(ClassLoaderResolver clr) { // Run callbacks for all classes managed by this table Iterator cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData managedCmd = cmdIter.next(); if (managingClassCurrent != null && managingClassCurrent.equals(managedCmd.getFullClassName())) { // We can't run callbacks for this class since it is still being initialised. Mark callbacks to run after it completes runCallbacksAfterManageClass = true; break; } Collection processedCallbacks = callbacksAppliedForManagedClass.get(managedCmd.getFullClassName()); Collection c = (Collection)storeMgr.getSchemaCallbacks().get(managedCmd.getFullClassName()); if (c != null) { if (processedCallbacks == null) { processedCallbacks = new HashSet(); callbacksAppliedForManagedClass.put(managedCmd.getFullClassName(), processedCallbacks); } for (Iterator it = c.iterator(); it.hasNext();) { AbstractMemberMetaData callbackMmd = (AbstractMemberMetaData) it.next(); if (processedCallbacks.contains(callbackMmd)) { continue; } else { processedCallbacks.add(callbackMmd); } if (callbackMmd.getJoinMetaData() == null) { // 1-N FK relationship AbstractMemberMetaData ownerFmd = callbackMmd; if (ownerFmd.getMappedBy() != null) { // Bidirectional (element has a PC mapping to the owner) // Check that the "mapped-by" field in the other class actually exists AbstractMemberMetaData fmd = managedCmd.getMetaDataForMember(ownerFmd.getMappedBy()); if (fmd == null) { throw new NucleusUserException(LOCALISER.msg("057036", ownerFmd.getMappedBy(), managedCmd.getFullClassName(), ownerFmd.getFullFieldName())); } if (ownerFmd.getMap() != null && storeMgr.getBooleanProperty(RDBMSPropertyNames.PROPERTY_RDBMS_UNIQUE_CONSTRAINTS_MAP_INVERSE)) { initializeFKMapUniqueConstraints(ownerFmd); } boolean duplicate = false; JavaTypeMapping fkDiscrimMapping = null; JavaTypeMapping orderMapping = null; if (ownerFmd.hasExtension("relation-discriminator-column")) { // Collection has a relation discriminator so we need to share the FK. Check for the required discriminator String colName = ownerFmd.getValueForExtension("relation-discriminator-column"); if (colName == null) { // No column defined so use a fallback name colName = "RELATION_DISCRIM"; } Set fkDiscrimEntries = getExternalFkDiscriminatorMappings().entrySet(); Iterator discrimMappingIter = fkDiscrimEntries.iterator(); while (discrimMappingIter.hasNext()) { Map.Entry entry = (Map.Entry)discrimMappingIter.next(); JavaTypeMapping discrimMapping = (JavaTypeMapping)entry.getValue(); String discrimColName = (discrimMapping.getDatastoreMapping(0).getColumn().getColumnMetaData()).getName(); if (discrimColName.equalsIgnoreCase(colName)) { duplicate = true; fkDiscrimMapping = discrimMapping; orderMapping = getExternalOrderMappings().get(entry.getKey()); break; } } if (!duplicate) { // Create the relation discriminator column since we dont have this discriminator ColumnMetaData colmd = new ColumnMetaData(); colmd.setName(colName); colmd.setAllowsNull(Boolean.TRUE); // Allow for elements not in any discriminated collection fkDiscrimMapping = storeMgr.getMappingManager().getMapping(String.class); // Only support String discriminators currently fkDiscrimMapping.setTable(this); ColumnCreator.createIndexColumn(fkDiscrimMapping, storeMgr, clr, this, colmd, false); } if (fkDiscrimMapping != null) { getExternalFkDiscriminatorMappings().put(ownerFmd, fkDiscrimMapping); } } // Add the order mapping as necessary addOrderMapping(ownerFmd, orderMapping, clr); } else { // Unidirectional (element knows nothing about the owner) String ownerClassName = ownerFmd.getAbstractClassMetaData().getFullClassName(); JavaTypeMapping fkMapping = new PersistableMapping(); fkMapping.setTable(this); fkMapping.initialize(storeMgr, ownerClassName); JavaTypeMapping fkDiscrimMapping = null; JavaTypeMapping orderMapping = null; boolean duplicate = false; try { // Get the owner id mapping of the "1" end DatastoreClass ownerTbl = storeMgr.getDatastoreClass(ownerClassName, clr); if (ownerTbl == null) { // Class doesn't have its own table (subclass-table) so find where it persists AbstractClassMetaData[] ownerParentCmds = storeMgr.getClassesManagingTableForClass(ownerFmd.getAbstractClassMetaData(), clr); if (ownerParentCmds.length > 1) { throw new NucleusUserException("Relation (" + ownerFmd.getFullFieldName() + ") with multiple related tables (using subclass-table). Not supported"); } ownerClassName = ownerParentCmds[0].getFullClassName(); ownerTbl = storeMgr.getDatastoreClass(ownerClassName, clr); } JavaTypeMapping ownerIdMapping = ownerTbl.getIdMapping(); ColumnMetaDataContainer colmdContainer = null; if (ownerFmd.hasCollection() || ownerFmd.hasArray()) { // 1-N Collection/array colmdContainer = ownerFmd.getElementMetaData(); } else if (ownerFmd.hasMap() && ownerFmd.getKeyMetaData() != null && ownerFmd.getKeyMetaData().getMappedBy() != null) { // 1-N Map with key stored in the value colmdContainer = ownerFmd.getValueMetaData(); } else if (ownerFmd.hasMap() && ownerFmd.getValueMetaData() != null && ownerFmd.getValueMetaData().getMappedBy() != null) { // 1-N Map with value stored in the key colmdContainer = ownerFmd.getKeyMetaData(); } CorrespondentColumnsMapper correspondentColumnsMapping = new CorrespondentColumnsMapper(colmdContainer, ownerIdMapping, true); int countIdFields = ownerIdMapping.getNumberOfDatastoreMappings(); for (int i=0; i and is indexed list so needs order mapping needsOrderMapping = true; if (omd.getMappedBy() != null) { // Try to find the mapping if already created orderMapping = getMemberMapping(omd.getMappedBy()); } } if (needsOrderMapping) { // if the field is list or array type, add index column state = TABLE_STATE_NEW; if (orderMapping == null) { // Create new order mapping since we need one and we aren't using a shared FK orderMapping = this.addOrderColumn(fmd, clr); } getExternalOrderMappings().put(fmd, orderMapping); state = TABLE_STATE_INITIALIZED; } return orderMapping; } /** * Accessor for the main class represented. * @return The name of the class **/ public String getType() { return cmd.getFullClassName(); } /** * Accessor for the identity-type. * @return identity-type tag value */ public IdentityType getIdentityType() { return cmd.getIdentityType(); } /** * Accessor for versionMetaData * @return Returns the versionMetaData. */ public final VersionMetaData getVersionMetaData() { return versionMetaData; } /** * Accessor for Discriminator MetaData * @return Returns the Discriminator MetaData. */ public final DiscriminatorMetaData getDiscriminatorMetaData() { return discriminatorMetaData; } /** * Convenience method to return the root table with a discriminator in this inheritance tree. * @return The root table which has the discriminator in this inheritance tree */ public final ClassTable getTableWithDiscriminator() { if (supertable != null) { ClassTable tbl = supertable.getTableWithDiscriminator(); if (tbl != null) { return tbl; } } if (discriminatorMetaData != null) { // Initialised and discriminator metadata set so return this return this; } else if (cmd.getInheritanceMetaData() != null && cmd.getInheritanceMetaData().getDiscriminatorMetaData() != null) { // Not initialised but has discriminator MetaData so return this return this; } return null; } /** * Whether this table or super table has id (primary key) attributed by the datastore * @return true if the id attributed by the datastore */ public boolean isObjectIdDatastoreAttributed() { boolean attributed = storeMgr.isStrategyDatastoreAttributed(cmd, -1); if (attributed) { return true; } for (int i=0; i) Iterator cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData thisCmd = cmdIter.next(); IndexMetaData[] classIndices = thisCmd.getIndexMetaData(); if (classIndices != null) { for (int i=0;i 0) { for (int i=0;i 0) { for (int i=0;i... Set memberNumbersSet = memberMappingsMap.keySet(); Iterator iter = memberNumbersSet.iterator(); while (iter.hasNext()) { AbstractMemberMetaData mmd = (AbstractMemberMetaData) iter.next(); JavaTypeMapping memberMapping = memberMappingsMap.get(mmd); if (mmd.getEmbeddedMetaData() != null && memberMapping instanceof EmbeddedPCMapping) { EmbeddedPCMapping embMapping = (EmbeddedPCMapping)memberMapping; addExpectedForeignKeysForEmbeddedPCField(foreignKeys, autoMode, clr, embMapping); } else { if (ClassUtils.isReferenceType(mmd.getType()) && memberMapping instanceof ReferenceMapping) { // Field is a reference type, so add a FK to the table of the PC for each PC implementation Collection fks = TableUtils.getForeignKeysForReferenceField(memberMapping, mmd, autoMode, storeMgr, clr); foreignKeys.addAll(fks); } else if (storeMgr.getNucleusContext().getMetaDataManager().getMetaDataForClass(mmd.getType(), clr) != null && memberMapping.getNumberOfDatastoreMappings() > 0 && memberMapping instanceof PersistableMapping) { // Field is for a PC class with the FK at this side, so add a FK to the table of this PC ForeignKey fk = TableUtils.getForeignKeyForPCField(memberMapping, mmd, autoMode, storeMgr, clr); if (fk != null) { foreignKeys.add(fk); } } } } // FK from id column(s) to id column(s) of superclass, as specified by // ForeignKeyMetaData idFkmd = (cmd.getInheritanceMetaData().getJoinMetaData() != null) ? cmd.getInheritanceMetaData().getJoinMetaData().getForeignKeyMetaData() : null; if (supertable != null && (autoMode || (idFkmd != null && idFkmd.getDeleteAction() != ForeignKeyAction.NONE))) { ForeignKey fk = new ForeignKey(getIdMapping(), dba, supertable, false); if (idFkmd != null && idFkmd.getName() != null) { fk.setName(idFkmd.getName()); } foreignKeys.add(0, fk); } // Add any user-required FKs for the class as a whole // ... Iterator cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData thisCmd = cmdIter.next(); ForeignKeyMetaData[] fkmds = thisCmd.getForeignKeyMetaData(); if (fkmds != null) { for (int i=0;i> externalFkKeysIter = externalFkKeys.iterator(); while (externalFkKeysIter.hasNext()) { Map.Entry entry = externalFkKeysIter.next(); AbstractMemberMetaData fmd = entry.getKey(); DatastoreClass referencedTable = storeMgr.getDatastoreClass(fmd.getAbstractClassMetaData().getFullClassName(), clr); if (referencedTable != null) { // Take from either or ForeignKeyMetaData fkmd = fmd.getForeignKeyMetaData(); if (fkmd == null && fmd.getElementMetaData() != null) { fkmd = fmd.getElementMetaData().getForeignKeyMetaData(); } if ((fkmd != null && fkmd.getDeleteAction() != ForeignKeyAction.NONE) || autoMode) { // Either has been specified by user, or using autoMode, so add FK JavaTypeMapping fkMapping = entry.getValue(); ForeignKey fk = new ForeignKey(fkMapping, dba, referencedTable, true); fk.setForMetaData(fkmd); // Does nothing when no FK MetaData if (!foreignKeys.contains(fk)) { // Only add when not already present (in the case of shared FKs there can be dups here) foreignKeys.add(fk); } } } } } return foreignKeys; } /** * Convenience method to add the expected FKs for an embedded PC field. * @param foreignKeys The list of FKs to add the FKs to * @param autoMode Whether operating in "auto-mode" where JPOX can create its own FKs * @param clr ClassLoader resolver * @param embeddedMapping The embedded PC mapping */ private void addExpectedForeignKeysForEmbeddedPCField(List foreignKeys, boolean autoMode, ClassLoaderResolver clr, EmbeddedPCMapping embeddedMapping) { for (int i=0;i 0 && embFieldMapping instanceof PersistableMapping) { // Field is for a PC class with the FK at this side, so add a FK to the table of this PC ForeignKey fk = TableUtils.getForeignKeyForPCField(embFieldMapping, embFmd, autoMode, storeMgr, clr); if (fk != null) { foreignKeys.add(fk); } } } } } /** * Convenience method to create a FK for the specified ForeignKeyMetaData. * Used for foreign-keys specified at <class> level. * @param fkmd ForeignKey MetaData * @return The ForeignKey */ private ForeignKey getForeignKeyForForeignKeyMetaData(ForeignKeyMetaData fkmd) { if (fkmd == null) { return null; } // Create the ForeignKey base details ForeignKey fk = new ForeignKey(fkmd.isDeferred()); fk.setForMetaData(fkmd); if (fkmd.getFkDefinitionApplies()) { // User-defined FK definition should be used return fk; } // Find the target of the foreign-key AbstractClassMetaData acmd = cmd; if (fkmd.getTable() == null) { // Can't create a FK if we don't know where it goes to NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("058105", acmd.getFullClassName())); return null; } DatastoreIdentifier tableId = storeMgr.getIdentifierFactory().newTableIdentifier(fkmd.getTable()); ClassTable refTable = (ClassTable)storeMgr.getDatastoreClass(tableId); if (refTable == null) { // TODO Go to the datastore and query for this table to get the columns of the PK NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("058106", acmd.getFullClassName(), fkmd.getTable())); return null; } PrimaryKey pk = refTable.getPrimaryKey(); List targetCols = pk.getColumns(); // Generate the columns for the source of the foreign-key List sourceCols = new ArrayList(); ColumnMetaData[] colmds = fkmd.getColumnMetaData(); String[] memberNames = fkmd.getMemberNames(); if (colmds != null && colmds.length > 0) { // FK specified via for (int i=0;i 0) { // FK specified via for (int i=0;i 0) { for (int i=0;i cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData thisCmd = cmdIter.next(); UniqueMetaData[] classCKs = thisCmd.getUniqueMetaData(); if (classCKs != null) { for (int i=0;i 0) { for (int i=0;i 0) { for (int i=0;i cks = candidateKeysByMapField.values().iterator(); while (cks.hasNext()) { DatastoreIdentifier ckName = idFactory.newCandidateKeyIdentifier(this, ++ckNum); CandidateKey ck = cks.next(); ck.setName(ckName.getIdentifierName()); String ckSql = dba.getAddCandidateKeyStatement(ck, idFactory); if (ckSql != null) { stmts.add(ckSql); } } return stmts; } /** * Method to initialise unique constraints for 1-N Map using FK. * @param ownerMmd metadata for the field/property with the map in the owner class */ private void initializeFKMapUniqueConstraints(AbstractMemberMetaData ownerMmd) { // Load "mapped-by" AbstractMemberMetaData mfmd = null; String map_field_name = ownerMmd.getMappedBy(); // Field in our class that maps back to the owner class if (map_field_name != null) { // Bidirectional mfmd = cmd.getMetaDataForMember(map_field_name); if (mfmd == null) { // Field not in primary class so may be in subclass so check all managed classes Iterator cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData managedCmd = cmdIter.next(); mfmd = managedCmd.getMetaDataForMember(map_field_name); if (mfmd != null) { break; } } } if (mfmd == null) { // "mapped-by" refers to a field in our class that doesnt exist! throw new NucleusUserException(LOCALISER.msg("057036", map_field_name, cmd.getFullClassName(), ownerMmd.getFullFieldName())); } if (ownerMmd.getJoinMetaData() == null) { // Load field of key in value if (ownerMmd.getKeyMetaData() != null && ownerMmd.getKeyMetaData().getMappedBy() != null) { // Key field is stored in the value table AbstractMemberMetaData kmd = null; String key_field_name = ownerMmd.getKeyMetaData().getMappedBy(); if (key_field_name != null) { kmd = cmd.getMetaDataForMember(key_field_name); } if (kmd == null) { // Field not in primary class so may be in subclass so check all managed classes Iterator cmdIter = managedClassMetaData.iterator(); while (cmdIter.hasNext()) { AbstractClassMetaData managedCmd = cmdIter.next(); kmd = managedCmd.getMetaDataForMember(key_field_name); if (kmd != null) { break; } } } if (kmd == null) { throw new ClassDefinitionException(LOCALISER.msg("057007", mfmd.getFullFieldName(), key_field_name)); } JavaTypeMapping ownerMapping = getMemberMapping(map_field_name); JavaTypeMapping keyMapping = getMemberMapping(kmd.getName()); if (dba.supportsOption(DatastoreAdapter.NULLS_IN_CANDIDATE_KEYS) || (!ownerMapping.isNullable() && !keyMapping.isNullable())) { // If the owner and key fields are represented in this table then we can impose // a unique constraint on them. If the key field is in a superclass then we // cannot do this so just omit it. if (keyMapping.getTable() == this && ownerMapping.getTable() == this) { CandidateKey ck = new CandidateKey(this); // This HashSet is to avoid duplicate adding of columns. HashSet addedColumns = new HashSet(); // Add columns for the owner field int countOwnerFields = ownerMapping.getNumberOfDatastoreMappings(); for (int i = 0; i < countOwnerFields; i++) { Column col = ownerMapping.getDatastoreMapping(i).getColumn(); addedColumns.add(col); ck.addColumn(col); } // Add columns for the key field int countKeyFields = keyMapping.getNumberOfDatastoreMappings(); for (int i = 0; i < countKeyFields; i++) { Column col = keyMapping.getDatastoreMapping(i).getColumn(); if (!addedColumns.contains(col)) { addedColumns.add(col); ck.addColumn(col); } else { NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("057041", ownerMmd.getName())); } } if (candidateKeysByMapField.put(mfmd, ck) != null) { // We have multiple "mapped-by" coming to this field so give a warning that this may potentially // cause problems. For example if they have the key field defined here for 2 different relations // so you may get keys/values appearing in the other relation that shouldn't be. // Logged as a WARNING for now. // If there is a situation where this should throw an exception, please update this AND COMMENT WHY. NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("057012", mfmd.getFullFieldName(), ownerMmd.getFullFieldName())); } } } } else if (ownerMmd.getValueMetaData() != null && ownerMmd.getValueMetaData().getMappedBy() != null) { // Value field is stored in the key table AbstractMemberMetaData vmd = null; String value_field_name = ownerMmd.getValueMetaData().getMappedBy(); if (value_field_name != null) { vmd = cmd.getMetaDataForMember(value_field_name); } if (vmd == null) { throw new ClassDefinitionException(LOCALISER.msg("057008", mfmd)); } JavaTypeMapping ownerMapping = getMemberMapping(map_field_name); JavaTypeMapping valueMapping = getMemberMapping(vmd.getName()); if (dba.supportsOption(DatastoreAdapter.NULLS_IN_CANDIDATE_KEYS) || (!ownerMapping.isNullable() && !valueMapping.isNullable())) { // If the owner and value fields are represented in this table then we can impose // a unique constraint on them. If the value field is in a superclass then we // cannot do this so just omit it. if (valueMapping.getTable() == this && ownerMapping.getTable() == this) { CandidateKey ck = new CandidateKey(this); // This HashSet is to avoid duplicate adding of columns. HashSet addedColumns = new HashSet(); // Add columns for the owner field int countOwnerFields = ownerMapping.getNumberOfDatastoreMappings(); for (int i = 0; i < countOwnerFields; i++) { Column col = ownerMapping.getDatastoreMapping(i).getColumn(); addedColumns.add(col); ck.addColumn(col); } // Add columns for the value field int countValueFields = valueMapping.getNumberOfDatastoreMappings(); for (int i = 0; i < countValueFields; i++) { Column col = valueMapping.getDatastoreMapping(i).getColumn(); if (!addedColumns.contains(col)) { addedColumns.add(col); ck.addColumn(col); } else { NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("057042", ownerMmd.getName())); } } if (candidateKeysByMapField.put(mfmd, ck) != null) { // We have multiple "mapped-by" coming to this field so give a warning that this may potentially // cause problems. For example if they have the key field defined here for 2 different relations // so you may get keys/values appearing in the other relation that shouldn't be. // Logged as a WARNING for now. // If there is a situation where this should throw an exception, please update this AND COMMENT WHY. NucleusLogger.DATASTORE_SCHEMA.warn(LOCALISER.msg("057012", mfmd.getFullFieldName(), ownerMmd.getFullFieldName())); } } } } else { // We can only have either the key stored in the value or the value stored in the key but nothing else! throw new ClassDefinitionException(LOCALISER.msg("057009", ownerMmd.getFullFieldName())); } } } } /** * Initialize the ID Mapping. * For datastore identity this will be a PCMapping that contains the OIDMapping. * For application identity this will be a PCMapping that contains the PK mapping(s). */ private void initializeIDMapping() { if (idMapping != null) { return; } final PersistableMapping mapping = new PersistableMapping(); mapping.setTable(this); mapping.initialize(getStoreManager(), cmd.getFullClassName()); if (getIdentityType() == IdentityType.DATASTORE) { mapping.addJavaTypeMapping(datastoreIDMapping); } else if (getIdentityType() == IdentityType.APPLICATION) { for (int i = 0; i < pkMappings.length; i++) { mapping.addJavaTypeMapping(pkMappings[i]); } } else { // Nothing to do for nondurable since no identity } idMapping = mapping; } /** * Accessor for a mapping for the ID (PersistenceCapable) for this table. * @return The (PersistenceCapable) ID mapping. */ public JavaTypeMapping getIdMapping() { return idMapping; } /** * Accessor for all of the order mappings (used by FK Lists, Collections, Arrays) * @return The mappings for the order columns. **/ private Map getExternalOrderMappings() { if (externalOrderMappings == null) { externalOrderMappings = new HashMap(); } return externalOrderMappings; } public boolean hasExternalFkMappings() { return externalFkMappings != null && externalFkMappings.size() > 0; } /** * Accessor for all of the external FK mappings. * @return The mappings for external FKs **/ private Map getExternalFkMappings() { if (externalFkMappings == null) { externalFkMappings = new HashMap(); } return externalFkMappings; } /** * Accessor for an external mapping for the specified field of the required type. * @param mmd MetaData for the field/property * @param mappingType Type of mapping * @return The (external) mapping */ public JavaTypeMapping getExternalMapping(AbstractMemberMetaData mmd, int mappingType) { if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) { return getExternalFkMappings().get(mmd); } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM) { return getExternalFkDiscriminatorMappings().get(mmd); } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX) { return getExternalOrderMappings().get(mmd); } else { return null; } } /** * Accessor for the MetaData for the (owner) field that an external mapping corresponds to. * @param mapping The mapping * @param mappingType The mapping type * @return metadata for the external mapping */ public AbstractMemberMetaData getMetaDataForExternalMapping(JavaTypeMapping mapping, int mappingType) { if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK) { Set entries = getExternalFkMappings().entrySet(); Iterator iter = entries.iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); if (entry.getValue() == mapping) { return (AbstractMemberMetaData)entry.getKey(); } } } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM) { Set entries = getExternalFkDiscriminatorMappings().entrySet(); Iterator iter = entries.iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); if (entry.getValue() == mapping) { return (AbstractMemberMetaData)entry.getKey(); } } } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX) { Set entries = getExternalOrderMappings().entrySet(); Iterator iter = entries.iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry)iter.next(); if (entry.getValue() == mapping) { return (AbstractMemberMetaData)entry.getKey(); } } } return null; } /** * Accessor for all of the external FK discriminator mappings. * @return The mappings for external FKs **/ private Map getExternalFkDiscriminatorMappings() { if (externalFkDiscriminatorMappings == null) { externalFkDiscriminatorMappings = new HashMap(); } return externalFkDiscriminatorMappings; } /** * Accessor for the field mapping for the specified field. * The field can be managed by a supertable of this table. * @param mmd MetaData for this field/property * @return the Mapping for the field/property */ public JavaTypeMapping getMemberMapping(AbstractMemberMetaData mmd) { if (mmd == null) { return null; } if (mmd instanceof PropertyMetaData && mmd.getAbstractClassMetaData() instanceof InterfaceMetaData) { // "Persistent Interfaces" don't do lookups correctly in here so just use the field name for now // TODO Fix Persistent Interfaces lookup return getMemberMapping(mmd.getName()); } if (mmd.isPrimaryKey()) { assertIsPKInitialized(); } else { assertIsInitialized(); } // Check if we manage this field JavaTypeMapping m = memberMappingsMap.get(mmd); if (m != null) { return m; } // Check supertable int ifc = cmd.getNoOfInheritedManagedMembers(); if (mmd.getAbsoluteFieldNumber() < ifc) { if (supertable != null) { m = supertable.getMemberMapping(mmd); if (m != null) { return m; } } } // Check secondary tables if (secondaryTables != null) { Collection secTables = secondaryTables.values(); Iterator iter = secTables.iterator(); while (iter.hasNext()) { SecondaryTable secTable = (SecondaryTable)iter.next(); m = secTable.getMemberMapping(mmd); if (m != null) { return m; } } } return null; } /** * Accessor for the mapping for the specified field only in this datastore class. * @param mmd Metadata of the field/property * @return The Mapping for the field/property (or null if not present here) */ public JavaTypeMapping getMemberMappingInDatastoreClass(AbstractMemberMetaData mmd) { if (mmd == null) { return null; } if (mmd instanceof PropertyMetaData && mmd.getAbstractClassMetaData() instanceof InterfaceMetaData) { // "Persistent Interfaces" don't do lookups correctly in here so just use the field name for now // TODO Fix Persistent Interfaces lookup return getMemberMapping(mmd.getName()); } if (mmd.isPrimaryKey()) { assertIsPKInitialized(); } else { assertIsInitialized(); } // Check if we manage this field JavaTypeMapping m = memberMappingsMap.get(mmd); if (m != null) { return m; } // Check if it is a PK field if (pkMappings != null) { for (int i=0;i iter = managedClassMetaData.iterator(); while (iter.hasNext()) { AbstractClassMetaData theCmd = iter.next(); final AbstractMemberMetaData foundMmd = theCmd.getMetaDataForMember(memberName); if (foundMmd != null) { if (mmd != null && (!mmd.toString().equalsIgnoreCase(foundMmd.toString()) || mmd.getType() != foundMmd.getType())) { final String errMsg = "Table " + getIdentifier() + " manages at least 2 subclasses that both define a field \"" + memberName + "\", " + "and the fields' metadata is different or they have different type! That means you can get e.g. wrong fetch results."; NucleusLogger.DATASTORE_SCHEMA.error(errMsg); throw new NucleusException(errMsg).setFatal(); } mmd = foundMmd; } } } return mmd; } /** * Utility to throw an exception if the object is not PersistenceCapable. * @param op The ObjectProvider for the object */ void assertPCClass(ObjectProvider op) { Class c = op.getObject().getClass(); if (!op.getExecutionContext().getClassLoaderResolver().isAssignableFrom(cmd.getFullClassName(),c)) { throw new NucleusException(LOCALISER.msg("057013",cmd.getFullClassName(),c)).setFatal(); } } /** * Adds an ordering column to the element table (this) in FK list relationships. * Used to store the position of the element in the List. * If the <order> provides a mapped-by, this will return the existing column mapping. * @param mmd The MetaData of the field/property with the list for the column to map to * @return The Mapping for the order column */ private JavaTypeMapping addOrderColumn(AbstractMemberMetaData mmd, ClassLoaderResolver clr) { Class indexType = Integer.class; JavaTypeMapping indexMapping = new IndexMapping(); indexMapping.initialize(storeMgr, indexType.getName()); indexMapping.setMemberMetaData(mmd); indexMapping.setTable(this); IdentifierFactory idFactory = storeMgr.getIdentifierFactory(); DatastoreIdentifier indexColumnName = null; ColumnMetaData colmd = null; // Allow for any user definition in OrderMetaData OrderMetaData omd = mmd.getOrderMetaData(); if (omd != null) { colmd = (omd.getColumnMetaData() != null && omd.getColumnMetaData().length > 0 ? omd.getColumnMetaData()[0] : null); if (omd.getMappedBy() != null) { // User has defined ordering using the column(s) of an existing field. state = TABLE_STATE_INITIALIZED; // Not adding anything so just set table back to "initialised" JavaTypeMapping orderMapping = getMemberMapping(omd.getMappedBy()); if (orderMapping == null) { throw new NucleusUserException(LOCALISER.msg("057021", mmd.getFullFieldName(), omd.getMappedBy())); } if (!(orderMapping instanceof IntegerMapping) && !(orderMapping instanceof LongMapping)) { throw new NucleusUserException(LOCALISER.msg("057022", mmd.getFullFieldName(), omd.getMappedBy())); } return orderMapping; } String colName = null; if (omd.getColumnMetaData() != null && omd.getColumnMetaData().length > 0 && omd.getColumnMetaData()[0].getName() != null) { // User-defined name so create an identifier using it colName = omd.getColumnMetaData()[0].getName(); indexColumnName = idFactory.newColumnIdentifier(colName); } } if (indexColumnName == null) { // No name defined so generate one indexColumnName = idFactory.newForeignKeyFieldIdentifier(mmd, null, null, storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(indexType), FieldRole.ROLE_INDEX); } Column column = addColumn(indexType.getName(), indexColumnName, indexMapping, colmd); if (colmd == null || (colmd.getAllowsNull() == null) || (colmd.getAllowsNull() != null && colmd.isAllowsNull())) { // User either wants it nullable, or havent specified anything, so make it nullable column.setNullable(); } storeMgr.getMappingManager().createDatastoreMapping(indexMapping, column, indexType.getName()); return indexMapping; } /** * Provide the mappings to the consumer for all primary-key fields mapped to * this table. * @param consumer Consumer for the mappings */ public void providePrimaryKeyMappings(MappingConsumer consumer) { consumer.preConsumeMapping(highestMemberNumber + 1); if (pkMappings != null) { // Application identity int[] primaryKeyFieldNumbers = cmd.getPKMemberPositions(); for (int i=0;i iter = externalFkMappings.keySet().iterator(); while (iter.hasNext()) { AbstractMemberMetaData fmd = iter.next(); JavaTypeMapping fieldMapping = externalFkMappings.get(fmd); if (fieldMapping != null) { consumer.consumeMapping(fieldMapping, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK); } } } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM && externalFkDiscriminatorMappings != null) { consumer.preConsumeMapping(highestMemberNumber + 1); Iterator iter = externalFkDiscriminatorMappings.keySet().iterator(); while (iter.hasNext()) { AbstractMemberMetaData fmd = iter.next(); JavaTypeMapping fieldMapping = externalFkDiscriminatorMappings.get(fmd); if (fieldMapping != null) { consumer.consumeMapping(fieldMapping, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM); } } } else if (mappingType == MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX && externalOrderMappings != null) { consumer.preConsumeMapping(highestMemberNumber + 1); Iterator iter = externalOrderMappings.keySet().iterator(); while (iter.hasNext()) { AbstractMemberMetaData fmd = iter.next(); JavaTypeMapping fieldMapping = externalOrderMappings.get(fmd); if (fieldMapping != null) { consumer.consumeMapping(fieldMapping, MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX); } } } } /** * Provide the mappings to the consumer for all absolute field Numbers in this table * that are container in the fieldNumbers parameter. * @param consumer Consumer for the mappings * @param fieldMetaData MetaData for the fields to provide mappings for * @param includeSecondaryTables Whether to provide fields in secondary tables */ public void provideMappingsForMembers(MappingConsumer consumer, AbstractMemberMetaData[] fieldMetaData, boolean includeSecondaryTables) { super.provideMappingsForMembers(consumer, fieldMetaData, true); if (includeSecondaryTables && secondaryTables != null) { Collection secTables = secondaryTables.values(); Iterator iter = secTables.iterator(); while (iter.hasNext()) { SecondaryTable secTable = (SecondaryTable)iter.next(); secTable.provideMappingsForMembers(consumer, fieldMetaData, false); } } } /** * Method to provide all unmapped columns to the consumer. * @param consumer Consumer of information */ public void provideUnmappedColumns(MappingConsumer consumer) { if (unmappedColumns != null) { Iterator iter = unmappedColumns.iterator(); while (iter.hasNext()) { consumer.consumeUnmappedColumn(iter.next()); } } } /** * Method to validate the constraints of this table. * @param conn Connection to use in validation * @param autoCreate Whether to auto create the constraints * @param autoCreateErrors Whether to log a warning only on errors during "auto create" * @param clr The ClassLoaderResolver * @return Whether the DB was modified * @throws SQLException Thrown when an error occurs in validation */ public boolean validateConstraints(Connection conn, boolean autoCreate, Collection autoCreateErrors, ClassLoaderResolver clr) throws SQLException { boolean modified = false; if (super.validateConstraints(conn, autoCreate, autoCreateErrors, clr)) { modified = true; } // Validate our secondary tables since we manage them if (secondaryTables != null) { Collection secTables = secondaryTables.values(); Iterator iter = secTables.iterator(); while (iter.hasNext()) { SecondaryTable secTable = (SecondaryTable)iter.next(); if (secTable.validateConstraints(conn, autoCreate, autoCreateErrors, clr)) { modified = true; } } } return modified; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy