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

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

There is a newer version: 6.0.8
Show newest version
/**********************************************************************
Copyright (c) 2002 Kelly Grizzle 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:
2002 Mike Martin - unknown changes
2003 Andy Jefferson - added localiser
2003 Andy Jefferson - replaced TableMetadata with identifier
2004 Marco Schulze - added advance-check via TypeManager.isSupportedType(...)
2005 Andy Jefferson - only create ADPT column when necessary
2005 Andy Jefferson - enabled ability to have embedded keys/values
    ...
**********************************************************************/
package org.datanucleus.store.rdbms.table;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.ForeignKeyMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.IndexMetaData;
import org.datanucleus.metadata.KeyMetaData;
import org.datanucleus.metadata.MapMetaData;
import org.datanucleus.metadata.PrimaryKeyMetaData;
import org.datanucleus.metadata.UniqueMetaData;
import org.datanucleus.metadata.ValueMetaData;
import org.datanucleus.store.rdbms.exceptions.NoTableManagedException;
import org.datanucleus.store.rdbms.RDBMSPropertyNames;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.identifier.DatastoreIdentifier;
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.mapping.java.EmbeddedKeyPCMapping;
import org.datanucleus.store.rdbms.mapping.java.EmbeddedValuePCMapping;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.mapping.java.PersistableMapping;
import org.datanucleus.store.rdbms.mapping.java.ReferenceMapping;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.NucleusLogger;

/**
 * Representation of a join table for a Map. A Map covers a wide range of possibilities
 * in terms of whether it allows duplicates or not, whether it allows nulls or not, whether it supports
 * ordering via indexes etc. Consequently the join table can vary depending on the required capabilities.
 * 

JoinTable Mappings

*

* The join table consists of the following mappings :- *

    *
  • ownerMapping linking back to the owning class with the Collection.
  • *
  • keyMapping either being an FK link to the key table or being an embedded/serialised key * stored wholely in this table.
  • *
  • valueMapping either being an FK link to the value table or being an embedded/serialised value * stored wholely in this table.
  • *
  • orderMapping which may be null, or otherwise stores an index for the keys. * This is either to provide uniqueness or ordering (and part of the PK).
  • *
*

*/ public class MapTable extends JoinTable implements DatastoreMap { /** Mapping to the key object. */ private JavaTypeMapping keyMapping; /** Mapping to the value object. */ private JavaTypeMapping valueMapping; /** * Mapping to allow ordering (of keys) or to allow duplicates. Can be used when the key is not suitable * for use as part of the PK and a PK is required for this join table. */ private JavaTypeMapping orderMapping; /** Map of field mappings when containing an embedded PC key. Keyed by the FieldMetaData of the field. */ protected Map embeddedKeyMappingsMap; /** Map of field mappings when containing an embedded PC value. Keyed by the FieldMetaData of the field. */ protected Map embeddedValueMappingsMap; /** * Constructor. * @param tableName Identifier name of the table * @param mmd MetaData for the field/property of the owner * @param storeMgr The Store Manager managing these tables. */ public MapTable(DatastoreIdentifier tableName, AbstractMemberMetaData mmd, RDBMSStoreManager storeMgr) { super(tableName, mmd, storeMgr); } /** * Method to initialise the table definition. * @param clr The ClassLoaderResolver */ public void initialize(ClassLoaderResolver clr) { assertIsUninitialized(); MapMetaData mapmd = mmd.getMap(); if (mapmd == null) { throw new NucleusUserException(LOCALISER.msg("057017",mmd)); } PrimaryKeyMetaData pkmd = (mmd.getJoinMetaData() != null ? mmd.getJoinMetaData().getPrimaryKeyMetaData() : null); boolean pkColsSpecified = (pkmd != null && pkmd.getColumnMetaData() != null); boolean pkRequired = requiresPrimaryKey(); // Add owner mapping ColumnMetaData[] ownerColmd = null; if (mmd.getJoinMetaData() != null && mmd.getJoinMetaData().getColumnMetaData() != null && mmd.getJoinMetaData().getColumnMetaData().length > 0) { // Column mappings defined at this side (1-N, M-N) // When specified at this side they use the tag ownerColmd = mmd.getJoinMetaData().getColumnMetaData(); } ownerMapping = ColumnCreator.createColumnsForJoinTables(clr.classForName(ownerType), mmd, ownerColmd, storeMgr, this, pkRequired, false, FieldRole.ROLE_OWNER, clr); if (NucleusLogger.DATASTORE.isDebugEnabled()) { debugMapping(ownerMapping); } String keyValueFieldName = (mmd.getKeyMetaData() != null ? mmd.getKeyMetaData().getMappedBy() : null); String valueKeyFieldName = (mmd.getValueMetaData() != null ? mmd.getValueMetaData().getMappedBy() : null); // Add key mapping boolean keyPC = (mmd.hasMap() && mmd.getMap().keyIsPersistent()); Class keyCls = clr.classForName(mapmd.getKeyType()); if (keyValueFieldName != null && isEmbeddedValuePC()) { // Added in value code } else if (isSerialisedKey() || isEmbeddedKeyPC() || (isEmbeddedKey() && !keyPC) || ClassUtils.isReferenceType(keyCls)) { // Key = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference keyMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_KEY); if (Boolean.TRUE.equals(mmd.getContainer().allowNulls())) { // Make all key col(s) nullable so we can store null elements for (int i=0;i 0) { // Column mappings defined at this side (1-N, M-N) keyColmd = keymd.getColumnMetaData(); } keyMapping = ColumnCreator.createColumnsForJoinTables(keyCls, mmd, keyColmd, storeMgr, this, false, false, FieldRole.ROLE_MAP_KEY, clr); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all key col(s) nullable so we can store null elements for (int i=0;i 0) { // Column mappings defined at this side (1-N, M-N) valueColmd = valuemd.getColumnMetaData(); } valueMapping = ColumnCreator.createColumnsForJoinTables(clr.classForName(mapmd.getValueType()), mmd, valueColmd, storeMgr, this, false, true, FieldRole.ROLE_MAP_VALUE, clr); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all value col(s) nullable so we can store null elements for (int i=0;i 1) { orderRequired = true; } } else if (!(keyMapping instanceof PersistableMapping)) { // Non-PC, so depends if the key column can be used as part of a PK // TODO This assumes the keyMapping has a single column but what if it is Color with 4 cols? Column elementCol = keyMapping.getDatastoreMapping(0).getColumn(); if (!storeMgr.getDatastoreAdapter().isValidPrimaryKeyType(elementCol.getJdbcType())) { // Not possible to use this Non-PC type as part of the PK orderRequired = true; } } } if (orderRequired) { // Order/Adapter (index) column is required (integer based) ColumnMetaData orderColmd = null; if (mmd.getOrderMetaData() != null && mmd.getOrderMetaData().getColumnMetaData() != null && mmd.getOrderMetaData().getColumnMetaData().length > 0) { // Specified "order" column info orderColmd = mmd.getOrderMetaData().getColumnMetaData()[0]; if (orderColmd.getName() == null) { orderColmd = new ColumnMetaData(orderColmd); if (mmd.hasExtension("adapter-column-name")) { // Specified "extension" column name // TODO Is this needed? The user can just specify orderColmd.setName(mmd.getValueForExtension("adapter-column-name")); } else { // No column name so use default DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd); orderColmd.setName(id.getIdentifierName()); } } } else { if (mmd.hasExtension("adapter-column-name")) { // Specified "extension" column name // TODO Is this needed? The user can just specify orderColmd = new ColumnMetaData(); orderColmd.setName(mmd.getValueForExtension("adapter-column-name")); } else { // No column name so use default DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd); orderColmd = new ColumnMetaData(); orderColmd.setName(id.getIdentifierName()); } } orderMapping = storeMgr.getMappingManager().getMapping(int.class); // JDO2 spec [18.5] order column is assumed to be "int" ColumnCreator.createIndexColumn(orderMapping, storeMgr, clr, this, orderColmd, pkRequired && !pkColsSpecified); if (NucleusLogger.DATASTORE.isDebugEnabled()) { debugMapping(orderMapping); } } // Define primary key of the join table (if any) if (pkRequired) { if (pkColsSpecified) { // Apply the users PK specification applyUserPrimaryKeySpecification(pkmd); } else { // Define PK using JPOX rules if (orderRequired) { // Order column specified so owner+order are the PK orderMapping.getDatastoreMapping(0).getColumn().setAsPrimaryKey(); } else { // No order column specified so owner+key are the PK for (int i=0;i columns * @param pkmd MetaData for the primary key */ protected void applyUserPrimaryKeySpecification(PrimaryKeyMetaData pkmd) { ColumnMetaData[] pkCols = pkmd.getColumnMetaData(); for (int i=0;i from ForeignKeyMetaData fkmd = null; if (mmd.getJoinMetaData() != null) { fkmd = mmd.getJoinMetaData().getForeignKeyMetaData(); } if (fkmd != null || autoMode) { ForeignKey fk = new ForeignKey(ownerMapping,dba,referencedTable, true); fk.setForMetaData(fkmd); foreignKeys.add(fk); } } if (!isSerialisedValuePC()) { if (isEmbeddedValuePC()) { // Add any FKs for the fields of the (embedded) value EmbeddedValuePCMapping embMapping = (EmbeddedValuePCMapping)valueMapping; 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); } } } } else if (mmd.getMap().valueIsPersistent()) { // FK from join table to value table referencedTable = storeMgr.getDatastoreClass(mmd.getMap().getValueType(), clr); if (referencedTable != null) { // Take from ForeignKeyMetaData fkmd = null; if (mmd.getValueMetaData() != null) { fkmd = mmd.getValueMetaData().getForeignKeyMetaData(); } if (fkmd != null || autoMode) { ForeignKey fk = new ForeignKey(valueMapping, dba, referencedTable, true); fk.setForMetaData(fkmd); foreignKeys.add(fk); } } } } if (!isSerialisedKeyPC()) { if (isEmbeddedKeyPC()) { // Add any FKs for the fields of the (embedded) key EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping)keyMapping; 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); } } } } else if (mmd.getMap().keyIsPersistent()) { // FK from join table to key table referencedTable = storeMgr.getDatastoreClass(mmd.getMap().getKeyType(), clr); if (referencedTable != null) { // Take from ForeignKeyMetaData fkmd = null; if (mmd.getKeyMetaData() != null) { fkmd = mmd.getKeyMetaData().getForeignKeyMetaData(); } if (fkmd != null || autoMode) { ForeignKey fk = new ForeignKey(keyMapping, dba, referencedTable, true); fk.setForMetaData(fkmd); foreignKeys.add(fk); } } } } } catch (NoTableManagedException e) { // expected when no table exists } return foreignKeys; } /** * Accessor for the indices for this table. * This includes both the user-defined indices (via MetaData), and the ones required by * foreign keys (required by relationships). * @param clr The ClassLoaderResolver * @return The indices */ protected Set getExpectedIndices(ClassLoaderResolver clr) { // The indices required by foreign keys (BaseTable) Set indices = super.getExpectedIndices(clr); if (keyMapping instanceof EmbeddedKeyPCMapping) { // Add all indices required by fields of the embedded key EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping)keyMapping; for (int i=0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy