FileDocCategorySizeDatePackage
ResultDesc.javaAPI DocGlassfish v2 API34326Fri May 04 22:35:12 BST 2007com.sun.jdo.spi.persistence.support.sqlstore.sql

ResultDesc

public class ResultDesc extends Object
This class is used by the store to materialize objects from a JDBC resultset. Each ResultDesc binds values from the ResultSet to instances of a persistence capable class.

Fields Summary
private List
fields
List of ResultFieldDesc/ResultDesc.
private List
fieldNames
List of field names corresponding to fields.
private com.sun.jdo.spi.persistence.support.sqlstore.model.ClassDesc
config
Class descriptor.
private boolean
prefetching
Indicates whether this ResultDesc is prefetching relationship fields.
private ArrayList
prefetchedCollectionFields
Array of ForeignFieldDesc. Holds prefetched collection relationship fields.
private com.sun.jdo.spi.persistence.support.sqlstore.model.ForeignFieldDesc
parentField
The field that is the recipient of the value from this ResultDesc.
private ResultFieldDesc
fieldProjection
Holds the projected local field.
private int
aggregateResultType
Result type for aggregate queries.
private static com.sun.jdo.spi.persistence.utility.logging.Logger
logger
The logger.
private static final ResourceBundle
messages
I18N message handler.
private boolean
debug
Constructors Summary
public ResultDesc(com.sun.jdo.spi.persistence.support.sqlstore.model.ClassDesc config, int aggregateResultType)


         
        fields = new ArrayList();
        fieldNames = new ArrayList();
        this.config = config;
        this.aggregateResultType = aggregateResultType;
    
Methods Summary
public voidaddField(com.sun.jdo.spi.persistence.support.sqlstore.model.LocalFieldDesc fieldDesc, com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.ColumnRef columnRef, boolean projection)
Create and add a ResultFieldDesc for the given fieldDesc and columnRef.

param
fieldDesc - the field descriptor for the field that is the recipient of the result value indicated by the columnRef.
param
columnRef - indicates which column in the resultset contains the value.
param
projection - indicates whether the column is a projection


            ResultFieldDesc rfd = new ResultFieldDesc(fieldDesc, columnRef);
            // remember the projection
            if (projection) {
                this.fieldProjection = rfd;
            }
            fields.add(rfd);
            fieldNames.add(fieldDesc.getName());
    
private voidaddField(com.sun.jdo.spi.persistence.support.sqlstore.sql.ResultDesc rs)

        if (rs != null) {
            fields.add(rs);
            fieldNames.add(null);
        }
    
private voidapplyDeferredUpdatesToPrefetchedCollections(java.util.Collection resultCollection)
The prefetched collection fields might have deferred updates for

param
resultCollection

        if (prefetching && prefetchedCollectionFields != null && prefetchedCollectionFields.size() > 0) {
            for (Iterator resultItr = resultCollection.iterator(); resultItr.hasNext();) {
                // resultPC is guranted to be instance of  PersistenceCapable
                PersistenceCapable resultPC = (PersistenceCapable) resultItr.next();

                // resultPC can be null if this is a projection query 
                if(resultPC != null) {
                    SQLStateManager sm = (SQLStateManager)resultPC.jdoGetStateManager();
                    Iterator prefetchedCollectionFieldsIter = prefetchedCollectionFields.iterator();
                    StateManager resultSM = resultPC.jdoGetStateManager();
                    while (prefetchedCollectionFieldsIter.hasNext()) {
                        ForeignFieldDesc prefetchedCollectionField =
                                (ForeignFieldDesc) prefetchedCollectionFieldsIter.next();
                        // process deferred updates for all prefetched collection relationship
                        if(prefetchedCollectionField.cardinalityUPB > 1) {
                            SCOCollection relationshipValue =
                                    (SCOCollection) prefetchedCollectionField.getValue(sm);
                            if(relationshipValue.isDeferred()){
                                relationshipValue.applyDeferredUpdates(null);
                            }
                            // Presence mask bit for a prefetched collection field is
                            // not set in setFields. Set the bit here.
                            resultSM.setPresenceMaskBit(prefetchedCollectionField.absoluteID);
                        }
                    }
                }
            }
        }
    
private static java.lang.ObjectcreateSCO(java.lang.Object value, StateManager sm, com.sun.jdo.spi.persistence.support.sqlstore.model.FieldDesc fieldDesc)
Creates a SCO corresponding to value. Currently used for dates. The actual SCO conversion for dates is done in {@link com.sun.jdo.spi.persistence.support.sqlstore.model.FieldDesc#createSCO(java.lang.Object, com.sun.jdo.spi.persistence.support.sqlstore.StateManager)}.

param
value Value to be converted.
param
sm StateManager of the persistent object being populated.
param
fieldDesc Field being bound.
return
New SCO instance, null if no SCO was created.

        Object retVal = null;

        if (fieldDesc != null) {
            int enumType = fieldDesc.getEnumType();

            // Need to convert Date fields into their SCO equivalents
            switch(enumType) {
                case FieldTypeEnumeration.UTIL_DATE:
                case FieldTypeEnumeration.SQL_DATE:
                case FieldTypeEnumeration.SQL_TIME:
                case FieldTypeEnumeration.SQL_TIMESTAMP:

                    retVal = fieldDesc.createSCO(value, sm);
                    break;
                default:
            }
        }

        return retVal;
    
public voiddoJoin(com.sun.jdo.spi.persistence.support.sqlstore.sql.ResultDesc foreignResult, com.sun.jdo.spi.persistence.support.sqlstore.model.ForeignFieldDesc parentField)
Joins foreignResult with this resultDesc

param
foreignResult the foreign ResultDesc
param
parentField parentField for the foreind ResultDesc

        addField(foreignResult);
        foreignResult.parentField = parentField;

        // if foreign result correponds to a collection relationship being
        // prefetched, remember it.
        if(parentField.cardinalityUPB > 1) {
            if(prefetchedCollectionFields == null) {
                prefetchedCollectionFields = new ArrayList();
            }
            prefetchedCollectionFields.add(parentField);
        }
    
private StateManagerfindOrCreateStateManager(java.sql.ResultSet resultData, PersistenceManager pm)
Returns a StateManager which PC instance to be populated with the values. If such instance exists in this PersistenceManager cache, it is returned, otherwise a new instance is created.

        try {
            Class oidClass = config.getOidClass();
            Object oid = oidClass.newInstance();

            // Copy key field values
            Field keyFields[] = config.getKeyFields();
            String keyNames[] = config.getKeyFieldNames();
            for (int i = 0; i < keyFields.length; i++) {
                Field keyField = keyFields[i];
                String keyName = keyNames[i];
                FieldDesc fd = config.getField(keyName);
                int index = fieldNames.indexOf(keyName);

                ResultFieldDesc rfd = (ResultFieldDesc)fields.get(index);

                Object v = getConvertedObject(resultData, rfd.getColumnRef(), fd, null);

                if (debug) {
                    logger.finest("sqlstore.resultdesc.marking_key_field",keyName); // NOI18N
                }

                if (v == null ) {
                    return null;
                }
                keyField.set(oid, v);
            }
            return pm.findOrCreateStateManager(oid, config.getPersistenceCapableClass());

        } catch (Exception e) {
            // RESOLVE...
            throw new JDOFatalInternalException(e.getMessage());
        }
    
private java.lang.ObjectgetAggregateResult(java.sql.ResultSet resultData)
Get result for Aggregates. Since resultset containing result for aggregates would not contain any other columns, it is assumed that the result is available at index == 1.

param
resultData The resultset from which result is to be extracted.

        Object result = null;

        if (resultData.next() ) {
            //Aggregate results are always at index 1;
            result = getValueFromResultSet(resultData, 1, aggregateResultType);
        }
        return result;
   
private static java.lang.ObjectgetConvertedObject(java.sql.ResultSet resultData, com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.ColumnRef columnRef, com.sun.jdo.spi.persistence.support.sqlstore.model.FieldDesc fieldDesc, StateManager sm)
Get the value to be bound to the field described by fieldDesc from the result set. The conversion to the correct field type might be done by the driver. If we can't get the correct type from the driver, the conversion in done in FieldDesc::convertValue.

param
resultData Result set from the database.
param
columnRef columnRef for the field.
param
fieldDesc Field descriptor of the field to be bound.
param
sm State manager for the persistent object being bound.
return
Object with the correct type defined in fieldDesc.

        Object retVal = null;
        try {
            retVal = getValueFromResultSet(resultData, columnRef, fieldDesc.getEnumType());
            // Create an SCO object in case we want to populate a pc.
            if (retVal != null) {
                // Create a SCO instance in case we want to populate a pc.
                Object scoVal = createSCO(retVal, sm, fieldDesc);
                if (scoVal != null) {
                    retVal = scoVal;
                }
            }
        } catch (SQLException sqlException) {
            //The driver is not able to convert for us
            //We would use resultData.getObject(index) below
            //and let FieldDesc::convertValue() do the conversion
            //Nothing to do here
            try {
                // Get the generic object and let FieldDesc::convertValue() deal with it.
                // This will return an SCO as needed.
                retVal = fieldDesc.convertValue(resultData.getObject(columnRef.getIndex()), sm);
            }
            catch (Exception e) {
                //Resolve : The original code was returning null and not throwing any
                //exception in this case. Should we also do that????
                logger.log(Logger.WARNING,"sqlstore.exception.log",e);
            }
        }

        return retVal;
    
private java.lang.ObjectgetProjectedField(java.sql.ResultSet resultData)
Returns the projected field from the result set. This field is always a local field. Foreign fields are handled in setFields. We return the database value for projections on local fields. Unless we flush for queries in optimistic transactions the value from the database might be different from the value in memory.

param
resultData The SQL result set.
return
The projected value from the result set. This might be a local field or the result of an aggregate query.
see
com.sun.jdo.spi.persistence.support.sqlstore.sql.ResultDesc#setFields(PersistenceManager, ResultSet)

        //Field projection can never be null if this method gets called.
        FieldDesc f = fieldProjection.getFieldDesc();

        if (debug) {
            logger.finest("sqlstore.resultdesc.returning_field", f.getName()); // NOI18N
        }
        return getConvertedObject(resultData, fieldProjection.getColumnRef(), f, null);
    
public java.lang.ObjectgetResult(PersistenceManager pm, java.sql.ResultSet resultData)
Materialize data from the result set into objects.

param
pm - the PersistenceManager responsible for instantiating objects
param
resultData - JDBC ResultSet containing the data to be materialized
return
Collection containing the resulting objects. For aggregate queries, the returned object type is specified by the caller.

        Object result = null;

        debug = logger.isLoggable(Logger.FINEST);

        if (!isAggregate()) {
            Collection resultCollection = new ArrayList();

            // Fill in the data from the current row of resultData.
            while (resultData.next()) {
                Object resultObject = null;

                if (fieldProjection != null) {
                    resultObject = getProjectedField(resultData);
                } else {
                    resultObject = setFields(pm, resultData);
                }
                // resultCollection might contain resultObject if prefetch
                // is enabled. Do not add duplicates. Duplicates are required
                // for projection queries
                if (!prefetching || !resultCollection.contains(resultObject)) {
                    resultCollection.add(resultObject);
                }
            }

            //Iterate over the results obtained and handle deferred collection updates.
            applyDeferredUpdatesToPrefetchedCollections(resultCollection);
            result = resultCollection;
        } else {
            // Aggregate functions return an object instead of a collection.
            result = getAggregateResult(resultData);
        }

        return result;
    
private static java.lang.ObjectgetValueFromResultSet(java.sql.ResultSet resultData, com.sun.jdo.spi.persistence.support.sqlstore.sql.generator.ColumnRef columnRef, int resultType)
Gets value at index from resultData. resultData is queried for passed resultType.

param
resultData The resultset object.
param
columnRef columnRef for the field.
param
resultType Type of expected result.
return
value from resultData at index.

        int index = columnRef.getIndex();
        int columnType = columnRef.getColumnType();

        return getValueFromResultSet(resultData, index, resultType, columnType);
    
private static java.lang.ObjectgetValueFromResultSet(java.sql.ResultSet resultData, int index, int resultType)
Gets value at index from resultData.resultData is queried for passed resultType.

param
resultData The resultset object.
param
index Index at which result needs to be obtained.
param
resultType Type of expected result.
return
value from resultData at index.


        // Types.OTHER is passed as a placeholder here.
        // It implies don't care for columnType.
        return getValueFromResultSet(resultData, index, resultType, Types.OTHER);
    
private static java.lang.ObjectgetValueFromResultSet(java.sql.ResultSet resultData, int index, int resultType, int columnType)
Gets value at index from resultData. resultData is queried for passed resultType.

param
resultData The resultset object.
param
index Index at which result needs to be obtained.
param
resultType Type of expected result.
param
columnType Types of column at index index as represented by java.sql.Types.
return
value from resultData at index.


        Object retVal = null;
        try {
            switch(resultType) {
                case FieldTypeEnumeration.BOOLEAN_PRIMITIVE :
                case FieldTypeEnumeration.BOOLEAN  :
                        boolean booleanValue = resultData.getBoolean(index);
                        if(!resultData.wasNull() )
                            retVal = new Boolean(booleanValue);
                        break;
                case FieldTypeEnumeration.CHARACTER_PRIMITIVE :
                case FieldTypeEnumeration.CHARACTER  :
                        String strValue = resultData.getString(index);
                        if(strValue != null)
                            retVal =  FieldDesc.getCharFromString(strValue);
                        break;
                case FieldTypeEnumeration.BYTE_PRIMITIVE :
                case FieldTypeEnumeration.BYTE  :
                        byte byteValue = resultData.getByte(index);
                        if(!resultData.wasNull() )
                            retVal = new Byte(byteValue);
                        break;
                case FieldTypeEnumeration.SHORT_PRIMITIVE :
                case FieldTypeEnumeration.SHORT  :
                        short shortValue = resultData.getShort(index);
                        if(!resultData.wasNull() )
                            retVal = new Short(shortValue);
                        break;
                case FieldTypeEnumeration.INTEGER_PRIMITIVE :
                case FieldTypeEnumeration.INTEGER  :
                        int intValue = resultData.getInt(index);
                        if(!resultData.wasNull() )
                            retVal = new Integer(intValue);
                        break;
                case FieldTypeEnumeration.LONG_PRIMITIVE :
                case FieldTypeEnumeration.LONG  :
                        long longValue = resultData.getLong(index);
                        if(!resultData.wasNull() )
                            retVal = new Long(longValue);
                        break;
                case FieldTypeEnumeration.FLOAT_PRIMITIVE :
                case FieldTypeEnumeration.FLOAT  :
                        float floatValue = resultData.getFloat(index);
                        if(!resultData.wasNull() )
                            retVal = new Float(floatValue);
                        break;
                case FieldTypeEnumeration.DOUBLE_PRIMITIVE :
                case FieldTypeEnumeration.DOUBLE  :
                        double doubleValue = resultData.getDouble(index);
                        if(!resultData.wasNull() )
                            retVal = new Double(doubleValue);
                        break;
                case FieldTypeEnumeration.BIGDECIMAL :
                case FieldTypeEnumeration.BIGINTEGER :
                        retVal = resultData.getBigDecimal(index);
                        if ((resultType == FieldTypeEnumeration.BIGINTEGER) && (retVal != null)) {
                            retVal = ( (java.math.BigDecimal) retVal).toBigInteger();
                        }
                        break;
                case FieldTypeEnumeration.STRING :
                        if(LocalFieldDesc.isCharLobType(columnType) ) {
                            Reader reader = resultData.getCharacterStream(index);
                            retVal = readCharacterStreamToString(reader);
                        } else {
                            retVal = resultData.getString(index);
                        }
                        break;
                case FieldTypeEnumeration.SQL_DATE :
                        retVal = resultData.getDate(index);
                        break;
                case FieldTypeEnumeration.SQL_TIME :
                        retVal = resultData.getTime(index);
                        break;
                case FieldTypeEnumeration.UTIL_DATE :
                case FieldTypeEnumeration.SQL_TIMESTAMP :
                        //Variable ts is introduced to avoid cast
                        Timestamp ts;
                        ts = resultData.getTimestamp(index);
                        if (resultType == FieldTypeEnumeration.UTIL_DATE && ts != null) {
                            retVal = new Date(ts.getTime());
                        } else {
							retVal = ts;
						}
                        break;
                case FieldTypeEnumeration.ARRAY_BYTE_PRIMITIVE :
                        InputStream is = resultData.getBinaryStream(index);
                        retVal = readInputStreamToByteArray(is);
                        break;
               case FieldTypeEnumeration.NOT_ENUMERATED :
                        //RESOLVE:
                        //We should only get here for getting values for hidden fields.
                        //hiddenFields does not have their java type initialized. Its sort of difficult
                        //to initialize java type without major re-org of the code in ClassDesc :(.
                        //But once it is done, we should throw an exception if we reach here.
                        //
                        //For now retrieve value for hidden fields as object as they are any way
                        //stored as Object in SQLStatemanager.
                        retVal = resultData.getObject(index);
                      break;
               default :
                        //If we reach here, a new type has been added to FieldTypeEnumeration.
                        //Please update this method to handle new type.
                        throw new JDOFatalInternalException(I18NHelper.getMessage(messages,
                            "sqlstore.resultdesc.unknownfieldtype",resultType) );
            }   //switch
        } catch (SQLException e) {
            if(logger.isLoggable(Logger.WARNING) ) {
                Object items[] =
                    { new Integer(index), new Integer(resultType), new Integer(columnType), e};
                logger.log(Logger.WARNING,"sqlstore.resultdesc.errorgettingvalefromresulset",items);
            }
            throw e;
        }

        // RESOLVE: Following is a workaround till we are able to initialize java type for hidden fields.
        // When we are able to determine java type of hidden fields, this code should go back
        // to case FieldTypeEnumeration.String
        if (LocalFieldDesc.isFixedCharType(columnType)
            // For Character fields, this method is expected to return
            // Character. Do not convert them to String.
            && resultType != FieldTypeEnumeration.CHARACTER_PRIMITIVE
            && resultType != FieldTypeEnumeration.CHARACTER
            && retVal != null) {
            // To support char columns, we rtrim fields mapped to fixedchar.
             retVal = StringHelper.rtrim(retVal.toString());
        }

        return retVal;
    
private booleanisAggregate()
Specifies, if this was an aggregate query.

        return aggregateResultType != FieldTypeEnumeration.NOT_ENUMERATED;
    
private static java.lang.StringreadCharacterStreamToString(java.io.Reader reader)
Reads from the character stream reader into a String.

param
reader Reader obtained from the database.
return
A String read from the reader.
see
java.sql.ResultSet#getCharacterStream(int)

        String retVal = null;
        if(reader != null) {
            BufferedReader buffReader = new BufferedReader(reader);
            StringBuffer buff = new StringBuffer();
            try {
                int charRead;
                while( (charRead = buffReader.read() ) != -1) {
                    buff.append( (char)charRead );
                }
            } catch (IOException e) {
                    // log the exception and don't return any value
                    // Eating the exception here. As the caller also does not
                    // know how to deal with this exception.
                    logger.log(Logger.WARNING,"sqlstore.exception.log",e);
            }
            retVal = buff.toString();
        }
        return retVal;
    
private static byte[]readInputStreamToByteArray(java.io.InputStream is)
Reads from input stream is into a byte array.

param
is Input stream obtained from the database.
return
A byte array read from the input stream.
see
java.sql.ResultSet#getBinaryStream(int)

        byte[] byteArray = null;
        if (is != null) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            byte[] chunk = new byte[2000];
            int read = 0;

            try {
                while ((read = is.read(chunk)) != -1) {
                    bos.write(chunk, 0, read);
                }
                byteArray = bos.toByteArray();
            } catch (IOException e) {
                // log the exception and don't return any value
                // Eating the exception here. As the caller also does not
                // know how to deal with this exception.
                logger.log(Logger.WARNING,"sqlstore.exception.log",e);
            }
        }
        return byteArray;
    
