Package org.datanucleus.store.rdbms.scostore

Source Code of org.datanucleus.store.rdbms.scostore.RDBMSJoinSetStore$RDBMSJoinSetStoreSpecialization

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

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.Transaction;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.metadata.AbstractMemberMetaData;
import org.datanucleus.metadata.DiscriminatorStrategy;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.exceptions.NotYetFlushedException;
import org.datanucleus.store.mapped.StatementClassMapping;
import org.datanucleus.store.mapped.StatementMappingIndex;
import org.datanucleus.store.mapped.StatementParameterMapping;
import org.datanucleus.store.mapped.exceptions.MappedDatastoreException;
import org.datanucleus.store.mapped.mapping.ReferenceMapping;
import org.datanucleus.store.mapped.scostore.ElementContainerStore;
import org.datanucleus.store.mapped.scostore.JoinSetStore;
import org.datanucleus.store.query.ResultObjectFactory;
import org.datanucleus.store.rdbms.JDBCUtils;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.SQLController;
import org.datanucleus.store.rdbms.fieldmanager.DynamicSchemaFieldManager;
import org.datanucleus.store.rdbms.mapping.RDBMSMapping;
import org.datanucleus.store.rdbms.sql.DiscriminatorStatementGenerator;
import org.datanucleus.store.rdbms.sql.SQLStatement;
import org.datanucleus.store.rdbms.sql.SQLStatementHelper;
import org.datanucleus.store.rdbms.sql.SQLTable;
import org.datanucleus.store.rdbms.sql.StatementGenerator;
import org.datanucleus.store.rdbms.sql.UnionStatementGenerator;
import org.datanucleus.store.rdbms.sql.expression.SQLExpression;
import org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory;
import org.datanucleus.store.rdbms.table.CollectionTable;
import org.datanucleus.util.ClassUtils;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.NucleusLogger;

/**
* RDBMS-specific implementation of a {@link JoinSetStore}.
*/
public class RDBMSJoinSetStore extends JoinSetStore
{
    /** Statement to check the existence of an owner-element relation. */
    protected String locateStmt;

    /** Statement to get the maximum order column id so we can set the next insert value. */
    protected String maxOrderColumnIdStmt;

    /** JDBC statement to use for retrieving keys of the map (locking). */
    private String iteratorStmtLocked = null;

    /** JDBC statement to use for retrieving keys of the map (not locking). */
    private String iteratorStmtUnlocked = null;

    private StatementClassMapping iteratorMappingClass = null;
    private StatementParameterMapping iteratorMappingParams = null;

    /**
     * Constructor for a join set store for RDBMS.
     * @param mmd owner member metadata
     * @param joinTable The join table
     * @param clr The ClassLoaderResolver
     */
    public RDBMSJoinSetStore(AbstractMemberMetaData mmd, CollectionTable joinTable, ClassLoaderResolver clr)
    {
        super(mmd, joinTable, clr, joinTable.getOwnerMapping(), joinTable.getElementMapping(),
            joinTable.getOrderMapping(), joinTable.getRelationDiscriminatorMapping(),
            joinTable.getRelationDiscriminatorValue(), joinTable.isEmbeddedElement(),
            joinTable.isSerialisedElement(),
            new RDBMSJoinSetStoreSpecialization(LOCALISER, clr, (RDBMSStoreManager)joinTable.getStoreManager()));
    }

    private RDBMSStoreManager getStoreMgr()
    {
        return (RDBMSStoreManager) storeMgr;
    }

    private RDBMSAbstractSetStoreSpecialization getSpecialization()
    {
        return (RDBMSAbstractSetStoreSpecialization) specialization;
    }

    private static class RDBMSJoinSetStoreSpecialization extends RDBMSAbstractSetStoreSpecialization
    {
        private RDBMSJoinSetStoreSpecialization(Localiser localiser, ClassLoaderResolver clr, RDBMSStoreManager storeMgr)
        {
            super(localiser, clr, storeMgr);
        }

        @Override
        protected String getRemoveStmt(ElementContainerStore ecs)
        {
            return ((RDBMSJoinSetStore)ecs).getRemoveStmt();
        }
    }

