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

org.datanucleus.store.rdbms.mapping.java.EmbeddedMapping Maven / Gradle / Ivy

/**********************************************************************
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.mapping.java;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.api.ApiAdapter;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.ColumnMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.metadata.EmbeddedMetaData;
import org.datanucleus.metadata.FieldPersistenceModifier;
import org.datanucleus.metadata.FieldRole;
import org.datanucleus.metadata.InheritanceMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.rdbms.mapping.MappingManager;
import org.datanucleus.store.rdbms.mapping.datastore.DatastoreMapping;
import org.datanucleus.store.rdbms.table.Column;
import org.datanucleus.store.rdbms.table.Table;
import org.datanucleus.util.NucleusLogger;

/**
 * Mapping for an embedded PC object. 
 * The PC object can be embedded directly (1-1 relation) or be the element of 
 * a collection, or be the key or value of a map.
 */
public abstract class EmbeddedMapping extends SingleFieldMapping
{
    protected DiscriminatorMetaData discrimMetaData;

    /** Mapping for a discriminator (when supporting inherited embedded objects. */
    protected DiscriminatorMapping discrimMapping;

    /** Mappings of the fields of the embedded PC. */
    protected List javaTypeMappings;

    /** ClassLoader resolver */
    protected ClassLoaderResolver clr;

    /** EmbeddedMetaData for the object being embedded. */
    protected EmbeddedMetaData emd;

    /** Type name for the object being embedded. */
    protected String typeName;

    /** Type of PC object. Corresponds to the values in StateManagerImpl. */
    protected short objectType = -1;

    /** MetaData for the embedded class. */
    protected AbstractClassMetaData embCmd = null;

    /**
     * Initialize this JavaTypeMapping with the given DatastoreAdapter for the given FieldMetaData.
     * @param table The datastore container storing this mapping (if any)
     * @param clr the ClassLoaderResolver
     * @param fmd FieldMetaData for the field to be mapped (if any)
     *  
     * @throws NucleusException
     */
    public void initialize(AbstractMemberMetaData fmd, Table table, ClassLoaderResolver clr)
    {
        throw new NucleusException("subclass must override this method").setFatal();
    }
    
    /**
     * Initialize this JavaTypeMapping with the given DatastoreAdapter for the given MetaData.
     * @param fmd metadata for the field
     * @param table Table for persisting this field
     * @param clr The ClassLoaderResolver
     * @param emd Embedded MetaData for the object being embedded
     * @param typeName type of the embedded PC object
     * @param objectType Type of the PC object being embedded (see StateManagerImpl object types)
     */
    public void initialize(AbstractMemberMetaData fmd, Table table, ClassLoaderResolver clr,
        EmbeddedMetaData emd, String typeName, int objectType)
    {
    	super.initialize(fmd, table, clr);
        this.clr = clr;
        this.emd = emd;
        this.typeName = typeName;
        this.objectType = (short) objectType;

        // Find the MetaData for the embedded PC class
        MetaDataManager mmgr = table.getStoreManager().getMetaDataManager();
        AbstractClassMetaData rootEmbCmd = mmgr.getMetaDataForClass(typeName, clr);
        if (rootEmbCmd == null)
        {
            // Not found so must be an interface
            if (fmd != null)
            {
                // Try using the fieldTypes on the field/property - we support it if only 1 implementation
                String[] fieldTypes = fmd.getFieldTypes();
                if (fieldTypes != null && fieldTypes.length == 1)
                {
                    rootEmbCmd = mmgr.getMetaDataForClass(fieldTypes[0], clr);
                }
                else if (fieldTypes != null && fieldTypes.length > 1)
                {
                    // TODO Cater for multiple implementations
                    throw new NucleusUserException("Field " + fmd.getFullFieldName() + 
                        " is a reference field that is embedded with multiple possible implementations. " +
                        "DataNucleus doesnt support embedded reference fields that have more than 1 implementation");
                }
            }

            if (rootEmbCmd == null)
            {
                // Try a persistent interface
                rootEmbCmd = mmgr.getMetaDataForInterface(clr.classForName(typeName), clr);
                if (rootEmbCmd == null && fmd.getFieldTypes() != null && fmd.getFieldTypes().length == 1)
                {
                    // No MetaData for the type so try "fieldType" specified on the field
                    rootEmbCmd = mmgr.getMetaDataForInterface(clr.classForName(fmd.getFieldTypes()[0]), clr);
                }
            }
        }

        embCmd = rootEmbCmd;

        AbstractMemberMetaData[] embFmds;
        if (emd == null && rootEmbCmd.isEmbeddedOnly())
        {
            // No  block yet the class is defined as embedded-only so just use its own definition of fields
            embFmds = rootEmbCmd.getManagedMembers();
        }
        else
        {
            //  block so use those field definitions
            embFmds = emd.getMemberMetaData();
        }

        String[] subclasses = mmgr.getSubclassesForClass(rootEmbCmd.getFullClassName(), true);
        if (subclasses != null && subclasses.length > 0)
        {
            if (rootEmbCmd.hasDiscriminatorStrategy())
            {
                // Fabricate a DiscriminatorMetaData to use for the embedded object
                discrimMetaData = new DiscriminatorMetaData();
                InheritanceMetaData embInhMd = new InheritanceMetaData();
                embInhMd.setParent(rootEmbCmd);
                discrimMetaData.setParent(embInhMd);

                // Set strategy based on the inheritance of the embedded object, otherwise class name.
                DiscriminatorMetaData dismd = rootEmbCmd.getDiscriminatorMetaDataRoot();
                if (dismd.getStrategy() != null && dismd.getStrategy() != DiscriminatorStrategy.NONE)
                {
                    discrimMetaData.setStrategy(dismd.getStrategy());
                }
                else
                {
                    discrimMetaData.setStrategy(DiscriminatorStrategy.CLASS_NAME); // Fallback to class name
                }

                // Set column for discriminator
                ColumnMetaData disColmd = new ColumnMetaData();
                disColmd.setAllowsNull(Boolean.TRUE);

                DiscriminatorMetaData embDismd = emd.getDiscriminatorMetaData();
                if (embDismd != null && embDismd.getColumnMetaData() != null)
                {
                    disColmd.setName(embDismd.getColumnMetaData().getName());
                }
                else
                {
                    ColumnMetaData colmd = dismd.getColumnMetaData();
                    if (colmd != null && colmd.getName() != null)
                    {
                        disColmd.setName(colmd.getName());
                    }
                }
                discrimMetaData.setColumnMetaData(disColmd);

                discrimMapping = DiscriminatorMapping.createDiscriminatorMapping(table, discrimMetaData);
                addDatastoreMapping(discrimMapping.getDatastoreMapping(0));
            }
            else
            {
                NucleusLogger.PERSISTENCE.info("Member " + mmd.getFullFieldName() + " is embedded and the type " + 
                    "(" + rootEmbCmd.getFullClassName() + ") has potential subclasses." +
                    " Impossible to detect which is stored embedded. Add a discriminator to the embedded type");
            }
        }

        // Add all fields of the embedded class (that are persistent)
        int[] pcFieldNumbers = rootEmbCmd.getAllMemberPositions();
        for (int i=0;i 0)
        {
            for (int i=0;i 0 &&
                    mapping.getMemberMetaData().getColumnMetaData()[0].getName().equals(nullColumn))
                {
                    // Try to cater for user having an integer based column and value
                    if (mapping instanceof IntegerMapping ||
                        mapping instanceof BigIntegerMapping ||
                        mapping instanceof LongMapping ||
                        mapping instanceof ShortMapping)
                    {
                        Object convertedValue = null;
                        try
                        {
                            if (mapping instanceof IntegerMapping ||
                                mapping instanceof ShortMapping)
                            {
                                convertedValue = Integer.valueOf(nullValue);
                            }
                            else if (mapping instanceof LongMapping ||
                                mapping instanceof BigIntegerMapping)
                            {
                                convertedValue = Long.valueOf(nullValue);
                            }
                        }
                        catch (Exception e)
                        {
                        }
                        mapping.setObject(ec, ps, posMapping, convertedValue);
                    }
                    else
                    {
                        mapping.setObject(ec, ps, posMapping, nullValue);
                    }
                }
                else
                {
                    if (mapping.getNumberOfDatastoreMappings() > 0)
                    {
                        mapping.setObject(ec, ps, posMapping, null);
                    }
                }
            }
        }
        else
        {
            ApiAdapter api = ec.getApiAdapter();
            if (!api.isPersistable(value))
            {
                throw new NucleusException(LOCALISER_RDBMS.msg("041016", value.getClass(), value)).setFatal();
            }

            AbstractClassMetaData embCmd = ec.getMetaDataManager().getMetaDataForClass(value.getClass(), ec.getClassLoaderResolver());
            ObjectProvider embSM = ec.findObjectProvider(value);
            if (embSM == null || api.getExecutionContext(value) == null)
            {
                // Assign a StateManager to manage our embedded object
                embSM = ec.newObjectProviderForEmbedded(value, false, ownerOP, ownerFieldNumber);
                embSM.setPcObjectType(objectType);
            }

            int n = 0;

            if (discrimMapping != null)
            {
                if (discrimMetaData.getStrategy() == DiscriminatorStrategy.CLASS_NAME)
                {
                    discrimMapping.setObject(ec, ps, new int[]{param[n]}, value.getClass().getName());
                }
                else if (discrimMetaData.getStrategy() == DiscriminatorStrategy.VALUE_MAP)
                {
                    DiscriminatorMetaData valueDismd = embCmd.getInheritanceMetaData().getDiscriminatorMetaData();
                    discrimMapping.setObject(ec, ps, new int[]{param[n]}, valueDismd.getValue());
                }
                n++;
            }

            for (int i=0; i= 0)
                {
                    // Member is present in this embedded type
                    Object fieldValue = embSM.provideField(embAbsFieldNum);
                    if (mapping instanceof EmbeddedPCMapping)
                    {
                        mapping.setObject(ec, ps, posMapping, fieldValue, embSM, embAbsFieldNum);
                    }
                    else
                    {
                        if (mapping.getNumberOfDatastoreMappings() > 0)
                        {
                            mapping.setObject(ec, ps, posMapping, fieldValue);
                        }
                    }
                }
                else
                {
                    mapping.setObject(ec, ps, posMapping, null);
                }
            }
        }
    }

    /**
     * Accessor for the embedded object from the result set
     * @param ec ExecutionContext
     * @param rs The ResultSet
     * @param param Array of param numbers in the ResultSet for the fields of this object
     * @return The embedded object
     */
    public Object getObject(ExecutionContext ec, ResultSet rs, int[] param)
    {
        return getObject(ec, rs, param, null, -1);
    }

    /**
     * Accessor for the embedded object from the result set
     * @param ec ExecutionContext
     * @param rs The ResultSet
     * @param param Array of param numbers in the ResultSet for the fields of this object
     * @param ownerOP ObjectProvider of the owning object containing this embedded object
     * @param ownerFieldNumber Field number in the owning object where this is stored
     * @return The embedded object
     */
    public Object getObject(ExecutionContext ec, ResultSet rs, int[] param, ObjectProvider ownerOP, int ownerFieldNumber)
    {
        Object value = null;

        int n = 0;

        // Determine the type of the embedded object
        AbstractClassMetaData embCmd = this.embCmd;
        if (discrimMapping != null)
        {
            Object discrimValue = discrimMapping.getObject(ec, rs, new int[]{param[n]});
            String className = ec.getMetaDataManager().getClassNameFromDiscriminatorValue((String)discrimValue, discrimMetaData);
            embCmd = storeMgr.getMetaDataManager().getMetaDataForClass(className, clr);
            n++;
        }

        // Create a PersistenceCapable to put the values into
        Class embeddedType = clr.classForName(embCmd.getFullClassName());
        if (mmd.getFieldTypes() != null && mmd.getFieldTypes().length > 0)
        {
            // Embedded type has field-type defined so use that as our embedded type
            embeddedType = ec.getClassLoaderResolver().classForName(mmd.getFieldTypes()[0]);
        }
        ObjectProvider embOP = ec.newObjectProviderForHollow(embeddedType, (Object)null);
        embOP.setPcObjectType(objectType);
        value = embOP.getObject();

        String nullColumn = null;
        String nullValue = null;
        if (emd != null)
        {
            nullColumn = emd.getNullIndicatorColumn();
            nullValue = emd.getNullIndicatorValue();
        }

        // Populate the field values
        for (int i=0; i= 0)
            {
                // Mapping for field that is present in this embedded type, so set the field
                if (mapping instanceof EmbeddedPCMapping)
                {
                    // We have a nested embedded
                    int numSubParams = mapping.getNumberOfDatastoreMappings();
                    int[] subParam = new int[numSubParams];
                    int k = 0;
                    for (int j=n;j= 0)
                {
                    embOP.replaceField(ownerFieldNumberInElement, ownerOP.getObject());
                }
            }
        }
        
        // Register the owner-embedded ObjectProvider relation now we have values set
        if (value != null && ownerOP != null)
        {
            ec.registerEmbeddedRelation(ownerOP, ownerFieldNumber, embOP);
        }

        return value;
    }

    /**
     * Accessor for the Java type being represented here.
     * @return The Java type
     */
    public Class getJavaType()
    {
        return clr.classForName(typeName);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy