Package org.apache.ojb.broker.accesslayer

Source Code of org.apache.ojb.broker.accesslayer.RowReaderDefaultImpl

package org.apache.ojb.broker.accesslayer;

/* Copyright 2002-2004 The Apache Software Foundation
*
* 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.
*/

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

import org.apache.ojb.broker.PBFactoryException;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.util.ClassHelper;
import org.apache.ojb.broker.util.logging.LoggerFactory;

/**
* Default implementation of the {@link RowReader} interface.
*
* @version $Id: RowReaderDefaultImpl.java,v 1.30 2004/05/06 18:54:44 arminw Exp $
*/

public class RowReaderDefaultImpl implements RowReader
{
    /**
     * represents a zero sized parameter array
     */
    private static final Object[] NO_ARGS = {};

    private ClassDescriptor m_cld;

    public RowReaderDefaultImpl(ClassDescriptor cld)
    {
        this.m_cld = cld;
    }

    /**
     * materialize a single object, described by cld,
     * from the first row of the ResultSet rs.
     * There are two possible strategies:
     * 1. The persistent class defines a public constructor with arguments matching the persistent
     * primitive attributes of the class. In this case we build an array args of arguments from rs
     * and call Constructor.newInstance(args) to build an object.
     * 2. The persistent class does not provide such a constructor, but only a public default
     * constructor. In this case we create an empty instance with Class.newInstance().
     * This empty instance is then filled by calling Field::set(obj,getObject(matchingColumn))
     * for each attribute.
     * The second strategy needs n calls to Field::set() which are much more expensive
     * than the filling of the args array in the first strategy.
     * client applications should therefore define adequate constructors to benefit from
     * performance gain of the first strategy.
     *
     * MBAIRD: The rowreader is told what type of object to materialize, so we have to trust
     * it is asked for the right type. It is possible someone marked an extent in the repository,
     * but not in java, or vice versa and this could cause problems in what is returned.
     *
     * we *have* to be able to materialize an object from a row that has a objConcreteClass, as we
     * retrieve ALL rows belonging to that table. The objects using the rowReader will make sure they
     * know what they are asking for, so we don't have to make sure a descriptor is assignable from the
     * selectClassDescriptor. This allows us to map both inherited classes and unrelated classes to the
     * same table.
     *
     */
    public Object readObjectFrom(Map row) throws PersistenceBrokerException
    {
        // allow to select a specific classdescriptor
        ClassDescriptor cld = selectClassDescriptor(row);
        return buildOrRefreshObject(row, cld, null);
    }

    /**
     * @see org.apache.ojb.broker.accesslayer.RowReader#refreshObject(Object, Map)
     */
    public void refreshObject(Object instance, Map row)
    {
        // 1. select target ClassDescriptor
        ClassDescriptor targetClassDescriptor = selectClassDescriptor(row);
        // 2. fill all scalar attributes of the existing object
        buildOrRefreshObject(row, targetClassDescriptor, instance);
    }

    /**
     * Creates an object instance according to clb, and fills its fileds width data provided by row.
     * @param row A {@link Map} contain the Object/Row mapping for the object.
     * @param targetClassDescriptor If the "ojbConcreteClass" feature was used, the target
     * {@link org.apache.ojb.broker.metadata.ClassDescriptor} could differ from the descriptor
     * this class was associated - see {@link #selectClassDescriptor}.
     * @param targetObject If 'null' a new object instance is build, else fields of object will
     * be refreshed.
     * @throws PersistenceBrokerException if there ewas an error creating the new object
     */
    protected Object buildOrRefreshObject(Map row, ClassDescriptor targetClassDescriptor, Object targetObject)
    {
        Object result = targetObject;
        FieldDescriptor fmd = null;

        if(targetObject == null)
        {
            // 1. create new object instance if needed
            result = ClassHelper.buildNewObjectInstance(targetClassDescriptor);
        }

        // 2. fill all scalar attributes of the new object
        FieldDescriptor[] fields = targetClassDescriptor.getFieldDescriptions();
        for (int i = 0; i < fields.length; i++)
        {
            fmd = fields[i];
            fmd.getPersistentField().set(result, row.get(fmd.getColumnName()));
        }

        if(targetObject == null)
        {
            // 3. for new build objects, invoke the initialization method for the class if one is provided
            Method initializationMethod = targetClassDescriptor.getInitializationMethod();
            if (initializationMethod != null)
            {
                try
                {
                    initializationMethod.invoke(result, NO_ARGS);
                }
                catch (Exception ex)
                {
                    throw new PersistenceBrokerException("Unable to invoke initialization method:" + initializationMethod.getName() + " for class:" + m_cld.getClassOfObject(), ex);
                }
            }
        }
        return result;
    }

