Package org.jpox.store.rdbms.scostore

Source Code of org.jpox.store.rdbms.scostore.AbstractArrayStore

/**********************************************************************
Copyright (c) 2005 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.jpox.store.rdbms.scostore;

import java.lang.reflect.Array;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import org.jpox.ClassLoaderResolver;
import org.jpox.ManagedConnection;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.Transaction;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXException;
import org.jpox.store.StoreManager;
import org.jpox.store.exceptions.NotYetFlushedException;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.expression.LogicSetExpression;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.expression.ScalarExpression;
import org.jpox.store.mapped.expression.StringLiteral;
import org.jpox.store.mapped.mapping.EmbeddedElementPCMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.ReferenceMapping;
import org.jpox.store.mapped.mapping.SerialisedPCMapping;
import org.jpox.store.mapped.mapping.SerialisedReferenceMapping;
import org.jpox.store.mapped.scostore.ArrayStoreQueryable;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.SQLController;
import org.jpox.store.rdbms.table.JoinTable;
import org.jpox.store.scostore.ArrayStore;
import org.jpox.util.JPOXLogger;

/**
* Abstract representation of the backing store for an array.
*
* @version $Revision: 1.37 $
*/
public abstract class AbstractArrayStore extends ElementContainerStore implements ArrayStore, ArrayStoreQueryable
{
    /**
     * Constructor.
     * @param storeMgr Manager for the store
     * @param clr ClassLoader resolver
     */
    protected AbstractArrayStore(StoreManager storeMgr, ClassLoaderResolver clr)
    {
        super(storeMgr, clr);
    }

    /**
     * Accessor for the array from the datastore.
     * @param ownerSM SM for the owner
     * @return The array (as a List of objects)
     */
    public List getArray(StateManager ownerSM)
    {
        Iterator iter = iterator(ownerSM);
        List elements = new ArrayList();
        while (iter.hasNext())
        {
            Object obj = iter.next();
            elements.add(obj);
        }

        return elements;
    }

    /**
     * Clear the association from owner to all elements.
     * Observes the necessary dependent field settings with respect to whether
     * it should delete the element when doing so.
     * @param ownerSM State Manager for the container.
     */
    public void clear(StateManager ownerSM)
    {
        Collection dependentElements = null;
        if (ownerMemberMetaData.getArray().isDependentElement())
        {
            // Retain the dependent elements that need deleting after clearing
            dependentElements = new HashSet();
            Iterator iter = iterator(ownerSM);
            while (iter.hasNext())
            {
                dependentElements.add(iter.next());
            }
        }

        String clearStmt = getClearStmt();
        try
        {
            ObjectManager om = ownerSM.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, clearStmt, false);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(ownerSM, om, ps, jdbcPosition);
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                    }

