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

org.datanucleus.store.rdbms.query.PersistentClassROF Maven / Gradle / Ivy

There is a newer version: 6.0.9
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 Erik Bengtson - removed subclasses operation
2003 Andy Jefferson - comments, and update to returned class name
2003 Erik Bengtson - added getObjectByAid
2004 Erik Bengtson - throws an JDOObjectNotFoundException
2004 Erik Bengtson - removed unused variable and import
2004 Erik Bengtson - added support for ignoreCache
2004 Andy Jefferson - coding standards
2005 Andy Jefferson - added support for using discriminator to distinguish objects 
    ...
**********************************************************************/
package org.datanucleus.store.rdbms.query;

import java.lang.reflect.Modifier;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.ExecutionContext;
import org.datanucleus.FetchPlan;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.identity.IdentityUtils;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.DiscriminatorMetaData;
import org.datanucleus.metadata.IdentityType;
import org.datanucleus.metadata.InterfaceMetaData;
import org.datanucleus.metadata.VersionMetaData;
import org.datanucleus.state.ObjectProvider;
import org.datanucleus.store.FieldValues;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.schema.table.SurrogateColumnType;
import org.datanucleus.store.rdbms.fieldmanager.ResultSetGetter;
import org.datanucleus.util.ConcurrentReferenceHashMap;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.StringUtils;
import org.datanucleus.util.ConcurrentReferenceHashMap.ReferenceType;

/**
 * Result-object factory that takes a JDBC ResultSet, a results mapping, and creates a persistable object instance for each row in the ResultSet. 
 * We use information in the result set to determine the object type; this can be a discriminator column, or can be a special "NucleusType" column defined just for result processing.
 * @param  Type of the persistent object that this creates
 */
public final class PersistentClassROF extends AbstractROF
{
    /** Metadata for the (root) persistable candidate class. */
    protected final AbstractClassMetaData rootCmd;

    /** Persistent class that this factory will generate (may be the root class). */
    protected Class persistentClass;

    /** Mapping of the results to members of this class (and sub-objects). */
    protected StatementClassMapping resultMapping = null;

    protected ResultSetGetter resultSetGetter = null;

    /** Resolved classes for metadata / discriminator keyed by class names. */
    private Map resolvedClasses = new ConcurrentReferenceHashMap<>(1, ReferenceType.STRONG, ReferenceType.SOFT);

    /**
     * Constructor.
     * @param ec ExecutionContext
     * @param rs ResultSet being processed
     * @param ignoreCache Whether to ignore the cache
     * @param fp FetchPlan
     * @param resultMapping Mapping information for the result set and how it maps to the class
     * @param acmd MetaData for the (root) candidate class
     * @param persistentClass Class that this factory will create instances of (or subclasses)
     */
    public PersistentClassROF(ExecutionContext ec, ResultSet rs, boolean ignoreCache, FetchPlan fp, StatementClassMapping resultMapping, AbstractClassMetaData acmd, Class persistentClass)
    {
        super(ec, rs, ignoreCache, fp);

        this.resultMapping = resultMapping;
        this.rootCmd = acmd;
        this.persistentClass = persistentClass;
    }

    /* (non-Javadoc)
     * @see org.datanucleus.store.rdbms.query.ResultObjectFactory#getResultSet()
     */
    @Override
    public ResultSet getResultSet()
    {
        return rs;
    }

    /**
     * Method to convert the current ResultSet row into a persistable Object.
     * @return The persistable object.
     */
    public T getObject()
    {
        // Find the class of the returned object in this row of the ResultSet
        String className = null;
        boolean requiresInheritanceCheck = true;
        String discrimValue = null;

        // Used for reporting details of a failed class lookup by discriminator
        boolean hasDiscrimValue = false;
	    boolean foundClassByDiscrim = false;

        StatementMappingIndex discrimMapIdx = resultMapping.getMappingForMemberPosition(SurrogateColumnType.DISCRIMINATOR.getFieldNumber());
        if (discrimMapIdx != null)
        {
            // Discriminator mapping registered so use that
            try
            {
                discrimValue = rs.getString(discrimMapIdx.getColumnPositions()[0]);
                if (discrimValue == null)
                {
                    // Discriminator has no value so return null object
                    NucleusLogger.DATASTORE_RETRIEVE.debug("Value of discriminator is null so assuming object is null");
                    return null;
                }
                hasDiscrimValue = true;
                JavaTypeMapping discrimMapping = discrimMapIdx.getMapping();
                DiscriminatorMetaData dismd = (discrimMapping != null ? discrimMapping.getTable().getDiscriminatorMetaData() : null);
                className = ec.getMetaDataManager().getClassNameFromDiscriminatorValue(discrimValue, dismd);
                if (className != null)
                {
                	foundClassByDiscrim = true;
                }
                requiresInheritanceCheck = false;
            }
            catch (SQLException sqle)
            {
                NucleusLogger.DATASTORE_RETRIEVE.debug("Exception obtaining value of discriminator : " + sqle.getMessage());
            }
        }
        else if (resultMapping.getNucleusTypeColumnName() != null)
        {
            // Extract the object type using the NucleusType column (if available)
            try
            {
                className = rs.getString(resultMapping.getNucleusTypeColumnName());
                if (className == null)
                {
                    // Discriminator has no value so return null object
                    NucleusLogger.DATASTORE_RETRIEVE.debug("Value of determiner column is null so assuming object is null");
                    return null;
                }

                className = className.trim();
                requiresInheritanceCheck = false;
            }
            catch (SQLException sqle)
            {
                // NucleusType column not found so ignore
            }
        }

        ClassLoaderResolver clr = ec.getClassLoaderResolver();
        Class pcClassForObject = persistentClass;
        if (className != null)
        {
            Class cls = (Class) resolvedClasses.get(className);
            if (cls != null)
            {
                pcClassForObject = cls;
            }
            else
            {
                if (persistentClass.getName().equals(className))
                {
                    pcClassForObject = persistentClass;
                }
                else
                {
                    pcClassForObject = clr.classForName(className, persistentClass.getClassLoader());
                }
                resolvedClasses.put(className, pcClassForObject);
            }
        }
        if (requiresInheritanceCheck)
        {
            // Check if no instantiable subclasses
            String[] subclasses = ec.getMetaDataManager().getSubclassesForClass(pcClassForObject.getName(), false);
            if (subclasses == null || subclasses.length == 0)
            {
                requiresInheritanceCheck = false;
            }
        }

        String warnMsg = null;
        if (Modifier.isAbstract(pcClassForObject.getModifiers()))
        {
            // Persistent class is abstract so we can't create instances of that type!
            // This can happen if the user is using subclass-table and hasn't provided a discriminator in 
            // the table. Try going out one level and find a (single) concrete subclass 
            // TODO make this more robust and go out further
            String[] subclasses = ec.getMetaDataManager().getSubclassesForClass(pcClassForObject.getName(), false);
            if (subclasses != null)
            {
                Class concreteSubclass = null;
                int numConcreteSubclasses = 0;
                for (int i=0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy