org.datanucleus.store.rdbms.query.PersistentClassROF Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-rdbms Show documentation
Show all versions of datanucleus-rdbms Show documentation
Plugin for DataNucleus providing persistence to RDBMS datastores.
/**********************************************************************
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.fieldmanager.FieldManager;
import org.datanucleus.store.rdbms.mapping.StatementClassMapping;
import org.datanucleus.store.rdbms.mapping.StatementMappingIndex;
import org.datanucleus.store.rdbms.mapping.java.JavaTypeMapping;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.fieldmanager.ResultSetGetter;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.SoftValueMap;
import org.datanucleus.util.StringUtils;
/**
* ResultObjectFactory that takes a JDBC ResultSet and create 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 implements ResultObjectFactory
{
protected final RDBMSStoreManager storeMgr;
/** Metadata for the persistent class. */
protected final AbstractClassMetaData acmd;
/** Persistent class that this factory will generate (may be the base class). */
private Class persistentClass;
/** Mapping for the statement to members of this class (and sub-objects). */
protected StatementClassMapping stmtMapping = null;
/** Fetch Plan to use when loading fields (if any). */
protected final FetchPlan fetchPlan;
/** Whether to ignore the cache */
private final boolean ignoreCache;
/** Resolved classes for metadata / discriminator keyed by class names. */
private Map resolvedClasses = new SoftValueMap();
/**
* Constructor.
* @param storeMgr RDBMS StoreManager
* @param acmd MetaData for the class (base class)
* @param mappingDefinition Mapping information for the result set and how it maps to the class
* @param ignoreCache Whether to ignore the cache
* @param fetchPlan the Fetch Plan
* @param persistentClass Class that this factory will create instances of (or subclasses)
*/
public PersistentClassROF(RDBMSStoreManager storeMgr, AbstractClassMetaData acmd, StatementClassMapping mappingDefinition,
boolean ignoreCache, FetchPlan fetchPlan, Class persistentClass)
{
if (mappingDefinition == null)
{
throw new NucleusException("Attempt to create PersistentClassROF with null mappingDefinition");
}
this.storeMgr = storeMgr;
this.stmtMapping = mappingDefinition;
this.acmd = acmd;
this.ignoreCache = ignoreCache;
this.fetchPlan = fetchPlan;
this.persistentClass = persistentClass;
}
/**
* Method to convert the current ResultSet row into an Object.
* @param ec execution context
* @param rs The ResultSet from the Query.
* @return The (persisted) object.
*/
public T getObject(final ExecutionContext ec, final ResultSet rs)
{
// Find the class of the returned object in this row of the ResultSet
String className = null;
boolean requiresInheritanceCheck = true;
StatementMappingIndex discrimMapIdx = stmtMapping.getMappingForMemberPosition(StatementClassMapping.MEMBER_DISCRIMINATOR);
if (discrimMapIdx != null)
{
// Discriminator mapping registered so use that
try
{
String 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;
}
JavaTypeMapping discrimMapping = discrimMapIdx.getMapping();
DiscriminatorMetaData dismd = (discrimMapping != null ? discrimMapping.getTable().getDiscriminatorMetaData() : null);
className = ec.getMetaDataManager().getClassNameFromDiscriminatorValue(discrimValue, dismd);
requiresInheritanceCheck = false;
}
catch (SQLException sqle)
{
NucleusLogger.DATASTORE_RETRIEVE.debug("Exception obtaining value of discriminator : " + sqle.getMessage());
}
}
else if (stmtMapping.getNucleusTypeColumnName() != null)
{
// Extract the object type using the NucleusType column (if available)
try
{
className = rs.getString(stmtMapping.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