org.datanucleus.store.rdbms.table.AbstractClassTable Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-rdbms Show documentation
Show all versions of datanucleus-rdbms Show documentation
Plugin for DataNucleus providing persistence to RDBMS datastores.
/**********************************************************************
Copyright (c) 2005 Andy Jefferson 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:
...
**********************************************************************/
package org.datanucleus.store.rdbms.table;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.identity.DatastoreId;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.ColumnMetaDataContainer;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.ValueGenerationStrategy;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.store.rdbms.adapter.DatastoreAdapter;
import org.datanucleus.store.rdbms.identifier.DatastoreIdentifier;
import org.datanucleus.store.rdbms.identifier.IdentifierType;
import org.datanucleus.store.rdbms.mapping.MappingConsumer;
import org.datanucleus.store.rdbms.mapping.MappingType;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.mapping.java.DatastoreIdMapping;
import org.datanucleus.store.rdbms.mapping.java.PersistableMapping;
import org.datanucleus.store.schema.table.SurrogateColumnType;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
/**
* Abstract representation of a table for a class.
* Abstracts out the common parts of a primary ClassTable and a SecondaryClassTable.
*
* Mappings
* A Table is built from a series of field mappings. Each Java class has a series of fields and each of these has an associated JavaTypeMapping.
* Each JavaTypeMapping has related ColumnMapping(s). These are used in mapping the Java class to the table, and are used when populating the table,
* and when retrieving data from the table back to the object. There are several categories of mappings in this class
*
* - memberMappingsMap - the set of mappings relating to the fields in the class. The mappings are keyed by the FieldMetaData of the field. Any embedded field
* will have a single mapping here of type EmbeddedPCMapping, with a set of datastore mappings attached.
* - datastoreIdMapping - the Identity mapping when using "datastore identity"
* - pkMappings - the mappings for the primary key column(s).
* - discriminatorMapping - mapping for any discriminator column. This is only used where classes share this table and some of them use "superclass-table" strategy
* - versionMapping - mapping for any versioning column
*
*/
public abstract class AbstractClassTable extends TableImpl
{
/** Mappings for members mapped to this table, keyed by the metadata for the member. */
protected Map memberMappingsMap = new LinkedHashMap<>();
/** Mappings for application identity (optional). */
protected JavaTypeMapping[] pkMappings;
/** Mapping for the overall "identity" of the table. */
protected JavaTypeMapping idMapping;
/** Mapping for any datastore identity. */
protected JavaTypeMapping datastoreIdMapping;
/** Mapping for any version/timestamp column. */
protected JavaTypeMapping versionMapping;
/** Mapping for any discriminator column. */
protected JavaTypeMapping discriminatorMapping;
/** Mapping for any multi-tenancy column. */
protected JavaTypeMapping multitenancyMapping;
/** Mapping for any soft-delete column. */
protected JavaTypeMapping softDeleteMapping;
/** Mapping for any create user column. */
protected JavaTypeMapping createUserMapping;
/** Mapping for any update user column. */
protected JavaTypeMapping updateUserMapping;
/** Mapping for any create timestamp column. */
protected JavaTypeMapping createTimestampMapping;
/** Mapping for any update timestamp column. */
protected JavaTypeMapping updateTimestampMapping;
/** MetaData for versioning of objects stored in this table. */
protected VersionMetaData versionMetaData;
/** MetaData for discriminator for objects stored in this table. */
protected DiscriminatorMetaData discriminatorMetaData;
/** Highest absolute field/property number managed by this table */
protected int highestMemberNumber = 0;
/**
* Constructor.
* @param tableName Name of the table
* @param storeMgr Store Manager that is managing this instance
*/
public AbstractClassTable(DatastoreIdentifier tableName, RDBMSStoreManager storeMgr)
{
super(tableName, storeMgr);
}
/**
* Convenience method to return the primary table.
* @return The primary table for this table
*/
public Table getPrimaryTable()
{
return this;
}
/**
* Method to initialise the table primary key field(s).
* @param clr The ClassLoaderResolver
**/
protected abstract void initializePK(ClassLoaderResolver clr);
/**
* Convenience method for whether the (fully-specified) member is managed by this table
* @param memberName Fully qualified name of the field/property
* @return Whether it is managed
*/
public boolean managesMember(String memberName)
{
if (memberName == null)
{
return false;
}
return (getMappingForMemberName(memberName) != null);
}
/**
* Accessor for the JavaTypeMapping that is handling the member of the specified name.
* Returns the first one that matches.
* @param memberName Name of the field/property
* @return The java type mapping
*/
protected JavaTypeMapping getMappingForMemberName(String memberName)
{
Iterator> memberMapEntryIter = memberMappingsMap.entrySet().iterator();
while (memberMapEntryIter.hasNext())
{
Map.Entry memberMapEntry = memberMapEntryIter.next();
AbstractMemberMetaData mmd = memberMapEntry.getKey();
if (mmd.getFullFieldName().equals(memberName))
{
return memberMapEntry.getValue();
}
}
return null;
}
/**
* Convenience method to return if this table manages the columns for the supplied mapping.
* @param mapping The mapping
* @return Whether the mapping is managed in this table
*/
public boolean managesMapping(JavaTypeMapping mapping)
{
if (memberMappingsMap.values().contains(mapping))
{
return true;
}
else if (mapping == discriminatorMapping)
{
return true;
}
else if (mapping == versionMapping)
{
return true;
}
else if (mapping == datastoreIdMapping)
{
return true;
}
else if (mapping == idMapping)
{
return true;
}
else if (mapping == multitenancyMapping)
{
return true;
}
return false;
}
/**
* Utility to create the application identity columns and mapping.
* Uses the id mapping of the specified class table and copies the mappings
* and columns, whilst retaining the passed preferences for column namings.
* This is used to copy the PK mappings of a superclass table so we have the same PK.
* @param columnContainer The container of column MetaData with any namings
* @param refTable The table that we use as reference
* @param clr The ClassLoaderResolver
* @param cmd The ClassMetaData
*/
final void addApplicationIdUsingClassTableId(ColumnMetaDataContainer columnContainer, DatastoreClass refTable, ClassLoaderResolver clr, AbstractClassMetaData cmd)
{
ColumnMetaData[] userdefinedCols = null;
int nextUserdefinedCol = 0;
if (columnContainer != null)
{
userdefinedCols = columnContainer.getColumnMetaData();
}
pkMappings = new JavaTypeMapping[cmd.getPKMemberPositions().length];
for (int i=0; i highestMemberNumber)
{
highestMemberNumber = absoluteFieldNumber;
}
}
}
/**
* Utility to create the datastore identity column and mapping.
* This is used in 2 modes. The first is where we have a (primary) class table
* and we aren't creating the OID mapping as a FK to another class. The second
* is where we have a (secondary) class table and we are creating the OID mapping
* as a FK to the primary class. In the second case the refTable will be specified.
* @param columnMetaData The column MetaData for the datastore id
* @param refTable Table used as a reference (if any)
* @param cmd The MetaData for the class
*/
void addDatastoreId(ColumnMetaData columnMetaData, DatastoreClass refTable, AbstractClassMetaData cmd)
{
// Create the mapping, setting its table
datastoreIdMapping = new DatastoreIdMapping();
datastoreIdMapping.setTable(this);
datastoreIdMapping.initialize(storeMgr, cmd.getFullClassName());
// Create a ColumnMetaData in the container if none is defined
ColumnMetaData colmd = null;
if (columnMetaData == null)
{
colmd = new ColumnMetaData();
}
else
{
colmd = columnMetaData;
}
if (colmd.getName() == null)
{
// Provide default column naming if none is defined
if (refTable != null)
{
colmd.setName(storeMgr.getIdentifierFactory().newColumnIdentifier(refTable.getIdentifier().getName(),
this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(DatastoreId.class), FieldRole.ROLE_OWNER, false).getName());
}
else
{
colmd.setName(storeMgr.getIdentifierFactory().newColumnIdentifier(identifier.getName(),
this.storeMgr.getNucleusContext().getTypeManager().isDefaultEmbeddedType(DatastoreId.class), FieldRole.ROLE_NONE, false).getName());
}
}
// Add the datastore identity column as the PK
Column idColumn = addColumn(DatastoreId.class.getName(), storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN, colmd.getName()), datastoreIdMapping, colmd);
idColumn.setPrimaryKey();
// Set the identity column type based on the IdentityStrategy
String strategyName = cmd.getIdentityMetaData().getValueStrategy().toString();
if (cmd.getIdentityMetaData().getValueStrategy().equals(ValueGenerationStrategy.CUSTOM))
{
strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName();
}
if (strategyName != null && ValueGenerationStrategy.NATIVE.toString().equals(strategyName))
{
strategyName = storeMgr.getValueGenerationStrategyForNative(cmd, -1);
}
// Check the value generator type being stored
Class valueGeneratedType = Long.class;
if (strategyName != null && ValueGenerationStrategy.IDENTITY.toString().equals(strategyName))
{
valueGeneratedType = dba.getIdentityJavaTypeForType(valueGeneratedType);
if (valueGeneratedType != Long.class)
{
NucleusLogger.DATASTORE_SCHEMA.debug("Class " + cmd.getFullClassName() + " uses IDENTITY strategy and rather than using BIGINT " +
" for the column type, using " + valueGeneratedType.getName() + " since the datastore requires that");
}
}
else
{
// Get the type that will be generated by the chosen ValueGeneration strategy
valueGeneratedType = storeMgr.getValueGenerationManager().getTypeForValueGeneratorForMember(strategyName, storeMgr.getValueGenerationManager().getMemberKey(cmd, -1));
}
storeMgr.getMappingManager().createColumnMapping(datastoreIdMapping, idColumn, valueGeneratedType.getName());
logMapping("DATASTORE_ID", datastoreIdMapping);
// Handle any auto-increment requirement
if (isObjectIdDatastoreAttributed())
{
if (this instanceof DatastoreClass && ((DatastoreClass)this).isBaseDatastoreClass())
{
// Only the base class can be autoincremented
idColumn.setIdentity(true);
}
}
// Check if auto-increment and that it is supported by this RDBMS
if (idColumn.isIdentity() && !dba.supportsOption(DatastoreAdapter.IDENTITY_COLUMNS))
{
throw new NucleusException(Localiser.msg("057020", cmd.getFullClassName(), "datastore-identity")).setFatal();
}
}
/**
* Utility to add the mapping for a field/property to the managed list.
* @param fieldMapping The mapping for the field/property
*/
protected void addMemberMapping(JavaTypeMapping fieldMapping)
{
AbstractMemberMetaData mmd = fieldMapping.getMemberMetaData();
logMapping(mmd.getFullFieldName(), fieldMapping);
memberMappingsMap.put(mmd, fieldMapping);
// Update highest field number if this is higher
int absoluteFieldNumber = mmd.getAbsoluteFieldNumber();
if (absoluteFieldNumber > highestMemberNumber)
{
highestMemberNumber = absoluteFieldNumber;
}
}
/**
* Accessor for the identity-type.
* @return identity-type tag value
*/
public abstract IdentityType getIdentityType();
/**
* Accessor for whether the table has its identity attributed
* by the datastore (e.g using autoincrement)
* @return Whether it is datastore attributed
*/
public abstract boolean isObjectIdDatastoreAttributed();
// -------------------------- Mapping Accessors --------------------------------
/* (non-Javadoc)
* @see org.datanucleus.store.schema.table.Table#getSurrogateColumn(org.datanucleus.store.schema.table.SurrogateColumnType)
*/
@Override
public Column getSurrogateColumn(SurrogateColumnType colType)
{
assertIsInitialized();
if (colType == SurrogateColumnType.DATASTORE_ID)
{
return datastoreIdMapping != null ? datastoreIdMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.DISCRIMINATOR)
{
return discriminatorMapping != null ? discriminatorMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.MULTITENANCY)
{
return multitenancyMapping != null ? multitenancyMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.VERSION)
{
return versionMapping != null ? versionMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.SOFTDELETE)
{
return softDeleteMapping != null ? softDeleteMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.CREATE_USER)
{
return createUserMapping != null ? createUserMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.CREATE_TIMESTAMP)
{
return createTimestampMapping != null ? createTimestampMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.UPDATE_USER)
{
return updateUserMapping != null ? updateUserMapping.getColumnMapping(0).getColumn() : null;
}
else if (colType == SurrogateColumnType.UPDATE_TIMESTAMP)
{
return updateTimestampMapping != null ? updateTimestampMapping.getColumnMapping(0).getColumn() : null;
}
return null;
}
/* (non-Javadoc)
* @see org.datanucleus.store.rdbms.table.Table#getSurrogateMapping(org.datanucleus.store.schema.table.SurrogateColumnType, boolean)
*/
@Override
public JavaTypeMapping getSurrogateMapping(SurrogateColumnType colType, boolean allowSuperclasses)
{
assertIsInitialized();
if (colType == SurrogateColumnType.DISCRIMINATOR)
{
return discriminatorMapping;
}
else if (colType == SurrogateColumnType.MULTITENANCY)
{
return multitenancyMapping;
}
else if (colType == SurrogateColumnType.VERSION)
{
return versionMapping;
}
else if (colType == SurrogateColumnType.DATASTORE_ID)
{
return datastoreIdMapping;
}
else if (colType == SurrogateColumnType.SOFTDELETE)
{
return softDeleteMapping;
}
else if (colType == SurrogateColumnType.CREATE_USER)
{
return createUserMapping;
}
else if (colType == SurrogateColumnType.CREATE_TIMESTAMP)
{
return createTimestampMapping;
}
else if (colType == SurrogateColumnType.UPDATE_USER)
{
return updateUserMapping;
}
else if (colType == SurrogateColumnType.UPDATE_TIMESTAMP)
{
return updateTimestampMapping;
}
return super.getSurrogateMapping(colType, allowSuperclasses);
}
/**
* Provide the mappings to the consumer for all primary-key fields mapped to
* this table (for application identity).
* @param consumer Consumer for the mappings
*/
public abstract void providePrimaryKeyMappings(MappingConsumer consumer);
/**
* Provide the mappings to the consumer for all non primary-key fields
* mapped to this table.
* @param consumer Consumer for the mappings
*/
final public void provideNonPrimaryKeyMappings(MappingConsumer consumer)
{
consumer.preConsumeMapping(highestMemberNumber + 1);
Iterator> memberMapEntryIter = memberMappingsMap.entrySet().iterator();
while (memberMapEntryIter.hasNext())
{
Map.Entry memberMapEntry = memberMapEntryIter.next();
AbstractMemberMetaData mmd = memberMapEntry.getKey();
JavaTypeMapping memberMapping = memberMapEntry.getValue();
if (memberMapping != null)
{
if (!mmd.isPrimaryKey())
{
consumer.consumeMapping(memberMapping, mmd);
}
}
}
}
/**
* Provide the mappings to the consumer for all specified members.
* @param consumer Consumer for the mappings
* @param mmds MetaData for the members to provide mappings for
* @param includeSecondaryTables Whether to provide members in secondary tables
*/
public void provideMappingsForMembers(MappingConsumer consumer, AbstractMemberMetaData[] mmds, boolean includeSecondaryTables)
{
consumer.preConsumeMapping(highestMemberNumber + 1);
for (int i = 0; i < mmds.length; i++)
{
JavaTypeMapping fieldMapping = memberMappingsMap.get(mmds[i]);
if (fieldMapping != null)
{
if (!mmds[i].isPrimaryKey())
{
consumer.consumeMapping(fieldMapping, mmds[i]);
}
}
}
}
/**
* Accessor for a mapping for a surrogate column (if present).
* @param colType The type of the surrogate column
* @param consumer Consumer for the mappings
**/
final public void provideSurrogateMapping(SurrogateColumnType colType, MappingConsumer consumer)
{
consumer.preConsumeMapping(highestMemberNumber + 1);
if (colType == SurrogateColumnType.DATASTORE_ID)
{
if (getIdentityType() == IdentityType.DATASTORE)
{
consumer.consumeMapping(datastoreIdMapping, MappingType.DATASTORE_ID);
}
}
else if (colType == SurrogateColumnType.DISCRIMINATOR)
{
JavaTypeMapping discrimMapping = getSurrogateMapping(SurrogateColumnType.DISCRIMINATOR, false);
if (discrimMapping != null)
{
consumer.consumeMapping(discrimMapping, MappingType.DISCRIMINATOR);
}
}
else if (colType == SurrogateColumnType.MULTITENANCY)
{
if (multitenancyMapping != null)
{
consumer.consumeMapping(multitenancyMapping, MappingType.MULTITENANCY);
}
}
else if (colType == SurrogateColumnType.VERSION)
{
JavaTypeMapping versionMapping = getSurrogateMapping(SurrogateColumnType.VERSION, false);
if (versionMapping != null)
{
consumer.consumeMapping(versionMapping, MappingType.VERSION);
}
}
else if (colType == SurrogateColumnType.SOFTDELETE)
{
if (softDeleteMapping != null)
{
consumer.consumeMapping(softDeleteMapping, MappingType.SOFTDELETE);
}
}
else if (colType == SurrogateColumnType.CREATE_USER)
{
if (createUserMapping != null)
{
consumer.consumeMapping(createUserMapping, MappingType.CREATEUSER);
}
}
else if (colType == SurrogateColumnType.CREATE_TIMESTAMP)
{
if (createTimestampMapping != null)
{
consumer.consumeMapping(createTimestampMapping, MappingType.CREATETIMESTAMP);
}
}
else if (colType == SurrogateColumnType.UPDATE_USER)
{
if (updateUserMapping != null)
{
consumer.consumeMapping(updateUserMapping, MappingType.UPDATEUSER);
}
}
else if (colType == SurrogateColumnType.UPDATE_TIMESTAMP)
{
if (updateTimestampMapping != null)
{
consumer.consumeMapping(updateTimestampMapping, MappingType.UPDATETIMESTAMP);
}
}
else
{
// TODO Support other types
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy