/** * Method to add a multi-tenancy discriminator column. * * @param colmd Metadata defining the column required */ protected void addMultitenancyMapping(ColumnMetaData colmd) { String colName = "TENANT_ID"; if (colmd != null && colmd.getName() != null) { colName = colmd.getName(); } String typeName = String.class.getName(); if (colmd != null && colmd.getJdbcType() != null) { if (colmd.getJdbcType() == JdbcType.INTEGER) { typeName = Integer.class.getName(); } } if (typeName.equals(Integer.class.getName())) { tenantMapping = new IntegerMapping(); } else { tenantMapping = new StringMapping(); } tenantMapping.setTable(this); tenantMapping.initialize(storeMgr, typeName); Column tenantColumn = addColumn( typeName, storeMgr.getIdentifierFactory().newIdentifier(IdentifierType.COLUMN, colName), tenantMapping, colmd); storeMgr.getMappingManager().createDatastoreMapping(tenantMapping, tenantColumn, typeName); logMapping("MULTITENANCY", tenantMapping); }
/** * 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 <join> 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()) { logMapping(mmd.getFullFieldName() + ".[OWNER]", 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 < keyMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = keyMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping); } if (valueKeyFieldName != null && isEmbeddedKeyPC()) { // Key (PC) is embedded and value is a field of the key EmbeddedKeyPCMapping embMapping = (EmbeddedKeyPCMapping) keyMapping; valueMapping = embMapping.getJavaTypeMapping(valueKeyFieldName); } } else { // Key = PC ColumnMetaData[] keyColmd = null; KeyMetaData keymd = mmd.getKeyMetaData(); if (keymd != null && keymd.getColumnMetaData() != null && keymd.getColumnMetaData().length > 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 < keyMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = keyMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[KEY]", keyMapping); } } // Add value mapping boolean valuePC = (mmd.hasMap() && mmd.getMap().valueIsPersistent()); Class valueCls = clr.classForName(mapmd.getValueType()); if (valueKeyFieldName != null && isEmbeddedKeyPC()) { // Added in key code } else if (isSerialisedValue() || isEmbeddedValuePC() || (isEmbeddedValue() && !valuePC) || ClassUtils.isReferenceType(valueCls)) { // Value = PC(embedded), PC(serialised), Non-PC(serialised), Non-PC(embedded), Reference valueMapping = storeMgr.getMappingManager().getMapping(this, mmd, clr, FieldRole.ROLE_MAP_VALUE); if (mmd.getContainer().allowNulls() == Boolean.TRUE) { // Make all value col(s) nullable so we can store null elements for (int i = 0; i < valueMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = valueMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping); } if (keyValueFieldName != null && isEmbeddedValuePC()) { // Value (PC) is embedded and key is a field of the value EmbeddedValuePCMapping embMapping = (EmbeddedValuePCMapping) valueMapping; keyMapping = embMapping.getJavaTypeMapping(keyValueFieldName); } } else { // Value = PC ColumnMetaData[] valueColmd = null; ValueMetaData valuemd = mmd.getValueMetaData(); if (valuemd != null && valuemd.getColumnMetaData() != null && valuemd.getColumnMetaData().length > 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 < valueMapping.getNumberOfDatastoreMappings(); i++) { Column elementCol = valueMapping.getDatastoreMapping(i).getColumn(); elementCol.setNullable(true); } } if (NucleusLogger.DATASTORE.isDebugEnabled()) { logMapping(mmd.getFullFieldName() + ".[VALUE]", valueMapping); } } // Add order mapping if required boolean orderRequired = false; if (mmd.getOrderMetaData() != null) { // User requested order column so add one orderRequired = true; } else if (requiresPrimaryKey() && !pkColsSpecified) { // PK is required so maybe need to add an index to form the PK if (isEmbeddedKeyPC()) { if (mmd.getMap().getKeyClassMetaData(clr, storeMgr.getMetaDataManager()).getIdentityType() != IdentityType.APPLICATION) { // Embedded key PC with datastore id so we need an index to form the PK orderRequired = true; } } else if (isSerialisedKey()) { // Serialised key, so need an index to form the PK orderRequired = true; } else if (keyMapping instanceof ReferenceMapping) { // ReferenceMapping, so have order if more than 1 implementation ReferenceMapping refMapping = (ReferenceMapping) keyMapping; if (refMapping.getJavaTypeMapping().length > 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) { // No column name so use default orderColmd = new ColumnMetaData(orderColmd); DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd); orderColmd.setName(id.getName()); } } else { // No column name so use default DatastoreIdentifier id = storeMgr.getIdentifierFactory().newIndexFieldIdentifier(mmd); orderColmd = new ColumnMetaData(); orderColmd.setName(id.getName()); } 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()) { logMapping(mmd.getFullFieldName() + ".[ORDER]", 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 internal rules if (orderRequired) { // Order column specified so owner+order are the PK orderMapping.getDatastoreMapping(0).getColumn().setPrimaryKey(); } else { // No order column specified so owner+key are the PK for (int i = 0; i < keyMapping.getNumberOfDatastoreMappings(); i++) { keyMapping.getDatastoreMapping(i).getColumn().setPrimaryKey(); } } } } if (NucleusLogger.DATASTORE_SCHEMA.isDebugEnabled()) { NucleusLogger.DATASTORE_SCHEMA.debug(Localiser.msg("057023", this)); } storeMgr.registerTableInitialized(this); state = TABLE_STATE_INITIALIZED; }
/** * 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(IdentityStrategy.CUSTOM)) { strategyName = cmd.getIdentityMetaData().getValueStrategy().getCustomName(); } if (strategyName != null && IdentityStrategy.NATIVE.toString().equals(strategyName)) { strategyName = storeMgr.getStrategyForNative(cmd, -1); } // Check the value generator type being stored Class valueGeneratedType = Long.class; if (strategyName != null && IdentityStrategy.IDENTITY.toString().equals(strategyName)) { valueGeneratedType = dba.getAutoIncrementJavaTypeForType(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"); } } try { // Create generator so we can find the generated type // a). Try as unique generator first AbstractGenerator generator = (AbstractGenerator) storeMgr .getNucleusContext() .getPluginManager() .createExecutableExtension( "org.datanucleus.store_valuegenerator", new String[] {"name", "unique"}, new String[] {strategyName, "true"}, "class-name", new Class[] {String.class, Properties.class}, new Object[] {null, null}); if (generator == null) { // b). Try as datastore-specific generator generator = (AbstractGenerator) storeMgr .getNucleusContext() .getPluginManager() .createExecutableExtension( "org.datanucleus.store_valuegenerator", new String[] {"name", "datastore"}, new String[] {strategyName, storeMgr.getStoreManagerKey()}, "class-name", new Class[] {String.class, Properties.class}, new Object[] {null, null}); } try { if (generator != null) { ParameterizedType parameterizedType = (ParameterizedType) generator.getClass().getGenericSuperclass(); valueGeneratedType = (Class) parameterizedType.getActualTypeArguments()[0]; if (valueGeneratedType == null) { // Use getStorageClass method if available valueGeneratedType = (Class) generator.getClass().getMethod("getStorageClass").invoke(null); } } } catch (Exception e) { } } catch (Exception e) { NucleusLogger.VALUEGENERATION.warn( "Error obtaining generator for strategy=" + strategyName, e); } storeMgr .getMappingManager() .createDatastoreMapping(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 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 < cmd.getPKMemberPositions().length; i++) { AbstractMemberMetaData mmd = cmd.getMetaDataForManagedMemberAtAbsolutePosition(cmd.getPKMemberPositions()[i]); JavaTypeMapping mapping = refTable.getMemberMapping(mmd); if (mapping == null) { // probably due to invalid metadata defined by the user throw new NucleusUserException( "Cannot find mapping for field " + mmd.getFullFieldName() + " in table " + refTable.toString() + " " + StringUtils.collectionToString(refTable.getColumns())); } JavaTypeMapping masterMapping = storeMgr.getMappingManager().getMapping(clr.classForName(mapping.getType())); masterMapping.setMemberMetaData(mmd); // Update field info in mapping masterMapping.setTable(this); pkMappings[i] = masterMapping; // Loop through each id column in the reference table and add the same here // applying the required names from the columnContainer for (int j = 0; j < mapping.getNumberOfDatastoreMappings(); j++) { JavaTypeMapping m = masterMapping; Column refColumn = mapping.getDatastoreMapping(j).getColumn(); if (mapping instanceof PersistableMapping) { m = storeMgr .getMappingManager() .getMapping(clr.classForName(refColumn.getJavaTypeMapping().getType())); ((PersistableMapping) masterMapping).addJavaTypeMapping(m); } ColumnMetaData userdefinedColumn = null; if (userdefinedCols != null) { for (int k = 0; k < userdefinedCols.length; k++) { if (refColumn.getIdentifier().toString().equals(userdefinedCols[k].getTarget())) { userdefinedColumn = userdefinedCols[k]; break; } } if (userdefinedColumn == null && nextUserdefinedCol < userdefinedCols.length) { userdefinedColumn = userdefinedCols[nextUserdefinedCol++]; } } // Add this application identity column Column idColumn = null; if (userdefinedColumn != null) { // User has provided a name for this column // Currently we only use the column namings from the users definition but we could easily // take more of their details. idColumn = addColumn( refColumn.getStoredJavaType(), storeMgr .getIdentifierFactory() .newIdentifier(IdentifierType.COLUMN, userdefinedColumn.getName()), m, refColumn.getColumnMetaData()); } else { // No name provided so take same as superclass idColumn = addColumn( refColumn.getStoredJavaType(), refColumn.getIdentifier(), m, refColumn.getColumnMetaData()); } if (mapping.getDatastoreMapping(j).getColumn().getColumnMetaData() != null) { refColumn.copyConfigurationTo(idColumn); } idColumn.setPrimaryKey(); // Set the column type based on the field.getType() getStoreManager() .getMappingManager() .createDatastoreMapping(m, idColumn, refColumn.getJavaTypeMapping().getType()); } // Update highest field number if this is higher int absoluteFieldNumber = mmd.getAbsoluteFieldNumber(); if (absoluteFieldNumber > highestMemberNumber) { highestMemberNumber = absoluteFieldNumber; } } }