    /**
     * materialize a single object, described by cld,
     * from the first row of the ResultSet rs.
     * There are two possible strategies:
     * 1. The persistent class defines a public constructor with arguments matching the persistent
     * primitive attributes of the class. In this case we build an array args of arguments from rs
     * and call Constructor.newInstance(args) to build an object.
     * 2. The persistent class does not provide such a constructor, but only a public default
     * constructor. In this case we create an empty instance with Class.newInstance().
     * This empty instance is then filled by calling Field::set(obj,getObject(matchingColumn))
     * for each attribute.
     * The second strategy needs n calls to Field::set() which are much more expensive
     * than the filling of the args array in the first strategy.
     * client applications should therefore define adequate constructors to benefit from
     * performance gain of the first strategy.
     *
     * @throws PersistenceBrokerException if there is an error accessing the access layer
     */
    public void readObjectArrayFrom(ResultSet rs, Map row)
    {
        FieldDescriptor[] fields = null;
        if (m_cld.getSuperClass() != null)
        {
            /**
             * treeder
             * append super class fields if exist
             */
            fields = m_cld.getFieldDescriptorsInHeirarchy();
        }
        else
        {
            fields = m_cld.getRepository().getFieldDescriptorsForMultiMappedTable(m_cld);
        }
        readValuesFrom(rs, row, fields);
    }

    /*
     * @see RowReader#readPkValuesFrom(ResultSet, ClassDescriptor, Map)
     * @throws PersistenceBrokerException if there is an error accessing the access layer
     */
    public void readPkValuesFrom(ResultSet rs, Map row)
    {
        FieldDescriptor[] pkFields = m_cld.getPkFields();
        readValuesFrom(rs, row, pkFields);
    }

    protected void readValuesFrom(ResultSet rs, Map row, FieldDescriptor[] fields)
    {
        int size = fields.length;
        Object val = null;
        FieldDescriptor fld = null;
        try
        {
            for (int j = 0; j < size; j++)
            {
                fld = fields[j];
                val = fld.getJdbcType().getObjectFromColumn(rs, fld.getColumnName());
                row.put(fld.getColumnName(), fld.getFieldConversion().sqlToJava(val));
            }
        }
        catch (SQLException t)
        {
            throw new PersistenceBrokerException("Error reading class type: " + m_cld.getClassNameOfObject()
                    + " from result set, current read field was " + (fld != null ? fld.getPersistentField().getName() : null), t);
        }
    }

    /**
     * Check if there is an attribute which tells us which concrete class is to be instantiated.
     */
    protected ClassDescriptor selectClassDescriptor(Map row) throws PersistenceBrokerException
    {
        // check if there is an attribute which tells us which concrete class is to be instantiated
        FieldDescriptor concreteClassFD = m_cld.getOjbConcreteClassField();

        if (concreteClassFD == null)
            return m_cld;
        else
        {
            try
            {
                String concreteClass = (String) row.get(concreteClassFD.getColumnName());
                if (concreteClass == null || concreteClass.trim().length() == 0)
                {
                    throw new PersistenceBrokerException(
                            "ojbConcreteClass field returned null or 0-length string");
                }
                else
                {
                    concreteClass = concreteClass.trim();
                }
                ClassDescriptor result = m_cld.getRepository().getDescriptorFor(concreteClass);
                if (result == null)
                {
                    LoggerFactory.getDefaultLogger().warn(
                            "[" + RowReaderDefaultImpl.class.getName()
                            + "] Can not find class-descriptor for ojbConcreteClass " + concreteClass
                            + ", use given class-descriptor '" + m_cld.getClassNameOfObject()
                            + "' instead");
                    result = m_cld;
                }
                return result;
            }
            catch (PBFactoryException e)
            {
                throw new PersistenceBrokerException(e);
            }
        }
    }

    public void setClassDescriptor(ClassDescriptor cld)
    {
        this.m_cld = cld;
    }

    public ClassDescriptor getClassDescriptor()
    {
        return m_cld;
    }
}
TOP

Related Classes of org.apache.ojb.broker.accesslayer.RowReaderDefaultImpl

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.