private static voidsetFieldValue(StateManager sm, com.sun.jdo.spi.persistence.support.sqlstore.model.FieldDesc f, java.lang.Object value)
Set given value for the given field f for given statemanager sm. Also set presence mask bit for the field in given sm.

param
sm Given StateManager
param
f Given field
param
value Given value.

        f.setValue(sm, value);
        sm.setPresenceMaskBit(f.absoluteID);
    
private java.lang.ObjectsetFields(PersistenceManager pm, java.sql.ResultSet resultData)
Bind the columns from this ResultSet row to the persistent object described by this ResultDesc. External queries always return only one type of objects and don't have nested ResultDescs. Internal queries can have nested ResultDescs. Run through all the fields of the field list and bind the values in that order. Nested ResultDescs are processed by recursive calls.

param
pm The PersistenceManager responsible for instantiating objects.
param
resultData JDBC ResultSet containing the data to be materialized.
return
Persistent object corresponding to values from ResultSet row, can be null.

        Object pcObj = null;
        // Get the Statemanager corresponding to the current row
        SQLStateManager sm = (SQLStateManager) findOrCreateStateManager(resultData, pm);
        if (sm != null) {
            pcObj = sm.getPersistent();
            sm.getLock();
            try {
                // Fields are read in the order in which they were placed in
                // the sql select statement. This ordering is important while reading
                // from streams corresponding to LONG columns on Oracle.
                for (int i = 0; i < fields.size();  i++) {
                    Object temp = fields.get(i);

                    if (temp instanceof ResultFieldDesc) {
                        ResultFieldDesc rfd = (ResultFieldDesc) temp;
                        LocalFieldDesc f = rfd.getFieldDesc();

                        if (!sm.getPresenceMaskBit(f.absoluteID)) {
                            Object value = getConvertedObject(resultData, rfd.getColumnRef(), f, sm);

                            if (debug) {
                                logger.finest("sqlstore.resultdesc.marking_field", f.getName()); // NOI18N
                            }

                            // Set the field value and presence mask bit.
                            setFieldValue(sm, f, value);
                        }
                    } else {
                        ResultDesc frd = (ResultDesc) temp;
                        ForeignFieldDesc parentField = frd.parentField;

                        // Only try to fetch the field if it is not already present.
                        // If the field is already present, it should be in
                        // consistent state w.r.t. this transaction. Overwriting
                        // it with the value from database might corrupt consistency of data.
                        if (!sm.getPresenceMaskBit(parentField.absoluteID)) {
                            Object fobj = frd.setFields(pm, resultData);

                            if(parentField.cardinalityUPB > 1) { // parentField is a collection.
                                SCOCollection  collection = (SCOCollection) parentField.getValue(sm);
                                if (collection == null) {
                                    // Getting first member of the collection. Initialize the collection.
                                    // Presence mask bit for a collection field can not set here.
                                    // This is because the collection is not fully present till all the
                                    // elements of the collection are added. If the bit is set here,
                                    // next member of this collection from this resultset will not
                                    // be processed(added) here.
                                    // This bit is set after all the elements of this collection are
                                    // processed. That is in applyDeferredUpdatesToPrefetchedCollections()
                                     sm.replaceCollection(parentField, null);
                                    // Get the newly created SCOCollection back.
                                    collection = (SCOCollection) parentField.getValue(sm);
                                }

                                if(fobj != null ) {
                                    collection.addToBaseCollection(fobj);
                                }
                            } else { // parentField is an object.
                                // Set the field value and presence mask bit.
                                setFieldValue(sm, parentField, fobj);
                            }
                        }
                        if (debug) {
                            logger.finest("sqlstore.resultdesc.marking_foreign_field", // NOI18N
                                    parentField.getName());
                        }
                    }
                }

                sm.initialize(true);
            } finally {
                // Always release the lock.
                sm.releaseLock();
            }
        } else {
            // sm can be null if we can not find or create a statemanager from the result data.
            // This is possible if we are projecting on a foreignfield and there is no
            // result returned.
        }

        return pcObj;
    
public voidsetParentField(com.sun.jdo.spi.persistence.support.sqlstore.model.ForeignFieldDesc parentField)
Set the field that is the recipient of the result of this ResultDesc.

param
parentField - field descriptor for the recipient field of the value of this ResultDesc.

        this.parentField = parentField;
    
public voidsetPrefetching()

        prefetching = true;