Package org.jpox.store.rdbms.query

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

/**********************************************************************
Copyright (c) 2007 Erik Bengtson 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:
2008 Andy Jefferson - simplify interface. Add BULK_UPDATE, BULK_DELETE
    ...
**********************************************************************/
package org.jpox.store.rdbms.query;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Collection;

import org.jpox.ManagedConnection;
import org.jpox.ManagedConnectionResourceListener;
import org.jpox.OMFContext;
import org.jpox.ObjectManager;
import org.jpox.PersistenceConfiguration;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.query.Evaluator;
import org.jpox.store.mapped.query.StatementText;
import org.jpox.store.query.Query;
import org.jpox.store.query.QueryResult;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.store.rdbms.SQLController;
import org.jpox.util.JPOXLogger;
import org.jpox.util.Localiser;
import org.jpox.util.TypeConversionHelper;

/**
* Evaluator for SQL-based queries.
* Takes the query, converts it into executable SQL and executes it, returning it in an appropriate form.
*
* @version $Revision: 1.9 $
*/
public class SQLEvaluator implements Evaluator
{
    /** Localiser for messages. */
    protected static final Localiser LOCALISER=Localiser.getInstance("org.jpox.store.Localisation",
        RDBMSManager.class.getClassLoader());

    /** Query being evaluated. */
    Query query;

    /** ObjectManager for this query. */
    ObjectManager om;

    /** Whether to apply DISTINCT to the query evaluation. */
    boolean distinct;

    /** Factory for obtaining results from the result set. */
    ResultObjectFactory rof;

    Collection candidateCollection;

    /**
     * Constructor.
     * @param query The query
     * @param distinct Whether to apply distinct to the results
     * @param rof Factory for results
     * @param candidateCollection Candidate collection (optional)
     */
    public SQLEvaluator(Query query, boolean distinct, ResultObjectFactory rof, Collection candidateCollection)
    {
        this.om = query.getObjectManager();
        this.distinct = distinct;
        this.query = query;
        this.rof = rof;
        this.candidateCollection = candidateCollection;
    }
   
    /**
     * Method to evaulate the query.
     * @param queryStmt The query statement.
     * @return Result of the query
     */
    public Object evaluate(QueryExpression queryStmt)
    {
        // Find the locking strategy (use the txn value, and override with any local query setting
        boolean useUpdateLock = ((Boolean)om.getTransaction().getOptions().get("transaction.serializeReadObjects")).booleanValue();
        Object useUpdateLockExt = query.getExtension("org.jpox.rdbms.query.useUpdateLock");
        if (useUpdateLockExt != null)
        {
            useUpdateLock = Boolean.valueOf((String)useUpdateLockExt).booleanValue();
        }

        QueryResult qr = null;
        try
        {
            RDBMSManager storeMgr = (RDBMSManager)om.getStoreManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
           
            SQLController sqlControl = storeMgr.getSQLController();

            try
            {
                StatementText stmtText = null;
                if (query.getType() == Query.SELECT)
                {
                    stmtText = queryStmt.toStatementText(useUpdateLock);
                }
                else if (query.getType() == Query.BULK_UPDATE)
                {
                    stmtText = queryStmt.toUpdateStatementText();
                    throw new JPOXException("JPOX doesnt currently support bulk update statements");
                }
                else if (query.getType() == Query.BULK_DELETE)
                {
                    // TODO Distinguish between update and delete
                    stmtText = queryStmt.toDeleteStatementText();
                    throw new JPOXException("JPOX doesnt currently support bulk delete statements");
                }

                PreparedStatement ps = getStatement(mconn, stmtText);
                try
                {
                    // Apply timeouts, result set constraints etc
                    prepareStatementForExecution(ps);

                    // Add a limit on the number of rows to include the maximum we may need
                    long toExclNo = query.getRangeToExcl();
                    if (toExclNo != 0 && toExclNo != Long.MAX_VALUE)
                    {
                        if (toExclNo > Integer.MAX_VALUE)
                        {
                            // setMaxRows takes an int as input so limit to the correct range
                            ps.setMaxRows(Integer.MAX_VALUE);
                        }
                        else
                        {
                            ps.setMaxRows((int)toExclNo);
                        }
                    }

                    if (query.getType() != Query.SELECT)
                    {
                        JPOXLogger.JDO.debug(">> SQLEvaluator.evaluate BULK operation SELECT");
                    }
                    if (query.getType() == Query.SELECT)
                    {
                        // SELECT query
                        ResultSet rs = sqlControl.executeStatementQuery(mconn, stmtText.toString(), ps);
                        try
                        {
                            // Check the type of result set needed
                            if (getResultSetType().equals("scroll-insensitive") ||
                                getResultSetType().equals("scroll-sensitive"))
                            {
                                qr = new ScrollableQueryResult(queryStmt, query, rof, rs,
                                    distinct ? null : candidateCollection);
                            }
                            else
                            {
                                qr = new ForwardQueryResult(queryStmt, query, rof, rs,
                                    distinct ? null : candidateCollection);
                            }

                            final QueryResult qr1 = qr;
                            final ManagedConnection mconn1 = mconn;
                            ManagedConnectionResourceListener listener = new ManagedConnectionResourceListener()
                            {
                                public void managedConnectionPreClose(){}
                                public void managedConnectionPostClose(){}
                                public void managedConnectionFlushed()
                                {
                                    // Disconnect the query from this ManagedConnection (read in unread rows etc)
                                    qr1.disconnect();
                                }
                                public void resourcePostClose()
                                {
                                    mconn1.removeListener(this);
                                }
                            };
                            mconn.addListener(listener);
                            ((AbstractRDBMSQueryResult)qr).addConnectionListener(listener);
                        }
                        finally
                        {
                            if (qr == null)
                            {
                                rs.close();
                            }
                        }
                    }
                    else
                    {
                        // UPDATE/DELETE query
                        int[] rcs = sqlControl.executeStatementUpdate(mconn, stmtText.toString(), ps, true);
                        JPOXLogger.JDO.info(">> Update statement returned " + rcs[0]);
                        // TODO Return the number of affected records
                    }
                }
                finally
                {
                    if (qr == null)
                    {
                        sqlControl.closeStatement(mconn, ps);
                    }
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(
                LOCALISER.msg("042007", queryStmt.toStatementText(useUpdateLock), e.getMessage()), e);
        }
        return qr;
       
    }

    /**
     * Accessor for the result set type.
     * Checks both the PMF, and also the query extensions.
     * @return The result set type string
     */
    protected String getResultSetType()
    {
        String propName = "org.jpox.rdbms.query.resultSetType";
        String rsTypeString = om.getOMFContext().getPersistenceConfiguration().getStringProperty(propName);
        Object rsTypeExt = query.getExtension(propName);
        if (rsTypeExt != null)
        {
            rsTypeString = (String)rsTypeExt;
        }
        return rsTypeString;
    }

    /**
     * Accessor for the result set concurrency.
     * Checks both the PMF, and also the query extensions.
     * @return The result set concurrency string
     */
    protected String getResultSetConcurrency()
    {
        String propName = "org.jpox.rdbms.query.resultSetConcurrency";
        String rsConcurrencyString = om.getOMFContext().getPersistenceConfiguration().getStringProperty(propName);
        Object rsConcurrencyExt = query.getExtension(propName);
        if (rsConcurrencyExt != null)
        {
            rsConcurrencyString = (String)rsConcurrencyExt;
        }
        return rsConcurrencyString;
    }

    /**
     * Method to create a PreparedStatement for use with the query.
     * @param conn the Connection
     * @param queryStmt The statement text for the query
     * @return the PreparedStatement
     * @throws SQLException Thrown if an error occurs creating the statement
     */
    protected PreparedStatement getStatement(ManagedConnection conn, String queryStmt)
    throws SQLException
    {
        // Apply any non-standard result set definition if required (either from the PMF, or via query extensions)
        String rsTypeString = getResultSetType();
        if (rsTypeString != null &&
            (!rsTypeString.equals("scroll-sensitive") && !rsTypeString.equals("forward-only") &&
             !rsTypeString.equals("scroll-insensitive")))
        {
            throw new JPOXUserException(
                "Query extension 'org.jpox.rdbms.query.resultSetType' has valid values of " +
                "scroll-sensitive,scroll-insensitive,forward-only only.");
        }
        String rsConcurrencyString = getResultSetConcurrency();
        if (rsConcurrencyString != null &&
            (!rsConcurrencyString.equals("read-only") && !rsConcurrencyString.equals("updateable")))
        {
            throw new JPOXUserException(
                "Query extension 'org.jpox.rdbms.query.resultSetConcurrency' has valid values of " +
                "read-only,updateable only.");
        }

        SQLController sqlControl = ((RDBMSManager)om.getStoreManager()).getSQLController();
        PreparedStatement ps = sqlControl.getStatementForQuery(conn, queryStmt, rsTypeString, rsConcurrencyString);

        return ps;
    }

    /**
     * Method to create a PreparedStatement for use with the query.
     * @param conn the Connection
     * @param stmtText The statement text
     * @return the PreparedStatement
     * @throws SQLException Thrown if an error occurs creating the statement
     */
    protected PreparedStatement getStatement(ManagedConnection conn, StatementText stmtText)
    throws SQLException
    {
        // Apply any non-standard result set definition if required (either from the PMF, or via query extensions)
        String rsTypeString = getResultSetType();
        if (rsTypeString != null &&
            (!rsTypeString.equals("scroll-sensitive") && !rsTypeString.equals("forward-only") &&
             !rsTypeString.equals("scroll-insensitive")))
        {
            throw new JPOXUserException(
                "Query extension 'org.jpox.rdbms.query.resultSetType' has valid values of " +
                "scroll-sensitive,scroll-insensitive,forward-only only.");
        }

        String rsConcurrencyString = getResultSetConcurrency();
        if (rsConcurrencyString != null &&
            (!rsConcurrencyString.equals("read-only") && !rsConcurrencyString.equals("updateable")))
        {
            throw new JPOXUserException(
                "Query extension 'JPOX org.jpox.rdbms.query.resultSetConcurrency' has valid values of " +
                "read-only,updateable only.");
        }

        SQLController sqlControl = ((RDBMSManager)om.getStoreManager()).getSQLController();
        PreparedStatement ps = sqlControl.getStatementForQuery(conn, stmtText.toString(), rsTypeString, rsConcurrencyString);
        stmtText.setParameters(om, ps);

        return ps;
    }

    /**
     * Method to apply any query timeouts, and to add any restrictions to the created ResultSet.
     * @param ps The PreparedStatement
     * @throws SQLException Thrown when an error occurs applying the constraints
     */
    protected void prepareStatementForExecution(PreparedStatement ps)
    throws SQLException
    {
        OMFContext omfCtx = om.getOMFContext();
        MappedStoreManager storeMgr = (MappedStoreManager)omfCtx.getStoreManager();
        PersistenceConfiguration conf = omfCtx.getPersistenceConfiguration();

        // Apply any user-specified timeout
        int timeout = conf.getIntProperty("org.jpox.query.timeout");
        Object timeoutExt = query.getExtension("org.jpox.query.timeout");
        if (timeoutExt != null)
        {
            // Accept timeout as an Integer or String
            if (timeoutExt instanceof Integer)
            {
                timeout = ((Integer)timeoutExt).intValue();
            }
            else if (timeoutExt instanceof String)
            {
                timeout = TypeConversionHelper.intFromString((String)timeoutExt, 0);
            }
        }
        if (timeout > 0)
        {
            ps.setQueryTimeout(timeout);
        }

        // Apply any fetch size
        int fetchSize = 0;
        if (query.getFetchPlan().getFetchSize() > 0)
        {
            // FetchPlan has a size set so use that
            fetchSize = query.getFetchPlan().getFetchSize();
        }
        if (storeMgr.getDatastoreAdapter().supportsQueryFetchSize(fetchSize))
        {
            ps.setFetchSize(fetchSize);
        }

        // Apply any fetch direction
        String propName = "org.jpox.rdbms.query.fetchDirection";
        String fetchDir = conf.getStringProperty(propName);
        Object fetchDirExt = query.getExtension(propName);
        if (fetchDirExt != null)
        {
            fetchDir = (String)fetchDirExt;
            if (!fetchDir.equals("forward") && !fetchDir.equals("reverse") && !fetchDir.equals("unknown"))
            {
                throw new JPOXUserException(
                    "Query extension for JPOX org.jpox.rdbms.query.fetchDirection has valid values of " +
                    "forward,reverse,unknown only");
            }
        }

        if (fetchDir.equals("reverse"))
        {
            ps.setFetchDirection(ResultSet.FETCH_REVERSE);
        }
        else if (fetchDir.equals("unknown"))
        {
            ps.setFetchDirection(ResultSet.FETCH_UNKNOWN);
        }
    }

    /**
     * Utility to take a ResultSet and return a ResultObjectFactory for extracting the results,
     * assuming that no candidate class is supplied. The QueryResult will return either a
     * result class type, or Object/Object[] depending on whether a ResultClass has been defined.
     * @param rs The ResultSet
     * @return The query ResultObjectFactory
     * @throws SQLException Thrown when an error occurs creating the QueryResult
     */
    protected ResultObjectFactory getResultObjectFactoryForNoCandidateClass(ResultSet rs, Class resultClass)
    throws SQLException
    {
        // No candidate class, so use resultClass or Object/Object[]
        Class requiredResultClass = resultClass;
        int numberOfColumns = 0;
        String[] resultFieldNames = null;
        try
        {
            ResultSetMetaData rsmd = rs.getMetaData();
            numberOfColumns = rsmd.getColumnCount();
            if (requiredResultClass == null)
            {
                if (numberOfColumns == 1)
                {
                    requiredResultClass = Object.class;
                }
                else
                {
                    requiredResultClass = Object[].class;
                }
            }

            // Generate names to use for the fields based on the column names
            resultFieldNames = new String[numberOfColumns];
            for (int i=0;i<numberOfColumns;i++)
            {
                resultFieldNames[i] = rsmd.getColumnName(i+1);
            }
        }
        catch (SQLException sqe)
        {
            // Do nothing
        }

        return new ResultClassROF(requiredResultClass, resultFieldNames);
    }   
}
TOP

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

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.