Package org.jpox.store.rdbms.query

Source Code of org.jpox.store.rdbms.query.PersistentIDROF

/**********************************************************************
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.jpox.store.rdbms.query;

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

import org.jpox.ClassLoaderResolver;
import org.jpox.FetchPlan;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.metadata.InterfaceMetaData;
import org.jpox.metadata.VersionMetaData;
import org.jpox.store.FieldValues;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.StatementExpressionIndex;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.OIDMapping;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.store.rdbms.fieldmanager.ResultSetGetter;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.SoftValueMap;

/**
* ResultObjectFactory that takes a JDBC ResultSet and create a PersistenceCapable object instance for
* each row in the ResultSet. We use information in the result set to determine the object type :
* <UL>
* <LI><B>discriminator</B> - when the candidate class and all possible subclasses in the query
* are stored in the same table they are distinguished using a "discriminator" column that will
* either store the class name, or a value representing that type.</LI>
* <LI><B>JPOXMETADATA</B> - in all other cases JPOX adds an extra column to the SELECT that contains
* the class name of the object.</LI>
* </UL>
*
* @version $Revision: 1.19 $
*/
public final class PersistentIDROF implements ResultObjectFactory
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER = Localiser.getInstance("org.jpox.store.rdbms.Localisation",
        RDBMSManager.class.getClassLoader());

    /**
     * Table where the objects of the candidate class are stored.
     * If the candidate uses "subclass-table" inheritance then this will be one or the other and may cause issues!
     */
    private final DatastoreClass table;

    /** Whether to ignore the cache */
    private final boolean ignoreCache;

    /** Field numbers to populate. */
    protected int[] fieldNumbers;

    /** Mapping from ResultSet to field numbers and mappings. */
    protected StatementExpressionIndex[] statementExpressionIndex;

    /** Indices of datastore identity columns */
    protected final int[] datastoreIdentityExpressionIndex;

    /** Index of version column(s). */
    protected final int[] versionIndex;

    /** Flag whether we should use a discriminator column to distinguish object types (otherwise use JPOXMETADATA). */
    protected boolean discriminator;
   
    /** if the results have included a meta data column with the class name. */
    protected final boolean hasMetaDataInResults;

    /** Fetch Plan to use when loading fields (if any). */
    protected final FetchPlan fetchPlan;

    /** fieldnumbers taken from this MetaData. */
    protected final AbstractClassMetaData acmd;

    /** Resolved classes for metadata / discriminator keyed by class names. */
    private Map resolvedClasses = new SoftValueMap();

    /** StatementExpressionIndex keyed by full class name. */
    private Map stmtExprIndexes = new SoftValueMap();

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

    /**
     * Constructor.
     * @param table Table being selected from
     * @param fieldNumbers Numbers of the fields in the select. May be null if no fields are retrieved
     * @param acmd MetaData for the class
     * @param statementExpressionIndex Index of columns to statement columns
     * @param datastoreIdentityExpressionIndex index for datastore identity
     * @param versionIndex Indices of the version column(s)
     * @param ignoreCache Whether to ignore the cache
     * @param discriminator Whether we use a discriminator column to distinguish object
     *                      types, or whether we use a JPOXMETADATA column
     * @param hasMetaDataInResults whether we use JPOXMETADATA column   
     * @param fetchPlan the Fetch Plan
     * @param persistentClass Class that this factory will create instances of (or subclasses)
     */
    public PersistentIDROF(DatastoreClass table,
                           int[] fieldNumbers,
                           AbstractClassMetaData acmd,
                           StatementExpressionIndex[] statementExpressionIndex,
                           int[] datastoreIdentityExpressionIndex,
                           int[] versionIndex,
                           boolean ignoreCache,
                           boolean discriminator,
                           boolean hasMetaDataInResults,
                           FetchPlan fetchPlan,
                           Class persistentClass)
    {
        this.statementExpressionIndex = statementExpressionIndex;
        if (fieldNumbers == null)
        {
            this.fieldNumbers = new int[0];
        }
        else
        {
            this.fieldNumbers = fieldNumbers;
        }
        this.acmd = acmd;
        this.table = table;
        this.datastoreIdentityExpressionIndex = datastoreIdentityExpressionIndex;
        this.versionIndex = versionIndex;
        this.ignoreCache = ignoreCache;
        this.discriminator = discriminator;
        this.hasMetaDataInResults = hasMetaDataInResults;
        this.fetchPlan = fetchPlan;
        this.persistentClass = persistentClass;
    }

    /**
     * Method to update the persistent class that the result object factory requires.
     * This is used where we have an Extent(BaseClass) and we pass it to a JDOQL query but the query
     * has been specified with a candidate class that is a subclass. So we use this method to restrict
     * the results further.
     * @param cls The Class the result factory requires.
     */
    public void setPersistentClass(Class cls)
    {
        this.persistentClass = cls;
    }

    /**
     * Method to convert the current ResultSet row into an Object.
     * @param om The ObjectManager
     * @param rs The ResultSet from the Query.
     * @return The (persisted) object.
     */
    public Object getObject(final ObjectManager om, final Object rs)
    {
        // Find the class of the returned object in this row of the ResultSet
        boolean requiresInheritanceCheck = true;
        String className = null;
        if (discriminator)
        {
            // Extract the class type from the discriminator in the ResultSet
            className = RDBMSQueryUtils.getClassNameFromDiscriminatorResultSetRow(table, (ResultSet)rs, om);
        }
        else if (hasMetaDataInResults)
        {
            // Extract the object type using the JPOXMETADATA column (if available)
            try
            {
                className = RDBMSQueryUtils.getClassNameFromJPOXMetaDataResultSetRow((ResultSet)rs);
            }
            catch (Exception e)
            {
                // Do nothing
            }
        }

        ClassLoaderResolver clr = om.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);
            }
            requiresInheritanceCheck = false;
        }

        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 concrete subclass TODO make this more ribust and go out further
            String[] subclasses = om.getMetaDataManager().getSubclassesForClass(pcClassForObject.getName(), false);
            if (subclasses != null)
            {
                for (int i=0;i<subclasses.length;i++)
                {
                    Class subcls = clr.classForName(subclasses[i]);
                    if (!Modifier.isAbstract(subcls.getModifiers()))
                    {
                        JPOXLogger.PERSISTENCE.warn(LOCALISER.msg("052300",
                            pcClassForObject.getName(), subcls.getName()));
                        pcClassForObject = subcls;
                        break;
                    }
                    if (i == subclasses.length-1)
                    {
                        throw new JPOXUserException(LOCALISER.msg("052301",
                            pcClassForObject.getName()));
                    }
                }
            }
        }

        int[] fieldNumbers = new int[this.fieldNumbers.length];
        AbstractClassMetaData cmd = om.getMetaDataManager().getMetaDataForClass(pcClassForObject, clr);
        for (int i = 0; i < this.fieldNumbers.length; i++)
        {
            if (acmd instanceof InterfaceMetaData)
            {
                fieldNumbers[i] = cmd.getAbsolutePositionOfMember(acmd.getMetaDataForManagedMemberAtAbsolutePosition(this.fieldNumbers[i]).getName());
            }
            else
            {
                fieldNumbers[i] = cmd.getAbsolutePositionOfMember(acmd.getMetaDataForManagedMemberAtAbsolutePosition(this.fieldNumbers[i]).getClassName(),
                    acmd.getMetaDataForManagedMemberAtAbsolutePosition(this.fieldNumbers[i]).getName());
                if (fieldNumbers[i] == -1)
                {
                    fieldNumbers[i] = cmd.getAbsolutePositionOfMember(acmd.getMetaDataForManagedMemberAtAbsolutePosition(this.fieldNumbers[i]).getName());
                }
            }
        }

        // Extract the object from the ResultSet
        Object obj = null;
        if (cmd.getIdentityType() == IdentityType.APPLICATION)
        {
            obj = getObjectByAID(om, (ResultSet)rs, fieldNumbers, cmd, pcClassForObject, requiresInheritanceCheck);
        }
        else if (cmd.getIdentityType() == IdentityType.DATASTORE)
        {
            JavaTypeMapping mapping = new OIDMapping();
            mapping.initialize(table.getStoreManager().getDatastoreAdapter(), pcClassForObject.getName());
            mapping.addDataStoreMapping(table.getDataStoreObjectIdMapping().getDataStoreMapping(0));
            Object id = mapping.getObject(om, rs, datastoreIdentityExpressionIndex);
            if (fieldNumbers == null)
            {
                obj = om.findObject(id, false, requiresInheritanceCheck, null);
            }
            else
            {
              if (requiresInheritanceCheck)
              {
                    obj = getObjectById(om, (ResultSet)rs, fieldNumbers, cmd, id, null);
              }
              else
              {
                obj = getObjectById(om, (ResultSet)rs, fieldNumbers, cmd, id, pcClassForObject);
              }
            }
        }
        else if (cmd.getIdentityType() == IdentityType.NONDURABLE)
        {
            Object id = om.newObjectId(className, null);
            if (fieldNumbers == null)
            {
                obj = om.findObject(id, false, requiresInheritanceCheck, null);
            }
            else
            {
                obj = getObjectById(om, (ResultSet)rs, fieldNumbers, cmd, id, pcClassForObject);
            }
        }

        // Set the version of the object where possible
        int versionFieldNumber = -1;
        VersionMetaData vermd = table.getVersionMetaData();
        if (vermd != null && vermd.getFieldName() != null)
        {
            // Version stored in a normal field
            // TODO Should only do this if the version was just retrieved
            versionFieldNumber = acmd.getMetaDataForMember(vermd.getFieldName()).getAbsoluteFieldNumber();
            StateManager objSM = om.findStateManager(obj);
            Object verFieldValue = objSM.provideField(versionFieldNumber);
            if (verFieldValue != null)
            {
                objSM.setVersion(verFieldValue);
            }
        }

        if (versionIndex != null)
        {
            // Surrogate version column returned by query
            JavaTypeMapping mapping = table.getVersionMapping(true);
            Object version = mapping.getObject(om, rs, versionIndex);
            StateManager objSM = om.findStateManager(obj);
            objSM.setVersion(version);
        }

        return obj;
    }

    /**
     * Returns a PC instance from a ResultSet row with an application identity.
     * @param om ObjectManager
     * @param rs The ResultSet
     * @param fieldNumbers Numbers of the fields (of the class) found in the ResultSet
     * @param cmd MetaData for the class
     * @param pcClass PersistenceCapable class
     * @param requiresInheritanceCheck Whether we need to check the inheritance level of the returned object
     * @return The object with this application identity
     */
    private Object getObjectByAID(final ObjectManager om, final ResultSet rs, final int[] fieldNumbers,
            AbstractClassMetaData cmd, Class pcClass, boolean requiresInheritanceCheck)
    {
        StatementExpressionIndex[] indexes = (StatementExpressionIndex[]) stmtExprIndexes.get(cmd.getFullClassName());
        if (indexes == null)
        {
            indexes = new StatementExpressionIndex[cmd.getNoOfManagedMembers() + cmd.getNoOfInheritedManagedMembers()];
            for (int i = 0; i < fieldNumbers.length; i++)
            {
              indexes[fieldNumbers[i]] = statementExpressionIndex[this.fieldNumbers[i]];
            }
            stmtExprIndexes.put(cmd.getFullClassName(), indexes);
        }
        final StatementExpressionIndex[] stmtExprIndex = indexes;           
        return om.findObjectUsingAID(pcClass, new FieldValues()
        {
            public void fetchFields(StateManager sm)
            {
                sm.replaceFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex), false);
            }
            public void fetchNonLoadedFields(StateManager sm)
            {
                sm.replaceNonLoadedFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex));
            }
            public FetchPlan getFetchPlanForLoading()
            {
                return fetchPlan;
            }
        }, ignoreCache, requiresInheritanceCheck);
    }

    /**
     * Returns a PC instance from a ResultSet row with a datastore identity.
     * @param om ObjectManager
     * @param rs The ResultSet
     * @param fieldNumbers Numbers of the fields (of the class) found in the ResultSet
     * @param cmd MetaData for the class
     * @param oid The object id
     * @param pcClass The PersistenceCapable class (where we know the instance type required, null if not)
     * @return The Object
     */
    private Object getObjectById(final ObjectManager om, final ResultSet rs, final int[] fieldNumbers,
            AbstractClassMetaData cmd, Object oid, Class pcClass)
    {
        if (oid == null)
        {
            return null;
        }

        StatementExpressionIndex[] indexes = (StatementExpressionIndex[]) stmtExprIndexes.get(cmd.getFullClassName());
        if (indexes == null)
        {
            indexes = new StatementExpressionIndex[cmd.getNoOfManagedMembers() + cmd.getNoOfInheritedManagedMembers()];
            for (int i = 0; i < fieldNumbers.length; i++)
            {
                indexes[fieldNumbers[i]] = statementExpressionIndex[this.fieldNumbers[i]];
            }
            stmtExprIndexes.put(cmd.getFullClassName(), indexes);
        }
        final StatementExpressionIndex[] stmtExprIndex = indexes;
        if (pcClass == null)
        {
            return om.findObject(oid, new FieldValues()
            {
                public void fetchFields(StateManager sm)
                {
                    sm.replaceFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex), false);
                }
                public void fetchNonLoadedFields(StateManager sm)
                {
                    sm.replaceNonLoadedFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex));
                }
                public FetchPlan getFetchPlanForLoading()
                {
                    return fetchPlan;
                }
            });
        }
        else
        {
            return om.findObject(oid, new FieldValues()
            {
                public void fetchFields(StateManager sm)
                {
                    sm.replaceFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex), false);
                }
                public void fetchNonLoadedFields(StateManager sm)
                {
                    sm.replaceNonLoadedFields(fieldNumbers, new ResultSetGetter(sm, rs, stmtExprIndex));
                }
                public FetchPlan getFetchPlanForLoading()
                {
                    return fetchPlan;
                }
            }, pcClass, ignoreCache);
        }
    }
}
TOP

Related Classes of org.jpox.store.rdbms.query.PersistentIDROF

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.