Package org.jpox.store.rdbms.scostore

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

/**********************************************************************
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.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;

import org.jpox.ClassLoaderResolver;
import org.jpox.FetchPlan;
import org.jpox.ManagedConnection;
import org.jpox.ObjectManager;
import org.jpox.ObjectManagerHelper;
import org.jpox.StateManager;
import org.jpox.FetchPlan.FetchPlanForClass;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.CollectionMetaData;
import org.jpox.metadata.DiscriminatorStrategy;
import org.jpox.metadata.FieldPersistenceModifier;
import org.jpox.metadata.IdentityType;
import org.jpox.sco.SCOUtils;
import org.jpox.store.FieldValues;
import org.jpox.store.StoreManager;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.IdentifierFactory;
import org.jpox.store.mapped.MappedStoreManager;
import org.jpox.store.mapped.StatementExpressionIndex;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.mapping.AbstractContainerMapping;
import org.jpox.store.mapped.mapping.EmbeddedElementPCMapping;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.Mappings;
import org.jpox.store.mapped.mapping.ReferenceMapping;
import org.jpox.store.query.ResultObjectFactory;
import org.jpox.store.rdbms.SQLController;
import org.jpox.store.rdbms.SQLWarnings;
import org.jpox.store.rdbms.fieldmanager.ParameterSetter;
import org.jpox.store.rdbms.mapping.RDBMSMapping;
import org.jpox.store.rdbms.query.PersistentIDROF;
import org.jpox.store.rdbms.table.JoinTable;
import org.jpox.util.ClassUtils;

/**
* Representation of the store of an element-based container.
* This is used to represent either a collection or an array.
* There are 3 types of situation that we try to cater for with respect to elements.
* <UL>
* <LI><B>element-type</B> is PC with "new-table" or "superclass-table" inheritance. In this case we will have
* <I>elementInfo</I> with 1 entry.</LI>
* <LI><B>element-type</B> is PC with "subclass-table" inheritance. In this case we will have <I>elementInfo</I>
* with "n" entries (1 for each subclass type with its own table). We also have <I>emd</I> being the MetaData
* for the element-type.</LI>
* <LI><B>element-type</B> is Reference type. In this case we will have <I>elementInfo</I> with "n" entries
* (1 for each implementation type).</LI>
* <LI><B>element-type</B> is non-PC. In this case we have no <I>elementInfo</I> and no <I>emd</I></LI>
* </UL>
*
* @version $Revision: 1.79 $
*/
public abstract class ElementContainerStore extends BaseContainerStore
{
    /** Flag to set whether the iterator statement will use a discriminator or not. */
    protected boolean iterateUsingDiscriminator = false;

    /**
     * Information for the elements of this container.
     * When the "element-type" table is new-table, or superclass-table then there is 1 value here.
     * When the "element-type" table uses subclass-table, or when it is a reference type then there can be multiple.
     */
    protected ElementInfo[] elementInfo;

    /** Inner class wrapping the information required for a valid element type. */
    public class ElementInfo
    {
        AbstractClassMetaData cmd; // MetaData for the element class
        DatastoreClass table; // Table storing the element

        /**
         * Constructor
         * @param cmd the AbstractClassMetaData
         * @param table the DatastoreClass
         */
        public ElementInfo(AbstractClassMetaData cmd, DatastoreClass table)
        {
            this.cmd = cmd;
            this.table = table;
        }

        /**
         * Accessor for the class name
         * @return the class name
         */
        public String getClassName()
        {
            return cmd.getFullClassName();
        }

        /**
         * Accessor for the AbstractClassMetaData
         * @return the AbstractClassMetaData
         */
        public AbstractClassMetaData getAbstractClassMetaData()
        {
            return cmd;
        }

        /**
         * Accessor for the table of the element
         * @return the DatastoreClass
         */
        public DatastoreClass getDatastoreClass()
        {
            return table;
        }

        /**
         * Accessor the discriminator strategy of the element
         * @return the strategy for the discriminator
         */
        public DiscriminatorStrategy getDiscriminatorStrategy()
        {
            return cmd.getDiscriminatorStrategy();
        }

        /**
         * Accessor the discriminator mapping of the element (in its table)
         * @return the JavaTypeMapping for the discriminator
         */
        public JavaTypeMapping getDiscriminatorMapping()
        {
            return table.getDiscriminatorMapping(false);
        }
    }

    /** MetaData for the "element-type" class. Not used for reference types since no metadata is present for the declared type. */
    protected AbstractClassMetaData emd;

    /** Table containing the link between owner and element. Not set when using FK relations. */
    protected DatastoreContainerObject containerTable;

    /** Mapping for the element. */
    protected JavaTypeMapping elementMapping;

    /** Type of the element. */
    protected String elementType;

    /** Whether the elements are embedded. */
    protected boolean elementsAreEmbedded;

    /** Whether the elements are serialised. */
    protected boolean elementsAreSerialised;

    /** Whether the element is of a persistent-interface (defined using "<interface>") type. */
    protected boolean elementIsPersistentInterface = false;

    /**
     * Mapping for an ordering column to allow for duplicates in the container.
     * Can also be used for ordering elements in a List/array.
     * Can also be used where we have an embedded object and so need to form the PK with something.
     */
    protected JavaTypeMapping orderMapping;

    /** Optional mapping to distinguish elements of one collection from another when sharing the join table. */
    protected JavaTypeMapping relationDiscriminatorMapping;

    /** Value to use to discriminate between elements of this collection from others using the same join table. */
    protected String relationDiscriminatorValue;

    /** Identifier for elements in JDOQL queries. */
    protected final DatastoreIdentifier elmIdentifier;

    /** ClassLoader resolver. */
    protected ClassLoaderResolver clr;

    /** Statement for getting the size of the container. */
    private String sizeStmt;

    /** Statement for clearing the container. */
    protected String clearStmt;

    /** Statement for adding an element to the container. */
    protected String addStmt;

    /** Statement for removing an element from the container. */
    protected String removeStmt;

    /** Whether we are using a discriminator in the "size" statement. */
    protected boolean usingDiscriminatorInSizeStmt = false;

    /**
     * Constructor.
     * @param storeMgr Manager for the store
     * @param clr ClassLoader resolver
     */
    protected ElementContainerStore(StoreManager storeMgr, ClassLoaderResolver clr)
    {
        super(storeMgr);
        this.clr = clr;

        elmIdentifier = ((MappedStoreManager)storeMgr).getIdentifierFactory().newIdentifier(IdentifierFactory.TABLE, "ELEMENT");
    }

    /**
     * Convenience method to find the element information relating to the element type.
     * Used specifically for the "element-type" of a collection/array to find the elements
     * which have table information. Not used for reference types.
     * @return Element information relating to the element type
     */
    protected ElementInfo[] getElementInformationForClass()
    {
        ElementInfo[] info = null;
        DatastoreClass tbl;
        String[] clsNames;
        if (!clr.classForName(elementType).isInterface())
        {
            tbl = storeMgr.getDatastoreClass(elementType, clr);
            clsNames = new String[] {elementType};
        }
        else
        {
            clsNames = storeMgr.getOMFContext().getMetaDataManager().getClassesImplementingInterface(elementType, clr);
            tbl = storeMgr.getDatastoreClass(clsNames[0], clr);
        }
        if (tbl == null)
        {
            AbstractClassMetaData[] subclassCmds = storeMgr.getClassesManagingTableForClass(emd, clr);
            info = new ElementInfo[subclassCmds.length];
            for (int i=0;i<subclassCmds.length;i++)
            {
                DatastoreClass table = storeMgr.getDatastoreClass(subclassCmds[i].getFullClassName(), clr);
                info[i] = new ElementInfo(subclassCmds[i], table);
            }
        }
        else
        {
            info = new ElementInfo[clsNames.length];
            for (int i=0; i<clsNames.length; i++)
            {
                AbstractClassMetaData cmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(clsNames[i], clr);
                DatastoreClass table = storeMgr.getDatastoreClass(cmd.getFullClassName(), clr);
                info[i] = new ElementInfo(cmd, table);
            }
        }
        return info;
    }

    /**
     * Accessor for whether the store has an order mapping, to allow for duplicates or ordering.
     * @return Whether an order mapping is present.
     */
    public boolean hasOrderMapping()
    {
        return (orderMapping != null);
    }

    /**
     * Accessor for the element type stored in this container.
     * @return The element type.
     **/
    public String getElementType()
    {
        return elementType;
    }

    /**
     * Method to validate an element against the accepted type.
     * @param clr The ClassLoaderResolver
     * @param element The element to validate
     * @return Whether it is valid.
     **/
    protected boolean validateElementType(ClassLoaderResolver clr, Object element)
    {
        if (element == null)
        {
            return true;
        }

        Class primitiveElementClass = ClassUtils.getPrimitiveTypeForType(element.getClass());
        if (primitiveElementClass != null)
        {
           
            // Allow for the element type being primitive, and the user wanting to store its wrapper
            String elementTypeWrapper = elementType;
            Class elementTypeClass = clr.classForName(elementType);
            if (elementTypeClass.isPrimitive())
            {
                elementTypeWrapper = ClassUtils.getWrapperTypeForPrimitiveType(elementTypeClass).getName();
            }
            return clr.isAssignableFrom(elementTypeWrapper, element.getClass());
        }
        return clr.isAssignableFrom(elementType, element.getClass());
    }

    /**
     * Method to check if an element is already persistent or is persistent but managed by
     * a different persistence manager.
     * @param sm The state manager of this owner
     * @param element The element
     * @return Whether it is valid for reading.
     */
    protected boolean validateElementForReading(StateManager sm, Object element)
    {
        if (!validateElementType(sm.getObjectManager().getClassLoaderResolver(), element))
        {
            return false;
        }

        if (element != null && !elementsAreEmbedded && !elementsAreSerialised)
        {
            ObjectManager om = sm.getObjectManager();
            if ((!om.getApiAdapter().isPersistent(element) ||
                 om != ObjectManagerHelper.getObjectManager(element)) && !om.getApiAdapter().isDetached(element))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Method to check if an element is already persistent, or is managed by a different
     * Persistencemanager. If not persistent, this will persist it.
     * @param sm The state manager of this owner
     * @param element The element
     * @param fieldValues any initial field values to use if persisting the element
     * @return Whether the element was persisted during this call
     */
    protected boolean validateElementForWriting(StateManager sm, Object element, FieldValues fieldValues)
    {
        // Check the element type for this collection
        if (!elementIsPersistentInterface &&
            !validateElementType(sm.getObjectManager().getClassLoaderResolver(), element))
        {
            throw new ClassCastException(LOCALISER.msg("056033", element.getClass().getName(),
                ownerMemberMetaData.getFullFieldName(), elementType));
        }

        boolean persisted = false;
        if (elementsAreEmbedded || elementsAreSerialised)
        {
            // Element is embedded/serialised so has no id
        }
        else
        {
            ObjectManager om = sm.getObjectManager();
            StateManager elementSM = om.findStateManager(element);
            if (elementSM != null && elementSM.isEmbedded())
            {
                // Element is already with StateManager and is embedded in another field!
                throw new JPOXUserException(LOCALISER.msg("056028",
                    ownerMemberMetaData.getFullFieldName(), element));
            }

            persisted = SCOUtils.validateObjectForWriting(om, element, fieldValues);
        }
        return persisted;
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the value for the element.
     * Not used with embedded PC elements.
     * @param om Object Manager
     * @param ps The PreparedStatement
     * @param element The element
     * @param jdbcPosition Position in JDBC statement to populate
     * @return The next position in the JDBC statement
     */
    protected int populateElementInStatement(ObjectManager om, PreparedStatement ps, Object element, int jdbcPosition)
    {
        if (!((RDBMSMapping)elementMapping.getDataStoreMapping(0)).insertValuesOnInsert())
        {
            // Dont try to insert any mappings with insert parameter that isnt ? (e.g Oracle)
            return jdbcPosition;
        }
        elementMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, elementMapping), element);
        return jdbcPosition + elementMapping.getNumberOfDatastoreFields();
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the value for the order index.
     * @param om Object Manager
     * @param ps The PreparedStatement
     * @param idx The order value
     * @param jdbcPosition Position in JDBC statement to populate
     * @return The next position in the JDBC statement
     */
    protected int populateOrderInStatement(ObjectManager om, PreparedStatement ps, int idx, int jdbcPosition)
    {
        orderMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, orderMapping), new Integer(idx));
        return jdbcPosition + orderMapping.getNumberOfDatastoreFields();
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the value for the distinguisher value.
     * @param om Object Manager
     * @param ps The PreparedStatement
     * @param jdbcPosition Position in JDBC statement to populate
     * @return The next position in the JDBC statement
     */
    protected int populateRelationDiscriminatorInStatement(ObjectManager om, PreparedStatement ps, int jdbcPosition)
    {
        relationDiscriminatorMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, relationDiscriminatorMapping), relationDiscriminatorValue);
        return jdbcPosition + relationDiscriminatorMapping.getNumberOfDatastoreFields();
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the value from the
     * element discriminator, optionally including all subclasses of the element type.
     * @param om Persistence Manager
     * @param ps The PreparedStatement
     * @param jdbcPosition Position in JDBC statement to populate
     * @param includeSubclasses Whether to include subclasses
     * @param info The element information
     * @return The next position in the JDBC statement
     */
    protected int populateElementDiscriminatorInStatement(ObjectManager om,
                                                          PreparedStatement ps,
                                                          int jdbcPosition,
                                                          boolean includeSubclasses,
                                                          ElementInfo info)
    {
        DiscriminatorStrategy strategy = info.getDiscriminatorStrategy();
        JavaTypeMapping discrimMapping = info.getDiscriminatorMapping();

        // Include element type
        if (strategy == DiscriminatorStrategy.CLASS_NAME)
        {
            discrimMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, discrimMapping), info.getClassName());
            jdbcPosition += discrimMapping.getNumberOfDatastoreFields();
        }
        else if (strategy == DiscriminatorStrategy.VALUE_MAP)
        {
            discrimMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, discrimMapping),
                info.getAbstractClassMetaData().getInheritanceMetaData().getDiscriminatorMetaData().getValue());
            jdbcPosition += discrimMapping.getNumberOfDatastoreFields();
        }

        // Include all subclasses
        if (includeSubclasses)
        {
            HashSet subclasses = storeMgr.getSubClassesForClass(info.getClassName(), true, clr);
            if (subclasses != null && subclasses.size() > 0)
            {
                Iterator iter = subclasses.iterator();
                while (iter.hasNext())
                {
                    String subclass = (String)iter.next();
                    if (strategy == DiscriminatorStrategy.CLASS_NAME)
                    {
                        discrimMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, discrimMapping), subclass);
                        jdbcPosition += discrimMapping.getNumberOfDatastoreFields();
                    }
                    else if (strategy == DiscriminatorStrategy.VALUE_MAP)
                    {
                        AbstractClassMetaData subclassCmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(subclass, clr);
                        discrimMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, discrimMapping),
                            subclassCmd.getInheritanceMetaData().getDiscriminatorMetaData().getValue());
                        jdbcPosition += discrimMapping.getNumberOfDatastoreFields();
                    }
                }
            }
        }
        return jdbcPosition;
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the field values from
     * the embedded element starting at the specified jdbc position.
     * @param sm State Manager of the owning container
     * @param element The embedded element
     * @param ps The PreparedStatement
     * @param jdbcPosition JDBC position in the statement to start at
     * @param joinTable The Join table where the elements are embedded
     * @return The next JDBC position
     */
    protected int populateEmbeddedElementFieldsInStatement(StateManager sm,
                                                           Object element,
                                                           PreparedStatement ps,
                                                           int jdbcPosition,
                                                           JoinTable joinTable)
    {
        EmbeddedElementPCMapping embeddedMapping = (EmbeddedElementPCMapping)elementMapping;
        StatementExpressionIndex[] statementExpressionIndex =
            new StatementExpressionIndex[emd.getNoOfManagedMembers() + emd.getNoOfInheritedManagedMembers()];
        int[] elementFieldNumbers = new int[embeddedMapping.getNumberOfJavaTypeMappings()];
        for (int i=0;i<embeddedMapping.getNumberOfJavaTypeMappings();i++)
        {
            JavaTypeMapping fieldMapping = embeddedMapping.getJavaTypeMapping(i);
            int absFieldNum = emd.getAbsolutePositionOfMember(fieldMapping.getFieldMetaData().getName());
            elementFieldNumbers[i] = absFieldNum;
            if (fieldMapping != null)
            {
                statementExpressionIndex[absFieldNum] = new StatementExpressionIndex();
                statementExpressionIndex[absFieldNum].setMapping(fieldMapping);
                int[] jdbcParamPositions = new int[fieldMapping.getNumberOfDatastoreFields()];
                for (int j=0;j<fieldMapping.getNumberOfDatastoreFields();j++)
                {
                    jdbcParamPositions[j] = jdbcPosition++;
                }
                statementExpressionIndex[absFieldNum].setParameterIndex(jdbcParamPositions);
            }
        }

        StateManager elementSM = getStateManagerForEmbeddedPCObject(sm, element, joinTable);
        elementSM.setPcObjectType(StateManager.EMBEDDED_COLLECTION_ELEMENT_PC);
        elementSM.provideFields(elementFieldNumbers, new ParameterSetter(elementSM, ps, statementExpressionIndex, true));

        return jdbcPosition;
    }

    /**
     * Generate statement for clearing the container.
     * <PRE>
     * DELETE FROM CONTAINERTABLE
     * WHERE OWNERCOL = ?
     * [AND RELATION_DISCRIM=?]
     * </PRE>
     * TODO Add a discriminator restriction on this statement so we only clear ones with a valid discriminator value
     * @return Statement for clearing the container.
     */
    protected String getClearStmt()
    {
        if (clearStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("DELETE FROM ");
            stmt.append(containerTable.toString());
            stmt.append(" WHERE ");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    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=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(" AND ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }
            clearStmt = stmt.toString();
        }

        return clearStmt;
    }

    /**
     * Accessor for an iterator through the container elements.
     * @param ownerSM State Manager for the container.
     * @return The Iterator
     */
    public abstract Iterator iterator(StateManager ownerSM);

    /**
     * Clear the association from owner to all elements.
     * Provides cascade-delete when the elements being deleted are PC types.
     * @param ownerSM State Manager for the container.
     */
    public void clear(StateManager ownerSM)
    {
        Collection dependentElements = null;
        CollectionMetaData collmd = ownerMemberMetaData.getCollection();
        if (collmd.isDependentElement() && !collmd.isEmbeddedElement() && !collmd.isSerializedElement())
        {
            // 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);
        }

        // Cascade-delete
        if (dependentElements != null && dependentElements.size() > 0)
        {
            Iterator iter = dependentElements.iterator();
            while (iter.hasNext())
            {
                Object obj = iter.next();
                if (ownerSM.getObjectManager().getApiAdapter().isDeleted(obj))
                {
                    // Element is tagged for deletion so will be deleted at flush(), and we dont need it immediately
                }
                else
                {
                    ownerSM.getObjectManager().deleteObjectInternal(obj);
                }
            }
        }
    }

    /**
     * Generate statement for getting the size of the container.
     * The order part is only present when an order mapping is used.
     * The discriminator part is only present when the element has a discriminator.
     * <PRE>
     * SELECT COUNT(*) FROM TBL THIS
     * [INNER JOIN ELEM_TBL ELEM ON TBL.COL = ELEM.ID] - when no null
     * [LEFT OUTER JOIN ELEM_TBL ELEM ON TBL.COL = ELEM.ID] - when allows null
     * WHERE THIS.OWNERCOL=?
     * [AND THIS.ORDERCOL IS NOT NULL]
     * [AND (DISCRIMINATOR=? OR DISCRMINATOR=? OR DISCRIMINATOR=? [OR DISCRIMINATOR IS NULL])]
     * [AND RELATION_DISCRIM=?]
     * </PRE>
     * The discriminator part includes all subclasses of the element type.
     * If the element is in a different table to the container then an INNER JOIN will
     * be present to link the two tables, and table aliases will be present also.
     * TODO Update this to allow for getting the size when more than 1 element table.
     * @return The Statement returning the size of the container.
     */
    protected String getSizeStmt()
    {
        if (sizeStmt != null && !usingDiscriminatorInSizeStmt)
        {
            // Statement exists and didnt need any discriminator when setting up the statement so just reuse it
            return sizeStmt;
        }

        boolean allowNulls = false;
        if (ownerMemberMetaData != null &&
            ownerMemberMetaData.hasExtension("allow-nulls") &&
            ownerMemberMetaData.getValueForExtension("allow-nulls").equalsIgnoreCase("true"))
        {
            allowNulls = true;
        }

        StringBuffer stmt = new StringBuffer();
        String containerAlias = "THIS";
        String joinedElementAlias = "ELEM";
        stmt.append("SELECT COUNT(*) FROM ");
        stmt.append(containerTable.toString()).append(" ").append(containerAlias);

        // Add join to element table if required (only allows for 1 element table currently)
        boolean joinedDiscrim = false;
        if (elementInfo != null && elementInfo.length == 1 &&
            elementInfo[0].getDatastoreClass() != containerTable &&
            elementInfo[0].getDiscriminatorMapping() != null)
        {
            // TODO Allow for more than 1 possible element table
            // Need join to the element table to restrict the discriminator
            joinedDiscrim = true;
            JavaTypeMapping elemIdMapping = elementInfo[0].getDatastoreClass().getIDMapping();
            if (allowNulls)
            {
                // User wants to allow for nulls so have to use left outer join
                stmt.append(" LEFT OUTER JOIN ");
            }
            else
            {
                // No nulls so use inner join
                stmt.append(" INNER JOIN ");
            }
            stmt.append(elementInfo[0].getDatastoreClass().toString()).append(" ").append(joinedElementAlias).append(" ON ");
            for (int i=0;i<elementMapping.getNumberOfDatastoreFields();i++)
            {
                if (i > 0)
                {
                    stmt.append(" AND ");
                }
                stmt.append(containerAlias).append(".").append(elementMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier());
                stmt.append("=");
                stmt.append(joinedElementAlias).append(".").append(elemIdMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier());
            }
        }

        stmt.append(" WHERE ");
        for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(" AND ");
            }
            stmt.append(containerAlias).append(".").append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append("=");
            stmt.append(((RDBMSMapping)ownerMapping.getDataStoreMapping(i)).getUpdateInputParameter());
        }

        if (orderMapping != null)
        {
            // If an ordering is present, restrict to items where the index is not null to
            // eliminate records that are added but may not be positioned yet.
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(containerAlias).append(".").append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(">=0");
            }
        }

        if (elementInfo != null && elementInfo.length == 1)
        {
            // TODO Support more than one element table
            // Add a discriminator filter for collections with an element discriminator
            StringBuffer discrStmt = new StringBuffer();
            for (int i=0; i<elementInfo.length; i++)
            {
                if (elementInfo[i].getDiscriminatorMapping() != null)
                {
                    usingDiscriminatorInSizeStmt = true;
                    if (discrStmt.length() > 0)
                    {
                        discrStmt.append(" OR ");
                    }
                    JavaTypeMapping discrimMapping = elementInfo[i].getDiscriminatorMapping();
                    for (int j=0;j<discrimMapping.getNumberOfDatastoreFields();j++)
                    {
                        if (joinedDiscrim)
                        {
                            discrStmt.append(joinedElementAlias);
                        }
                        else
                        {
                            discrStmt.append(containerAlias);
                        }
                        discrStmt.append(".");
                        discrStmt.append(discrimMapping.getDataStoreMapping(j).getDatastoreField().getIdentifier().toString());
                        discrStmt.append("=");
                        discrStmt.append(((RDBMSMapping)discrimMapping.getDataStoreMapping(j)).getUpdateInputParameter());
                    }

                    HashSet subclasses = storeMgr.getSubClassesForClass(elementInfo[i].getClassName(), true, clr);
                    if (subclasses != null && subclasses.size() > 0)
                    {
                        for (int j=0;j<subclasses.size();j++)
                        {
                            for (int k=0;k<discrimMapping.getNumberOfDatastoreFields();k++)
                            {
                                discrStmt.append(" OR ");
                                if (joinedDiscrim)
                                {
                                    discrStmt.append(joinedElementAlias);
                                }
                                else
                                {
                                    discrStmt.append(containerAlias);
                                }
                                discrStmt.append(".");
                                discrStmt.append(discrimMapping.getDataStoreMapping(k).getDatastoreField().getIdentifier().toString());
                                discrStmt.append("=");
                                discrStmt.append(((RDBMSMapping)discrimMapping.getDataStoreMapping(k)).getUpdateInputParameter());
                            }
                        }
                    }
                }
            }
            if (discrStmt.length() > 0)
            {
                stmt.append(" AND (");
                stmt.append(discrStmt);
                if (allowNulls)
                {
                    stmt.append(" OR ");
                    stmt.append(elementInfo[0].getDiscriminatorMapping().getDataStoreMapping(0).getDatastoreField().getIdentifier().toString());
                    stmt.append(" IS NULL");
                }
                stmt.append(")");
            }
        }
        if (relationDiscriminatorMapping != null)
        {
            for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(" AND ");
                stmt.append(containerAlias).append(".").append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append("=");
                stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
        }

        sizeStmt = stmt.toString();
        return sizeStmt;
    }

    /**
     * Method to return the size of the container.
     * @param sm The state manager.
     * @return The size.
     **/
    public int size(StateManager sm)
    {
        int numRows;

        String sizeStmt = getSizeStmt();
        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, sizeStmt);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    if (elementInfo != null && elementInfo.length == 1)
                    {
                        // TODO Allow for multiple element types (e.g interface implementations)
                        for (int i=0; i<elementInfo.length; i++)
                        {
                            if (elementInfo[i].getDiscriminatorMapping() != null)
                            {
                                jdbcPosition = populateElementDiscriminatorInStatement(om, ps, jdbcPosition, true, elementInfo[i]);
                            }
                        }
                    }
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                    }

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, sizeStmt, ps);
                    try
                    {
                        if (!rs.next())
                        {
                            throw new JPOXDataStoreException(LOCALISER.msg("056007",sizeStmt));
                        }

                        numRows = rs.getInt(1);
                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056007",sizeStmt),e);
        }

        return numRows;
    }

    /**
     * Generates the statement for adding items.
     * The EMBEDDEDFIELDX columns are only added for embedded PC elements.
     * <PRE>
     * INSERT INTO COLLTABLE (OWNERCOL,[ELEMENTCOL],[EMBEDDEDFIELD1, EMBEDDEDFIELD2,...],[ORDERCOL])
     * VALUES (?,?,?)
     * </PRE>
     * @return The Statement for adding an item
     */
    protected String getAddStmt()
    {
        if (addStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("INSERT INTO ");
            stmt.append(containerTable.toString());
            stmt.append(" (");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            }

            for (int i=0; i<elementMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(",");
                stmt.append(elementMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            }

            if (orderMapping != null)
            {
                for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                }
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                }
            }

            stmt.append(") VALUES (");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(((RDBMSMapping)ownerMapping.getDataStoreMapping(i)).getInsertionInputParameter());
            }

            for (int i=0; i<elementMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(",");
                stmt.append(((RDBMSMapping)elementMapping.getDataStoreMapping(0)).getInsertionInputParameter());
            }

            if (orderMapping != null)
            {
                for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(((RDBMSMapping)orderMapping.getDataStoreMapping(0)).getInsertionInputParameter());
                }
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(0)).getInsertionInputParameter());
                }
            }

            stmt.append(") ");

            addStmt = stmt.toString();
        }

        return addStmt;
    }

    // ----------------------------- Query Methods --------------------------------

    /**
     * Method to generate a new result object factory from which to retrieve element objects.
     * @param sm State Manager for owner
     * @param stmt The query
     * @param ignoreCache Whether to ignore the cache
     * @param useFetchPlan Whether to use the current FetchPlan
     * @return The Result object factory
     */
    public ResultObjectFactory newResultObjectFactory(StateManager sm,
                                                            QueryExpression stmt,
                                                            boolean ignoreCache,
                                                            boolean useFetchPlan)
    {
        ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
        // TODO Allow for more than just the first elementTable (currently take the columns to select from the first element table only)
        if (elementsAreEmbedded || elementsAreSerialised)
        {
            return new PersistentIDROF(null , null, emd, null, null, null, ignoreCache, false,
                stmt.hasMetaDataExpression(), null, clr.classForName(elementType));
        }
        else if (elementMapping instanceof ReferenceMapping)
        {
            // Element = Reference (Interface/Object)
            // TODO Respect any fetch plan for the implementation class
            // This selects the id column(s) in the join table of all implementations of the reference type
            // It is processed by ReferenceMapping where it decides which of the implementation types is stored in this row.
            JavaTypeMapping[] implMappings = ((ReferenceMapping)elementMapping).getJavaTypeMapping();
            for (int i=0;i<implMappings.length;i++)
            {
                stmt.select(implMappings[i]);
            }

            return new PersistentIDROF(elementInfo != null ? elementInfo[0].getDatastoreClass() : null, null, emd,
                    null, null, null, ignoreCache, false,
                    stmt.hasMetaDataExpression(), null, clr.classForName(elementType));
        }
        else
        {
            // Element = PC
            int[] datastoreIndex = null;
            int[] versionIndex = null;

            // Select any datastore/version columns
            if (stmt.getTableExpression(elmIdentifier) != null)
            {
                if (elementInfo[0].getDatastoreClass().getIdentityType() == IdentityType.DATASTORE)
                {
                    datastoreIndex = stmt.select(elmIdentifier, elementInfo[0].getDatastoreClass().getDataStoreObjectIdMapping(), true);
                }
                JavaTypeMapping verMapping = elementInfo[0].getDatastoreClass().getVersionMapping(true);
                if (verMapping != null)
                {
                    versionIndex = stmt.select(elmIdentifier, verMapping, true);
                }
            }
            else
            {
                if (elementInfo[0].getDatastoreClass().getIdentityType() == IdentityType.DATASTORE)
                {
                    datastoreIndex = stmt.select(stmt.getMainTableAlias(),
                        elementInfo[0].getDatastoreClass().getDataStoreObjectIdMapping(),true);
                }
                JavaTypeMapping verMapping = elementInfo[0].getDatastoreClass().getVersionMapping(true);
                if (verMapping != null)
                {
                    versionIndex = stmt.select(stmt.getMainTableAlias(), verMapping, true);
                }
            }

            StatementExpressionIndex[] statementExpressionIndex = null;
            int[] prefetchFieldNumbers = null;
            if (useFetchPlan)
            {
                // Select the FetchPlan fields
                FetchPlan fp = sm.getObjectManager().getFetchPlan();
                fp.manageFetchPlanForClass(emd);
                FetchPlanForClass fpc = fp.getFetchPlanForClass(emd);
                int fieldNumbers[] = fpc.getFieldsInActualFetchPlan();
                int fn[] = new int[fieldNumbers.length];
                int prefetchFieldCount = 0;
                int fieldCount = emd.getNoOfInheritedManagedMembers() + emd.getNoOfManagedMembers();

                statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                for (int i=0; i<fieldNumbers.length; ++i)
                {
                    AbstractMemberMetaData fmd = emd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]);
                    if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT)
                    {
                        JavaTypeMapping m = elementInfo[0].getDatastoreClass().getFieldMapping(fmd);
                        if (m != null) // Field is not a Collection, List, Map, etc so has a datastore mapping
                        {
                            if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                            {
                                statementExpressionIndex[fieldNumbers[i]] = new StatementExpressionIndex();
                                statementExpressionIndex[fieldNumbers[i]].setMapping(m);
                                fn[prefetchFieldCount++] = fieldNumbers[i];
                            }
                        }
                    }
                }
               
                prefetchFieldNumbers = new int[prefetchFieldCount];
                System.arraycopy(fn, 0, prefetchFieldNumbers, 0, prefetchFieldCount);
            }
            else
            {
                // if no fetchplan is used and is application identity, select only PK fields
                if (emd.getIdentityType() == IdentityType.APPLICATION)
                {
                    prefetchFieldNumbers = new int[emd.getPKMemberPositions().length];
                    int fieldCount = emd.getNoOfInheritedManagedMembers() + emd.getNoOfManagedMembers();
                    statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                    for (int i = 0; i < prefetchFieldNumbers.length; ++i)
                    {
                        prefetchFieldNumbers[i] = emd.getPKMemberPositions()[i];
                        AbstractMemberMetaData fmd = emd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[i]);
                        if (fmd.getPersistenceModifier() == FieldPersistenceModifier.PERSISTENT)
                        {
                            JavaTypeMapping m = elementInfo[0].getDatastoreClass().getFieldMapping(fmd);
                            if (m != null) // Field is not a Collection, List, Map, etc so has a datastore mapping
                            {
                                if (m.includeInFetchStatement() && !(m instanceof AbstractContainerMapping))
                                {
                                    statementExpressionIndex[prefetchFieldNumbers[i]] = new StatementExpressionIndex();
                                    statementExpressionIndex[prefetchFieldNumbers[i]].setMapping(m);
                                }
                            }
                        }
                    }
                }
            }

            if (stmt.getTableExpression(elmIdentifier) != null)
            {
                Mappings.selectMapping(stmt, elmIdentifier, statementExpressionIndex);
            }
            else
            {
                Mappings.selectMapping(stmt, statementExpressionIndex);
            }

            return new PersistentIDROF(elementInfo != null ? elementInfo[0].getDatastoreClass() : null,
                prefetchFieldNumbers, emd, statementExpressionIndex, datastoreIndex, versionIndex,
                ignoreCache, iterateUsingDiscriminator,
                stmt.hasMetaDataExpression(), null, clr.classForName(elementType));
        }
    }
}
TOP

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

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.