                    sqlControl.executeStatementUpdate(mconn, clearStmt, ps, true);
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056013", clearStmt), e);
        }

        if (dependentElements != null && dependentElements.size() > 0)
        {
            ownerSM.getObjectManager().deleteObjects(dependentElements.toArray());
        }
    }

    /**
     * Method to set the array for the specified owner to the passed value.
     * @param ownerSM State Manager for the owner
     * @param array the array
     * @return Whether the array was updated successfully
     */
    public boolean set(StateManager ownerSM, Object array)
    {
        if (array == null || Array.getLength(array) == 0)
        {
            return true;
        }

        // Validate all elements for writing
        int length = Array.getLength(array);
        for (int i=0;i<length;i++)
        {
            Object obj = Array.get(array, i);
            validateElementForWriting(ownerSM, obj, null);
        }

        boolean modified = false;
        List exceptions = new ArrayList();
        boolean batched = allowsBatching() && length > 1;

        try
        {
            ObjectManager om = ownerSM.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            sqlControl.processStatementsForConnection(mconn); // Process all waiting batched statements before we start our work

            try
            {
                // Loop through all elements to be added
                Object element = null;
                for (int i=0;i<length;i++)
                {
                    element = Array.get(array, i);

                    try
                    {
                        // Add the row to the join table
                        int[] rc = internalAdd(ownerSM, element, mconn, batched, i, (i == length-1));
                        if (rc != null)
                        {
                            for (int j=0;j<rc.length;j++)
                            {
                                if (rc[j] > 0)
                                {
                                    // At least one record was inserted
                                    modified = true;
                                }
                            }
                        }
                    }
                    catch (SQLException sqle)
                    {
                        sqle.printStackTrace();
                        exceptions.add(sqle);
                        JPOXLogger.DATASTORE.error(sqle);
                    }
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            e.printStackTrace();
            exceptions.add(e);
            JPOXLogger.DATASTORE.error(e);
        }

        if (!exceptions.isEmpty())
        {
            // Throw all exceptions received as the cause of a JPOXDataStoreException so the user can see which record(s) didn't persist
            String msg = LOCALISER.msg("056009", getAddStmt());
            JPOXLogger.DATASTORE.error(msg);
            throw new JPOXDataStoreException(msg, (Throwable[])exceptions.toArray(new Throwable[exceptions.size()]), ownerSM.getObject());
        }

        return modified;
    }

    /**
     * Adds one element to the association owner vs elements
     * @param sm State Manager for the container
     * @param element The element to add
     * @param position The position to add this element at
     * @return Whether it was successful
     */
    public boolean add(StateManager sm, Object element, int position)
    {
        validateElementForWriting(sm, element, null);

        boolean modified = false;

        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);

            try
            {
                // Add a row to the join table
                int[] returnCode = internalAdd(sm, element, mconn, false, position, true);
                if (returnCode[0] > 0)
                {
                    modified = true;
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056009", getAddStmt()), e);
        }

        return modified;
    }

    /**
     * Internal method to add a row to the join table.
     * Used by add() and set() to add a row to the join table.
     * @param sm StateManager for the owner of the collection
     * @param element The element to add the relation to
     * @param conn The connection
     * @param batched Whether we are batching
     * @param orderId The order id to use for this element relation
     * @param executeNow Whether to execute the statement now (and not wait for any batch)
     * @return Whether a row was inserted
     * @throws SQLException Thrown if an error occurs
     */
    protected int[] internalAdd(StateManager sm, Object element, ManagedConnection conn, boolean batched, int orderId, boolean executeNow)
    throws SQLException
    {
        ObjectManager om = sm.getObjectManager();
        SQLController sqlControl = storeMgr.getSQLController();
        String addStmt = getAddStmt();
        PreparedStatement ps = sqlControl.getStatementForUpdate(conn, addStmt, false);
        boolean notYetFlushedError = false;
        try
        {
            // Insert the join table row
            int jdbcPosition = 1;
            jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
            jdbcPosition = populateElementInStatement(om, ps, element, jdbcPosition);
            jdbcPosition = populateOrderInStatement(om, ps, orderId, jdbcPosition);
            if (relationDiscriminatorMapping != null)
            {
                jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
            }

            // Execute the statement
            return sqlControl.executeStatementUpdate(conn, addStmt, ps, executeNow);
        }
        catch (NotYetFlushedException nfe)
        {
            notYetFlushedError = true;
            throw nfe;
        }
        finally
        {
            if (notYetFlushedError)
            {
                sqlControl.abortStatementForConnection(conn, ps);
            }
            else
            {
                sqlControl.closeStatement(conn, ps);
            }
        }
    }

    /**
     * Accessor for the statement for the iterator.
     * @param ownerSM the owner StateManager
     * @return The iterator Query Statement.
     **/
    protected abstract QueryExpression getIteratorStatement(StateManager ownerSM);

    /**
     * Accessor for an iterator through the array elements.
     * @param ownerSM State Manager for the container.
     * @return The Iterator
     **/
    public Iterator iterator(StateManager ownerSM)
    {
        // Create the basic statement (without any selected columns)
        QueryExpression stmt = getIteratorStatement(ownerSM);
        if (stmt == null)
        {
            throw new JPOXException(LOCALISER.msg("056005")).setFatal();
        }

        // Add the required field selections to the statement and get a factory for extracting them
        ResultObjectFactory rof = newResultObjectFactory(ownerSM, stmt, false, true);

        ObjectManager om = ownerSM.getObjectManager();
        Transaction tx = om.getTransaction();
        boolean useUpdateLock = ((Boolean)tx.getOptions().get("transaction.serializeReadObjects")).booleanValue();
        String statement = storeMgr.getStatementTextForQuery(stmt, useUpdateLock);
        if (statement == null)
        {
            // this happens when collection is null
            throw new JPOXException(LOCALISER.msg("056005")).setFatal();
        }

        Iterator iter;
        try
        {
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = storeMgr.getStatementForQuery(stmt, om, mconn, useUpdateLock, null, null);
                try
                {
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, statement, ps);
                    try
                    {
                        iter = new ArrayStoreIterator(ownerSM, rs, rof);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056006", statement), e);
        }

        return iter;
    }

    /**
     * Inner class providing iterator.
     **/
    private class ArrayStoreIterator implements Iterator
    {
        private final ObjectManager om;
        private final Iterator delegate;
        private Object lastElement = null;

        /**
         * Constructor
         * @param sm the StateManager
         * @param rs the ResultSet
         * @param rof the Query.ResultObjectFactory
         * @throws SQLException
         */
        public ArrayStoreIterator(StateManager sm, ResultSet rs, ResultObjectFactory rof)
        throws SQLException
        {
            this.om = sm.getObjectManager();

            ArrayList results = new ArrayList();
            if (rs != null)
            {
                while (rs.next())
                {
                    Object nextElement;
                    if (elementsAreEmbedded || elementsAreSerialised)
                    {
                        int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                        for (int i = 0; i < param.length; ++i)
                        {
                            param[i] = i + 1;
                        }

                        if (elementMapping instanceof SerialisedPCMapping ||
                            elementMapping instanceof SerialisedReferenceMapping ||
                            elementMapping instanceof EmbeddedElementPCMapping)
                        {
                            // Element = Serialised
                            int ownerFieldNumber = -1;
                            if (containerTable != null)
                            {
                                ownerFieldNumber = ((JoinTable)containerTable).getOwnerFieldMetaData().getAbsoluteFieldNumber();
                            }
                            nextElement = elementMapping.getObject(om, rs, param, sm, ownerFieldNumber);
                        }
                        else
                        {
                            // Element = Non-PC
                            nextElement = elementMapping.getObject(om, rs, param);
                        }
                    }
                    else if (elementMapping instanceof ReferenceMapping)
                    {
                        // Element = Reference (Interface/Object)
                        int param[] = new int[elementMapping.getNumberOfDatastoreFields()];
                        for (int i = 0; i < param.length; ++i)
                        {
                            param[i] = i + 1;
                        }
                        nextElement = elementMapping.getObject(om, rs, param);
                    }
                    else
                    {
                        // Element = PC
                        nextElement = rof.getObject(om, rs);
                    }

                    results.add(nextElement);
                }
            }
            delegate = results.iterator();
        }

        public boolean hasNext()
        {
            return delegate.hasNext();
        }

        public Object next()
        {
            lastElement = delegate.next();

            return lastElement;
        }

        public synchronized void remove()
        {
            // Do nothing
        }
    }
   
    /**
     * Query utility to generate an exists() statement for an element.
     * The generated query will be of the form
     * <PRE>
     * SELECT 1 FROM JOINTABLE THIS_JOIN WHERE THIS_JOIN.OWNER_ID_OID = THIS.OWNER_ID
     * </PRE>
     * @param qs The JDOQL query statement
     * @param mapping mapping of the field
     * @param ownerTe Expression for the table
     * @param arrayTableAlias alias for the array
     * @return The JDOQL query statement
     */
    public QueryExpression getExistsSubquery(QueryExpression qs,
                                             JavaTypeMapping mapping,
                                             LogicSetExpression ownerTe,
                                             DatastoreIdentifier arrayTableAlias)
    {
        QueryExpression stmt = dba.newQueryStatement(containerTable, arrayTableAlias, qs.getClassLoaderResolver());
        stmt.setParent(qs);

        // Join for the owner
        ScalarExpression ownerExpr = mapping.newScalarExpression(stmt, ownerTe);
        ScalarExpression ownerInCollectionExpr = ownerMapping.newScalarExpression(stmt, stmt.getTableExpression(arrayTableAlias));
        stmt.andCondition(ownerExpr.eq(ownerInCollectionExpr));

        stmt.select(arrayTableAlias, elementMapping);

        return stmt;
    }
   
    /**
     * Query utility to generate a subquery for the size() of the collection.
     * The generated query will be of the form
     * <PRE>
     * SELECT COUNT(*) FROM JOINTABLE THIS_JOIN WHERE THIS_JOIN.OWNER_ID_OID = THIS.OWNER_ID
     * </PRE>
     * @param qs The query statement
     * @param mapping mapping of the field
     * @param ownerTe Expression for the table
     * @param arrayTableAlias alias for the array
     * @return The query statement
     */
    public QueryExpression getSizeSubquery(QueryExpression qs,
                                           JavaTypeMapping mapping,
                                           LogicSetExpression ownerTe,
                                           DatastoreIdentifier arrayTableAlias)
    {
        QueryExpression stmt = dba.newQueryStatement(containerTable, arrayTableAlias, qs.getClassLoaderResolver());
        stmt.setParent(qs);

        // Join for the owner
        ScalarExpression ownerExpr = mapping.newScalarExpression(stmt, ownerTe);
        ScalarExpression ownerInCollectionExpr = ownerMapping.newScalarExpression(stmt, stmt.getTableExpression(arrayTableAlias));
        stmt.andCondition(ownerExpr.eq(ownerInCollectionExpr));

        // Select COUNT(*)
        JavaTypeMapping m = dba.getMapping(String.class, storeMgr);
        StringLiteral lit = (StringLiteral)m.newLiteral(stmt, "COUNT(*)");
        lit.generateStatementWithoutQuotes();
        stmt.selectScalarExpression(lit);

        return stmt;
    }   
}
TOP

Related Classes of org.jpox.store.rdbms.scostore.AbstractArrayStore

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.