Package org.apache.ojb.broker.accesslayer.sql

Source Code of org.apache.ojb.broker.accesslayer.sql.SqlSelectStatement

package org.apache.ojb.broker.accesslayer.sql;

/* Copyright 2002-2005 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.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.lang.ref.WeakReference;

import org.apache.commons.collections.set.ListOrderedSet;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.DescriptorRepository;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.JdbcType;
import org.apache.ojb.broker.platforms.Platform;
import org.apache.ojb.broker.query.Criteria;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.query.ReportQuery;
import org.apache.ojb.broker.query.ReportQueryByCriteria;
import org.apache.ojb.broker.util.SqlHelper;
import org.apache.ojb.broker.util.logging.Logger;

/**
* Model a SELECT Statement
*
* @author <a href="mailto:jbraeuchi@hotmail.com">Jakob Braeuchi</a>
* @version $Id: SqlSelectStatement.java,v 1.22.2.8 2005/12/22 18:25:51 brj Exp $
*/
public class SqlSelectStatement extends SqlQueryStatement implements SelectStatement
{
    private WeakReference fieldsForSelect;

    /**
     * Constructor for SqlSelectStatement.
     *
     * @param pf
     * @param cld
     * @param query
     * @param logger
     */
    public SqlSelectStatement(Platform pf, ClassDescriptor cld, Query query, Logger logger)
    {
        super(pf, cld, query, logger);
    }

    /**
     * Constructor for SqlSelectStatement.
     *
     * @param parent
     * @param pf
     * @param cld
     * @param query
     * @param logger
     */
    public SqlSelectStatement(SqlQueryStatement parent, Platform pf, ClassDescriptor cld, Query query, Logger logger)
    {
        super(parent, pf, cld, query, logger);
    }

    /**
     * Append a Column with alias: A0 name -> A0.name
     * @param anAlias the TableAlias
     * @param field
     * @param buf
     */
    protected void appendColumn(TableAlias anAlias, FieldDescriptor field, StringBuffer buf)
    {
        buf.append(anAlias.alias);
        buf.append(".");
        buf.append(field.getColumnName());
    }

    /**
     * Appends to the statement a comma separated list of column names.
     *
     * DO NOT use this if order of columns is important. The row readers build reflectively and look up
     * column names to find values, so this is safe. In the case of update, you CANNOT use this as the
     * order of columns is important.
     *
     * @return list of column names for the set of all unique columns for multiple classes mapped to the
     * same table.
     */
    protected List appendListOfColumnsForSelect(StringBuffer buf)
    {
        FieldDescriptor[] fieldDescriptors = getFieldsForSelect();
        ArrayList columnList = new ArrayList();
        TableAlias searchAlias = getSearchTable();
       
        for (int i = 0; i < fieldDescriptors.length; i++)
        {
            FieldDescriptor field = fieldDescriptors[i];
            TableAlias alias = getTableAliasForClassDescriptor(field.getClassDescriptor());
            if (alias == null)
            {
                alias = searchAlias;
            }
            if (i > 0)
            {
                buf.append(",");
            }
            appendColumn(alias, field, buf);
            columnList.add(field.getAttributeName());
        }
       
        appendClazzColumnForSelect(buf);
        return columnList;
    }
    /**
     * Get MultiJoined ClassDescriptors
     * @param cld
     */
    private ClassDescriptor[] getMultiJoinedClassDescriptors(ClassDescriptor cld)
    {
        DescriptorRepository repository = cld.getRepository();
        Class[] multiJoinedClasses = repository.getSubClassesMultipleJoinedTables(cld, true);
        ClassDescriptor[] result = new ClassDescriptor[multiJoinedClasses.length];

        for (int i = 0 ; i < multiJoinedClasses.length; i++)
        {
            result[i] = repository.getDescriptorFor(multiJoinedClasses[i]);
         }

        return result;
    }

    /**
     * Create the OJB_CLAZZ pseudo column based on CASE WHEN.
     * This column defines the Class to be instantiated.
     * @param buf
     */
    private void appendClazzColumnForSelect(StringBuffer buf)
    {
        ClassDescriptor cld = getSearchClassDescriptor();
        ClassDescriptor[] clds = getMultiJoinedClassDescriptors(cld);

        if (clds.length == 0)
        {
            return;
        }
       
        buf.append(",CASE");

        for (int i = clds.length; i > 0; i--)
        {
            buf.append(" WHEN ");

            ClassDescriptor subCld = clds[i - 1];
            FieldDescriptor[] fieldDescriptors = subCld.getPkFields();

            TableAlias alias = getTableAliasForClassDescriptor(subCld);
            for (int j = 0; j < fieldDescriptors.length; j++)
            {
                FieldDescriptor field = fieldDescriptors[j];
                if (j > 0)
                {
                    buf.append(" AND ");
                }
                appendColumn(alias, field, buf);
                buf.append(" IS NOT NULL");
            }
            buf.append(" THEN '").append(subCld.getClassNameOfObject()).append("'");
        }
        buf.append(" ELSE '").append(cld.getClassNameOfObject()).append("'");
        buf.append(" END AS " + SqlHelper.OJB_CLASS_COLUMN);
    }
   
    /**
     * Return the Fields to be selected.
     *
     * @return the Fields to be selected
     */
    protected FieldDescriptor[] getFieldsForSelect()
    {
        if (fieldsForSelect == null || fieldsForSelect.get() == null)
        {
            fieldsForSelect = new WeakReference(buildFieldsForSelect(getSearchClassDescriptor()));
        }
        return (FieldDescriptor[]) fieldsForSelect.get();
    }

    /**
     * Return the Fields to be selected.
     *
     * @param cld the ClassDescriptor
     * @return the Fields to be selected
     */
    protected FieldDescriptor[] buildFieldsForSelect(ClassDescriptor cld)
    {
        DescriptorRepository repository = cld.getRepository();
        Set fields = new ListOrderedSet();   // keep the order of the fields
       
        // add Standard Fields
        // MBAIRD: if the object being queried on has multiple classes mapped to the table,
        // then we will get all the fields that are a unique set across all those classes so if we need to
        // we can materialize an extent
        FieldDescriptor fds[] = repository.getFieldDescriptorsForMultiMappedTable(cld);
        for (int i = 0; i < fds.length; i++)
        {
            fields.add(fds[i]);
        }

        // add inherited Fields. This is important when querying for a class having a super-reference
        fds = cld.getFieldDescriptor(true);
        for (int i = 0; i < fds.length; i++)
        {
            fields.add(fds[i]);
        }

        // add Fields of joined subclasses
        Class[] multiJoinedClasses = repository.getSubClassesMultipleJoinedTables(cld, true);
        for (int c = 0; c < multiJoinedClasses.length; c++)
        {
            ClassDescriptor subCld = repository.getDescriptorFor(multiJoinedClasses[c]);
            fds = subCld.getFieldDescriptions();
            for (int i = 0; i < fds.length; i++)
            {
                fields.add(fds[i]);
            }
        }

        FieldDescriptor[] result = new FieldDescriptor[fields.size()];
        fields.toArray(result);
        return result;
    }

    /**
     * Appends to the statement a comma separated list of column names.
     *
     * @param columns defines the columns to be selected (for reports)
     * @return list of column names
     */
    protected List appendListOfColumns(String[] columns, StringBuffer buf)
    {
        ArrayList columnList = new ArrayList();

        for (int i = 0; i < columns.length; i++)
        {
            if (i > 0)
            {
                buf.append(",");
            }
            appendColName(columns[i], false, null, buf);
            columnList.add(columns[i]);
        }
        return columnList;
    }

    /**
     * @see org.apache.ojb.broker.accesslayer.sql.SqlQueryStatement#buildStatement()
     */
    protected String buildStatement()
    {
        StringBuffer stmt = new StringBuffer(1024);
        Query query = getQuery();
        boolean first = true;
        List orderByFields = null;
        String[] attributes = null;
        String[] joinAttributes = null;
        Iterator it = getJoinTreeToCriteria().entrySet().iterator();
        List columnList = new ArrayList();

        if (query instanceof ReportQuery)
        {
            attributes = ((ReportQuery) query).getAttributes();
            joinAttributes = ((ReportQuery) query).getJoinAttributes();
        }

        while (it.hasNext())
        {
            Map.Entry entry = (Map.Entry) it.next();
            Criteria whereCrit = (Criteria) entry.getValue();
            Criteria havingCrit = query.getHavingCriteria();
            StringBuffer where = new StringBuffer();
            StringBuffer having = new StringBuffer();
            List groupByFields;

            // Set correct tree of joins for the current criteria
            setRoot((TableAlias) entry.getKey());

            if (whereCrit != null && whereCrit.isEmpty())
            {
                whereCrit = null;
            }

            if (havingCrit != null && havingCrit.isEmpty())
            {
                havingCrit = null;
            }

            if (first)
            {
                first = false;
            }
            else
            {
                stmt.append(" UNION ");
            }

            stmt.append("SELECT ");
            if (query.isDistinct())
            {
                stmt.append("DISTINCT ");
            }

            if (attributes == null || attributes.length == 0)
            {
                /**
                 * MBAIRD: use the appendListofColumnsForSelect, as it finds
                 * the union of select items for all object mapped to the same table. This
                 * will allow us to load objects with unique mapping fields that are mapped
                 * to the same table.
                 */
                columnList.addAll(appendListOfColumnsForSelect(stmt));
            }
            else
            {
                columnList.addAll(appendListOfColumns(attributes, stmt));
            }

            // BRJ:
            // joinColumns are only used to force the building of a join;
            // they are not appended to the select-clause !
            // these columns are used in COUNT-ReportQueries and
            // are taken from the query the COUNT is based on
            if (joinAttributes != null && joinAttributes.length > 0)
            {
                for (int i = 0; i < joinAttributes.length; i++)
                {
          getAttributeInfo(joinAttributes[i], false, null, getQuery().getPathClasses());
                }
            }

            groupByFields = query.getGroupBy();
            ensureColumns(groupByFields, columnList);
           
            orderByFields = query.getOrderBy();
            columnList = ensureColumns(orderByFields, columnList, stmt);
/*
arminw:
TODO: this feature doesn't work, so remove this in future
*/
            /**
             * treeder: going to map superclass tables here,
             * not sure about the columns, just using all columns for now
             */
            ClassDescriptor cld = getBaseClassDescriptor();
            ClassDescriptor cldSuper = null;
            if (cld.getSuperClass() != null)
            {
                // then we have a super class so join tables
                cldSuper = cld.getRepository().getDescriptorFor(cld.getSuperClass());
                appendSuperClassColumns(cldSuper, stmt);
            }

            stmt.append(" FROM ");
            appendTableWithJoins(getRoot(), where, stmt);

            if (cld.getSuperClass() != null)
            {
                appendSuperClassJoin(cld, cldSuper, stmt, where);
            }
           
            appendWhereClause(where, whereCrit, stmt);
            appendGroupByClause(groupByFields, stmt);
            appendHavingClause(having, havingCrit, stmt);
        }

        appendOrderByClause(orderByFields, columnList, stmt);

        if (query instanceof ReportQueryByCriteria)
        {
             ((ReportQueryByCriteria) query).setAttributeFieldDescriptors(m_attrToFld);
        }

        return stmt.toString();
    }

/*
arminw:
TODO: this feature doesn't work, so remove this in future
*/
    private void appendSuperClassJoin(ClassDescriptor cld, ClassDescriptor cldSuper, StringBuffer stmt, StringBuffer where)
    {
        stmt.append(",");
        appendTable(cldSuper, stmt);

        if (where != null)
        {
            if (where.length() > 0)
            {
                where.append(" AND ");
            }

            // get reference field in super class
            // TODO: do not use the superclassfield anymore, just assume that the id is the same in both tables - @see PBroker.storeToDb
            int superFieldRef = cld.getSuperClassFieldRef();
            FieldDescriptor refField = cld.getFieldDescriptorByIndex(superFieldRef);

            appendTable(cldSuper, where);
            where.append(".");
            appendField(cldSuper.getAutoIncrementFields()[0], where);
            where.append(" = ");
            appendTable(cld, where);
            where.append(".");
            appendField(refField, where);
        }
    }

    private void appendSuperClassColumns(ClassDescriptor cldSuper, StringBuffer buf)
    {
        FieldDescriptor[] fields = cldSuper.getFieldDescriptions();
        for (int i = 0; i < fields.length; i++)
        {
            FieldDescriptor field = fields[i];
            if (i > 0)
            {
                buf.append(",");
            }
            buf.append(cldSuper.getFullTableName());
            buf.append(".");
            buf.append(field.getColumnName());
        }
    }

    /**
     * Append table name. Quote if necessary.
     */
    protected void appendTable(ClassDescriptor cld, StringBuffer buf)
    {
        buf.append(cld.getFullTableName());
    }

    /**
     * Append column name. Quote if necessary.
     */
    protected void appendField(FieldDescriptor fld, StringBuffer buf)
    {
        buf.append(fld.getColumnName());
    }

    public Query getQueryInstance()
    {
        return getQuery();
    }

    public int getColumnIndex(FieldDescriptor fld)
    {
        int index = JdbcType.MIN_INT;
        FieldDescriptor[] fields = getFieldsForSelect();
        if (fields != null)
        {
            for (int i = 0; i < fields.length; i++)
            {
                if (fields[i].equals(fld))
                {
                    index = i + 1// starts at 1
                    break;
                }
            }
        }
        return index;
    }
}
TOP

Related Classes of org.apache.ojb.broker.accesslayer.sql.SqlSelectStatement

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.