All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.datanucleus.store.db4o.query.SQLQueryResult Maven / Gradle / Ivy

/**********************************************************************
Copyright (c) 2008 Andy Jefferson 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:
    ...
**********************************************************************/
package org.datanucleus.store.db4o.query;

import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Map;
import java.util.NoSuchElementException;

import org.datanucleus.FetchPlan;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.QueryResultMetaData;
import org.datanucleus.query.QueryUtils;
import org.datanucleus.sql4o.ObjectSetWrapper;
import org.datanucleus.sql4o.Result;
import org.datanucleus.sql4o.Sql4oException;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.db4o.DB4OStoreManager;
import org.datanucleus.store.db4o.DB4OUtils;
import org.datanucleus.store.query.AbstractQueryResult;
import org.datanucleus.store.query.Query;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.SoftValueMap;
import org.datanucleus.util.StringUtils;
import org.datanucleus.util.WeakValueMap;

import com.db4o.ObjectContainer;

/**
 * Result from an SQL query with db4o.
 * Takes the sql4o result and converts it into the result format expected by JDO/JPA SQL definitions.
 * Provides caching of results in this object, controlled by property "datanucleus.query.resultCacheType".
 */
public class SQLQueryResult extends AbstractQueryResult
{
    /** Whether to load any unread results at commit (when connection is closed). */
    private boolean loadResultsAtCommit = true; // Default to load

    /** Map of ResultSet object values, keyed by the list index ("0", "1", etc). */
    private Map resultsObjsByIndex = null;

    /** Size of results, if known. -1 otherwise. */
    protected int size = -1;

    /** DB4O Object Container, to obtain any more information from where required. */
    ObjectContainer objectContainer;

    /** Results from sql4o. */
    ObjectSetWrapper results;

    QueryResultMetaData resultMetaData;
    Class resultClass;
    Class candidateClass;