    protected boolean removeAllInternal(ObjectProvider sm, Collection elements, int size)
    {
        boolean modified = false;

        String removeAllStmt = getRemoveAllStmt(sm, elements);
        try
        {
            ExecutionContext ec = sm.getExecutionContext();
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, removeAllStmt, false);
                try
                {
                    int jdbcPosition = 1;
                    Iterator iter = elements.iterator();
                    while (iter.hasNext())
                    {
                        Object element = iter.next();
                        jdbcPosition = BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                        jdbcPosition = BackingStoreHelper.populateElementInStatement(ec, ps, element, jdbcPosition, elementMapping);
                        if (relationDiscriminatorMapping != null)
                        {
                            jdbcPosition = BackingStoreHelper.populateRelationDiscriminatorInStatement(ec, ps, jdbcPosition, this);
                        }
                    }

                    int[] number = sqlControl.executeStatementUpdate(mconn, removeAllStmt, ps, true);
                    if (number[0] > 0)
                    {
                        modified = true;
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            NucleusLogger.DATASTORE.error(e);
            throw new NucleusDataStoreException(LOCALISER.msg("056012", removeAllStmt), e);
        }
        return modified;
    }

    /**
     * Generate statement for deleting items from the Set.
     * The EMBEDDEDFIELDX is only present when the elements are PC(embedded).
     * <PRE>
     * DELETE FROM SETTABLE
     * WHERE OWNERCOL=?
     * AND ELEMENTCOL = ?
     * [AND EMBEDDEDFIELD1 = ? AND EMBEDDEDFIELD2 = ? AND EMBEDDEDFIELD3 = ?]
     * [AND RELATION_DISCRIM = ?]
     * </PRE>
     * @return Statement for deleting items from the Set.
     */
    protected String getRemoveStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("DELETE FROM ");
        stmt.append(containerTable.toString());
        stmt.append(" WHERE ");
        stmt.append(ownerMapping.getDatastoreMapping(0).getDatastoreField().getIdentifier().toString());
        stmt.append(" = ");
        stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(0)).getUpdateInputParameter());
        for (int i = 1; i < ownerMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(" AND ");
            stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(i)).getUpdateInputParameter());
        }

        // TODO This doesn't allow for any of the element fields being "null" since in that situation we
        // would want the SQL to have "IS NULL" instead of "= NULL"
        for (int i = 0; i < elementMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(" AND ");
            stmt.append(elementMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            if (elementsAreSerialised)
            {
                // Can't directly compare serialised element fields
                stmt.append(" LIKE ");
            }
            else
            {
                stmt.append(" = ");
            }
            stmt.append(((RDBMSMapping) elementMapping.getDatastoreMapping(i)).getUpdateInputParameter());
        }

        if (relationDiscriminatorMapping != null)
        {
            for (int i = 0; i < relationDiscriminatorMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(" AND ");
                stmt.append(relationDiscriminatorMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping) relationDiscriminatorMapping.getDatastoreMapping(i)).getUpdateInputParameter());
            }
        }

        return stmt.toString();
    }

    /**
     * Generate statement for removing a collection of items from the Set.
     * <PRE>
     * DELETE FROM SETTABLE
     * WHERE (OWNERCOL=? AND ELEMENTCOL=?) OR
     *      (OWNERCOL=? AND ELEMENTCOL=?) OR
     *      (OWNERCOL=? AND ELEMENTCOL=?)
     * </PRE>
     * @param ownerSm The owner StateManager
     * @param elements Collection of elements to remove
     * @return Statement for deleting items from the Set.
     */
    protected String getRemoveAllStmt(ObjectProvider ownerSm, Collection elements)
    {
        if (elements == null || elements.size() == 0)
        {
            return null;
        }

        StringBuffer stmt = new StringBuffer();
        stmt.append("DELETE FROM ");
        stmt.append(containerTable.toString());
        stmt.append(" WHERE ");
        Iterator elementsIter = elements.iterator();
        boolean first = true;
        while (elementsIter.hasNext())
        {
            elementsIter.next(); // Not really used at the moment except to size the Collection
            if (first)
            {
                stmt.append("(");
            }
            else
            {
                stmt.append(" OR (");
            }
            for (int i = 0; i < ownerMapping.getNumberOfDatastoreMappings(); i++)
            {
                if (i > 0)
                {
                    stmt.append(" AND ");
                }
                stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(i)).getUpdateInputParameter());
            }
            for (int i = 0; i < elementMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(" AND ");
                stmt.append(elementMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                if (elementsAreSerialised)
                {
                    // Can't directly compare serialised element fields
                    stmt.append(" LIKE ");
                }
                else
                {
                    stmt.append(" = ");
                }
                stmt.append(((RDBMSMapping) elementMapping.getDatastoreMapping(i)).getUpdateInputParameter());
            }

            if (relationDiscriminatorMapping != null)
            {
                for (int i = 0; i < relationDiscriminatorMapping.getNumberOfDatastoreMappings(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping) relationDiscriminatorMapping.getDatastoreMapping(i)).getUpdateInputParameter());
                }
            }

            stmt.append(")");
            first = false;
        }

        return stmt.toString();
    }

    public boolean locate(ObjectProvider sm, Object element)
    {
        boolean exists = true;
        String stmt = getLocateStmt();
        try
        {
            ExecutionContext ec = sm.getExecutionContext();
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition =
                        BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                    jdbcPosition =
                        BackingStoreHelper.populateElementInStatement(ec, ps, element, jdbcPosition, elementMapping);
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = BackingStoreHelper.populateRelationDiscriminatorInStatement(ec, ps, jdbcPosition, this);
                    }

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        if (!rs.next())
                        {
                            exists = false;
                        }
                    }
                    catch (SQLException sqle)
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            NucleusLogger.DATASTORE.error(e);
            throw new NucleusDataStoreException(LOCALISER.msg("RDBMS.SCO.LocateRequestFailed", stmt), e);
        }
        return exists;
    }

    protected int[] doInternalAdd(ObjectProvider sm, Object element, ManagedConnection conn, boolean batched,
            int orderId, boolean executeNow)
    throws MappedDatastoreException
    {
        // Check for dynamic schema updates prior to addition
        if (storeMgr.getBooleanObjectProperty("datanucleus.rdbms.dynamicSchemaUpdates").booleanValue())
        {
            DynamicSchemaFieldManager dynamicSchemaFM = new DynamicSchemaFieldManager((RDBMSStoreManager)storeMgr, sm);
            Collection coll = new HashSet();
            coll.add(element);
            dynamicSchemaFM.storeObjectField(ownerMemberMetaData.getAbsoluteFieldNumber(), coll);
            if (dynamicSchemaFM.hasPerformedSchemaUpdates())
            {
                getSpecialization().invalidateAddStmt();
            }
        }

        String addStmt = getSpecialization().getAddStmt(this);
        boolean notYetFlushedError = false;
        ExecutionContext ec = sm.getExecutionContext();
        SQLController sqlControl = getStoreMgr().getSQLController();
        try
        {
            PreparedStatement ps = sqlControl.getStatementForUpdate(conn, addStmt, batched);
            try
            {
                // Insert the join table row
                int jdbcPosition = 1;
                jdbcPosition = BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                jdbcPosition = BackingStoreHelper.populateElementInStatement(ec, ps, element, jdbcPosition, elementMapping);
                if (orderMapping != null)
                {
                    jdbcPosition = BackingStoreHelper.populateOrderInStatement(ec, ps, orderId, jdbcPosition, orderMapping);
                }
                if (relationDiscriminatorMapping != null)
                {
                    jdbcPosition = BackingStoreHelper.populateRelationDiscriminatorInStatement(ec, ps, jdbcPosition, this);
                }

                // 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);
                }
            }
        }
        catch (SQLException e)
        {
            throw new MappedDatastoreException(addStmt, e);
        }
    }
    /**
     * Generate statement for checking the existence of an owner-element relation (used for M-N).
     * <PRE>
     * SELECT 1 FROM SETTABLE WHERE OWNERCOL = ? AND ELEMENTCOL = ?
     * </PRE>
     * @return Statement for locating an owner-element relation
     */
    private String getLocateStmt()
    {
        if (locateStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT 1 FROM ");
            stmt.append(containerTable.toString());
            stmt.append(" WHERE ");
            for (int i = 0; i < ownerMapping.getNumberOfDatastoreMappings(); i++)
            {
                if (i > 0)
                {
                    stmt.append(" AND ");
                }
                stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append("=");
                stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(i)).getInsertionInputParameter());
            }
            for (int i = 0; i < elementMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(" AND ");
                stmt.append(elementMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                if (elementsAreSerialised)
                {
                    // Can't directly compare serialised element fields
                    stmt.append(" LIKE ");
                }
                else
                {
                    stmt.append("=");
                }
                stmt.append(((RDBMSMapping) elementMapping.getDatastoreMapping(i)).getInsertionInputParameter());
            }

            if (relationDiscriminatorMapping != null)
            {
                for (int i = 0; i < relationDiscriminatorMapping.getNumberOfDatastoreMappings(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append("=");
                    stmt.append(((RDBMSMapping) relationDiscriminatorMapping.getDatastoreMapping(i)).getInsertionInputParameter());
                }
            }
            locateStmt = stmt.toString();
        }

        return locateStmt;
    }

    protected void preGetNextIDForOrderColumn(ManagedConnection mconn) throws MappedDatastoreException
    {
        SQLController sqlControl = getStoreMgr().getSQLController();
        try
        {
            // Process all waiting batched statements before we start our work
            sqlControl.processStatementsForConnection(mconn);
        }
        catch (SQLException e)
        {
            throw new MappedDatastoreException("SQLException", e);
        }
    }

    /**
     * Generate statement for obtaining the maximum id for the order column.
     * <PRE>
     * SELECT MAX(SCOID) FROM SETTABLE
     * WHERE OWNERCOL=?
     * [AND RELATION_DISCRIM=?]
     * </PRE>
     * @return The Statement returning the higher id
     */
    private String getMaxOrderColumnIdStmt()
    {
        if (maxOrderColumnIdStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("SELECT MAX(" + orderMapping.getDatastoreMapping(0).getDatastoreField().getIdentifier().toString() + ")");
            stmt.append(" FROM ");
            stmt.append(containerTable.toString());
            stmt.append(" WHERE ");
            stmt.append(ownerMapping.getDatastoreMapping(0).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(0)).getUpdateInputParameter());
            for (int i = 1; i < ownerMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(" AND ");
                stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping) ownerMapping.getDatastoreMapping(i)).getUpdateInputParameter());
            }

            if (relationDiscriminatorMapping != null)
            {
                for (int i = 1; i < relationDiscriminatorMapping.getNumberOfDatastoreMappings(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping) relationDiscriminatorMapping.getDatastoreMapping(i)).getUpdateInputParameter());
                }
            }
            maxOrderColumnIdStmt = stmt.toString();
        }

        return maxOrderColumnIdStmt;
    }

    protected int getNextIDForOrderColumn(ObjectProvider sm)
    {
        int nextID;
        ExecutionContext ec = sm.getExecutionContext();
        String stmt = getMaxOrderColumnIdStmt();
        try
        {
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();

            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                    if (relationDiscriminatorMapping != null)
                    {
                        BackingStoreHelper.populateRelationDiscriminatorInStatement(ec, ps, jdbcPosition, this);
                    }

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        if (!rs.next())
                        {
                            nextID = 1;
                        }
                        else
                        {
                            nextID = rs.getInt(1) + 1;
                        }

                        JDBCUtils.logWarnings(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("056020", stmt), e);
        }

        return nextID;
    }

    /**
     * Accessor for an iterator for the set.
     * @param ownerSM State Manager for the set.
     * @return Iterator for the set.
     */
    public Iterator iterator(ObjectProvider ownerSM)
    {
        ExecutionContext ec = ownerSM.getExecutionContext();
        if (iteratorStmtLocked == null)
        {
            synchronized (this) // Make sure this completes in case another thread needs the same info
            {
                // Generate the statement, and statement mapping/parameter information
                SQLStatement sqlStmt = getSQLStatementForIterator(ownerSM);
                iteratorStmtUnlocked = sqlStmt.getSelectStatement().toSQL();
                sqlStmt.addExtension("lock-for-update", true);
                iteratorStmtLocked = sqlStmt.getSelectStatement().toSQL();
            }
        }

        Transaction tx = ec.getTransaction();
        String stmt = (tx.lockReadObjects() ? iteratorStmtLocked : iteratorStmtUnlocked);
        try
        {
            ManagedConnection mconn = storeMgr.getConnection(ec);
            SQLController sqlControl = ((RDBMSStoreManager)storeMgr).getSQLController();
            try
            {
                // Create the statement and set the owner
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                StatementMappingIndex ownerIdx = iteratorMappingParams.getMappingForParameter("owner");
                int numParams = ownerIdx.getNumberOfParameterOccurrences();
                for (int paramInstance=0;paramInstance<numParams;paramInstance++)
                {
                    ownerIdx.getMapping().setObject(ec, ps,
                        ownerIdx.getParameterPositionsForOccurrence(paramInstance), ownerSM.getObject());
                }

                try
                {
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        if (elementsAreEmbedded || elementsAreSerialised)
                        {
                            // No ResultObjectFactory needed - handled by SetStoreIterator
                            return new RDBMSSetStoreIterator(ownerSM, rs, null, this);
                        }
                        else if (elementMapping instanceof ReferenceMapping)
                        {
                            // No ResultObjectFactory needed - handled by SetStoreIterator
                            return new RDBMSSetStoreIterator(ownerSM, rs, null, this);
                        }
                        else
                        {
                            ResultObjectFactory rof = storeMgr.newResultObjectFactory(emd,
                                iteratorMappingClass, false, null, clr.classForName(elementType));
                            return new RDBMSSetStoreIterator(ownerSM, rs, rof, this);
                        }
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("056006", stmt),e);
        }
        catch (MappedDatastoreException e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("056006", stmt),e);
        }
    }

    /**
     * Method to generate an SQLStatement for iterating through elements of the set.
     * Selects the join table.
     * Populates the iteratorMappingDef and iteratorMappingParams.
     * @param ownerSM StateManager for the owner object
     * @return The SQLStatement
     */
    protected SQLStatement getSQLStatementForIterator(ObjectProvider ownerSM)
    {
        SQLStatement sqlStmt = null;

        final ClassLoaderResolver clr = ownerSM.getExecutionContext().getClassLoaderResolver();
        RDBMSStoreManager storeMgr = (RDBMSStoreManager)this.storeMgr;
        SQLExpressionFactory exprFactory = storeMgr.getSQLExpressionFactory();
        if (elementsAreEmbedded || elementsAreSerialised)
        {
            // Element = embedded, serialised (maybe Non-PC)
            // Just select the join table since we're going to return the embedded/serialised columns from it
            sqlStmt = new SQLStatement(storeMgr, containerTable, null, null);
            sqlStmt.setClassLoaderResolver(clr);

            // Select the element column - first select is assumed by SetStoreIterator
            sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
        }
        else if (elementMapping instanceof ReferenceMapping)
        {
            // Element = Reference type (interface/Object)
            // Just select the join table since we're going to return the implementation id columns only
            sqlStmt = new SQLStatement(storeMgr, containerTable, null, null);
            sqlStmt.setClassLoaderResolver(clr);

            // Select the reference column(s) - first select is assumed by SetStoreIterator
            sqlStmt.select(sqlStmt.getPrimaryTable(), elementMapping, null);
        }
        else
        {
            // Element = PC
            // Join to the element table(s)
            iteratorMappingClass = new StatementClassMapping();
            for (int i = 0; i < elementInfo.length; i++)
            {
                // TODO This will only work if all element types have a discriminator
                final int elementNo = i;
                final Class elementCls = clr.classForName(elementInfo[elementNo].getClassName());
                SQLStatement elementStmt = null;
                if (elementInfo[elementNo].getDiscriminatorStrategy() != null &&
                    elementInfo[elementNo].getDiscriminatorStrategy() != DiscriminatorStrategy.NONE)
                {
                    // The element uses a discriminator so just use that in the SELECT
                    String elementType = ownerMemberMetaData.getCollection().getElementType();
                    if (ClassUtils.isReferenceType(clr.classForName(elementType)))
                    {
                        String[] clsNames = storeMgr.getNucleusContext().getMetaDataManager().getClassesImplementingInterface(
                            elementType, clr);
                        Class[] cls = new Class[clsNames.length];
                        for (int j = 0; j < clsNames.length; j++)
                        {
                            cls[j] = clr.classForName(clsNames[j]);
                        }

                        StatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, cls,
                            true, null, null, containerTable, null, elementMapping);
                        if (allowNulls)
                        {
                            stmtGen.setOption(StatementGenerator.OPTION_ALLOW_NULLS);
                        }
                        elementStmt = stmtGen.getStatement();
                    }
                    else
                    {
                        StatementGenerator stmtGen = new DiscriminatorStatementGenerator(storeMgr, clr, elementCls,
                            true, null, null, containerTable, null, elementMapping);
                        if (allowNulls)
                        {
                            stmtGen.setOption(StatementGenerator.OPTION_ALLOW_NULLS);
                        }
                        elementStmt = stmtGen.getStatement();
                    }
                    iterateUsingDiscriminator = true;
                }
                else
                {
                    // No discriminator, but subclasses so use UNIONs
                    StatementGenerator stmtGen = new UnionStatementGenerator(storeMgr, clr, elementCls, true, null,
                        null, containerTable, null, elementMapping);
                    stmtGen.setOption(StatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
                    iteratorMappingClass.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
                    elementStmt = stmtGen.getStatement();
                }

                if (sqlStmt == null)
                {
                    sqlStmt = elementStmt;
                }
                else
                {
                    sqlStmt.union(elementStmt);
                }
            }

            // Select the required fields
            SQLTable elementSqlTbl = sqlStmt.getTable(elementInfo[0].getDatastoreClass(),
                sqlStmt.getPrimaryTable().getGroupName());
            SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, iteratorMappingClass,
                ownerSM.getExecutionContext().getFetchPlan(), elementSqlTbl, emd, 0);
        }

        // Apply condition on join-table owner field to filter by owner
        SQLTable ownerSqlTbl =
            SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), ownerMapping);
        SQLExpression ownerExpr = exprFactory.newExpression(sqlStmt, ownerSqlTbl, ownerMapping);
        SQLExpression ownerVal = exprFactory.newLiteralParameter(sqlStmt, ownerMapping, null, "OWNER");
        sqlStmt.whereAnd(ownerExpr.eq(ownerVal), true);

        if (relationDiscriminatorMapping != null)
        {
            // Apply condition on distinguisher field to filter by distinguisher (when present)
            SQLTable distSqlTbl =
                SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), relationDiscriminatorMapping);
            SQLExpression distExpr = exprFactory.newExpression(sqlStmt, distSqlTbl, relationDiscriminatorMapping);
            SQLExpression distVal = exprFactory.newLiteral(sqlStmt, relationDiscriminatorMapping, relationDiscriminatorValue);
            sqlStmt.whereAnd(distExpr.eq(distVal), true);
        }

        if (orderMapping != null)
        {
            // Order by the ordering column, when present
            SQLTable orderSqlTbl =
                SQLStatementHelper.getSQLTableForMappingOfTable(sqlStmt, sqlStmt.getPrimaryTable(), orderMapping);
            SQLExpression[] orderExprs = new SQLExpression[orderMapping.getNumberOfDatastoreMappings()];
            boolean descendingOrder[] = new boolean[orderMapping.getNumberOfDatastoreMappings()];
            orderExprs[0] = exprFactory.newExpression(sqlStmt, orderSqlTbl, orderMapping);
            sqlStmt.setOrdering(orderExprs, descendingOrder);
        }

        // Input parameter(s) - the owner
        int inputParamNum = 1;
        StatementMappingIndex ownerIdx = new StatementMappingIndex(ownerMapping);
        if (sqlStmt.getNumberOfUnions() > 0)
        {
            // Add parameter occurrence for each union of statement
            for (int j=0;j<sqlStmt.getNumberOfUnions()+1;j++)
            {
                int[] paramPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
                for (int k=0;k<paramPositions.length;k++)
                {
                    paramPositions[k] = inputParamNum++;
                }
                ownerIdx.addParameterOccurrence(paramPositions);
            }
        }
        else
        {
            int[] paramPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
            for (int k=0;k<paramPositions.length;k++)
            {
                paramPositions[k] = inputParamNum++;
            }
            ownerIdx.addParameterOccurrence(paramPositions);
        }
        iteratorMappingParams = new StatementParameterMapping();
        iteratorMappingParams.addMappingForParameter("owner", ownerIdx);

        return sqlStmt;
    }
}
TOP

Related Classes of org.datanucleus.store.rdbms.scostore.RDBMSJoinSetStore$RDBMSJoinSetStoreSpecialization

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.