    /**
     * Constructor.
     * @param query Query being executed
     * @param cont Object Container in use
     * @param results The results from sql4o
     */
    public SQLQueryResult(Query query, ObjectContainer cont, ObjectSetWrapper results, 
            QueryResultMetaData resultMetaData)
    {
        super(query);
        this.objectContainer = cont;
        this.results = results;
        this.resultMetaData = resultMetaData;
        this.resultClass = query.getResultClass();
        this.candidateClass = query.getCandidateClass();

        String ext = (String)query.getExtension("datanucleus.query.loadResultsAtCommit");
        if (ext != null)
        {
            loadResultsAtCommit = new Boolean(ext).booleanValue();
        }

        ext = (String)query.getExtension("datanucleus.query.resultCacheType");
        if (ext != null)
        {
            if (ext.equalsIgnoreCase("soft"))
            {
                resultsObjsByIndex = new SoftValueMap();
            }
            else if (ext.equalsIgnoreCase("weak"))
            {
                resultsObjsByIndex = new WeakValueMap();
            }
            else if (ext.equalsIgnoreCase("hard"))
            {
                resultsObjsByIndex = new HashMap();
            }
            else if (ext.equalsIgnoreCase("none"))
            {
                resultsObjsByIndex = null;
            }
            else
            {
                resultsObjsByIndex = new WeakValueMap();
            }
        }
        else
        {
            resultsObjsByIndex = new WeakValueMap();
        }

        size = results.size();

        if (resultsObjsByIndex != null)
        {
            // Caching results so load up any result objects needed right now
            int fetchSize = query.getFetchPlan().getFetchSize();
            if (!query.getObjectManager().getTransaction().isActive() || fetchSize == FetchPlan.FETCH_SIZE_GREEDY)
            {
                // No transaction or in "greedy" mode so load all results now
                for (int i=0;i 0)
            {
                // Load up the first "fetchSize" objects now
                for (int i=0;i 0 we have a previous
                return (iterRowNum > 0);
            }
        }

        public Object next()
        {
            synchronized (SQLQueryResult.this)
            {
                if (!isOpen())
                {
                    // Spec 14.6.7 Calling next() on closed Query will throw NoSuchElementException
                    throw new NoSuchElementException(LOCALISER.msg("052600"));
                }

                if (!hasNext())
                {
                    throw new NoSuchElementException("No next element");
                }
                Object obj = getObjectForIndex(iterRowNum);
                iterRowNum++;

                return obj;
            }
        }

        public int nextIndex()
        {
            if (hasNext())
            {
                return iterRowNum;
            }
            return size();
        }

        public Object previous()
        {
            synchronized (SQLQueryResult.this)
            {
                if (!isOpen())
                {
                    // Spec 14.6.7 Calling previous() on closed Query will throw NoSuchElementException
                    throw new NoSuchElementException(LOCALISER.msg("052600"));
                }

                if (!hasPrevious())
                {
                    throw new NoSuchElementException("No previous element");
                }

                iterRowNum--;
                return getObjectForIndex(iterRowNum);
            }
        }

        public int previousIndex()
        {
            if (iterRowNum == 0)
            {
                return -1;
            }
            return iterRowNum-1;
        }

        public void remove()
        {
            throw new UnsupportedOperationException(LOCALISER.msg("052603"));
        }

        public void set(Object obj)
        {
            throw new UnsupportedOperationException(LOCALISER.msg("052603"));
        }
    }

    /**
     * Accessor for the result object at an index.
     * If the object has already been processed will return that object,
     * otherwise will retrieve the object using the factory.
     * @param index The list index position
     * @return The result object
     */
    protected Object getObjectForIndex(int index)
    {
        Object obj = null;
        if (resultsObjsByIndex != null)
        {
            // Caching objects, so check the cache for this index
            obj = resultsObjsByIndex.get("" + index);
            if (obj != null)
            {
                // Already retrieved so return it
                return obj;
            }
        }

        if (resultMetaData != null)
        {
            // Each row of the ResultSet is defined by MetaData
            obj = getRowForResultMetaData(index);
        }
        else if (resultClass != null)
        {
            // Each row of the ResultSet is an instance of resultClass
            obj = getRowForResultClass(index);
        }
        else if (candidateClass == null)
        {
            // Each row of the ResultSet is an Object or Object[]
            obj = getRowForNoCandidateClass(index);
        }
        else
        {
            // Each row of the ResultSet is an instance of the candidate class
            obj = getRowForCandidateClass(index);
        }

        if (resultsObjsByIndex != null)
        {
            // Put it in our cache, keyed by the list index
            resultsObjsByIndex.put("" + index, obj);
        }

        return obj;
    }

    /**
     * Accessor for the row object(s) when there is metadata defining the result.
     * Each row of results should follow the result metadata (JPA).
     * @param index Row index
     * @return The row in the required form
     */
    protected Object getRowForResultMetaData(int index)
    {
        // TODO Implement this
        return null;
    }

    /**
     * Accessor for the row object(s) when there is a candidate class.
     * Each row of results should be of candidate type.
     * @param index Row index
     * @return The row in the required form
     */
    protected Object getRowForCandidateClass(int index)
    {
        // Current sql4o doesnt support joins and so when we have a candidate class each row
        // is an object of the candidate type.
        // TODO Cater for case where user only selects identity field(s)
        Result obj = results.get(index);
        Object row = obj.getBaseObject(0); // Note : the param is for when joins are supported in sql4o
        prepareObjectForUse(row, objectContainer);
        return row;
    }

    /**
     * Method to prepare results when there is a result class.
     * Each row of results should be an instance of ResultClass.
     * @param index The row number
     * @return The results in the required form
     */
    protected Object getRowForResultClass(int index)
    {
        if (resultClass == Object[].class)
        {
            return getRowForNoCandidateClass(index);
        }

        if (QueryUtils.resultClassIsSimple(resultClass.getName()))
        {
            // User wants a single field
            /*if (fieldValues.length == 1 && 
                (fieldValues[0] == null || resultClass.isAssignableFrom(fieldValues[0].getClass())))
            {
                // Simple object is the correct type so just give them the field
                return fieldValues[0];
            }
            else if (fieldValues.length == 1 && !resultClass.isAssignableFrom(fieldValues[0].getClass()))
            {
                // Simple object is not assignable to the ResultClass so throw an error
                String msg = LOCALISER.msg("021202",
                    resultClass.getName(), fieldValues[0].getClass().getName());
                JPOXLogger.QUERY.error(msg);
                throw new JPOXUserException(msg);
            }*/
        }
        else
        {
            
        }

        // TODO Implement this
        return null;
    }

    /**
     * Accessor for the row object(s) when there is no candidate class.
     * Each row of results should be of the form Object (when 1 column) or Object[] (when multiple).
     * @param index Row index
     * @return The row in the required form
     */
    protected Object getRowForNoCandidateClass(int index)
    {
        int numCols = results.getNumberOfFields();
        try
        {
            Result obj = results.get(index);
            if (numCols > 0)
            {
                if (numCols > 1)
                {
                    Object[] row = new Object[numCols];
                    for (int i=0;i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy