Package org.jpox.store.rdbms.scostore

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

/**********************************************************************
Copyright (c) 2002 Mike Martin (TJDO) 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:
2003 Erik Bengtson - removed unused import
2003 Andy Jefferson - coding standards
2003 Andy Jefferson - addition of getGetStatement for inherited values
2004 Andy Jefferson - addition of query methods
2004 Andy Jefferson - moved statements from subclasses
2005 Andy Jefferson - allow for embedded keys/values
    ...
**********************************************************************/
package org.jpox.store.rdbms.scostore;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;

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.Transaction;
import org.jpox.FetchPlan.FetchPlanForClass;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.metadata.AbstractClassMetaData;
import org.jpox.metadata.IdentityType;
import org.jpox.sco.SCOUtils;
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.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.AbstractContainerMapping;
import org.jpox.store.mapped.mapping.EmbeddedKeyPCMapping;
import org.jpox.store.mapped.mapping.EmbeddedValuePCMapping;
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.mapped.mapping.SerialisedPCMapping;
import org.jpox.store.mapped.mapping.SerialisedReferenceMapping;
import org.jpox.store.mapped.scostore.MapStoreQueryable;
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.store.scostore.MapStore;

/**
* Abstract representation of the backing store for a Map.
*
* @version $Revision: 1.97 $
**/
abstract class AbstractMapStore extends BaseContainerStore implements MapStore, MapStoreQueryable
{
    /** Flag to set whether the iterator statement will use a discriminator or not. */
    protected boolean iterateUsingDiscriminator = false;

    protected DatastoreContainerObject mapTable;
    protected DatastoreClass valueTable;
    protected AbstractClassMetaData kmd;
    protected AbstractClassMetaData vmd;

    /** Mapping to the key. */
    protected JavaTypeMapping keyMapping;

    /** Mapping to the value. */
    protected JavaTypeMapping valueMapping;

    /** Type of the key. */
    protected String keyType;

    /** Type of the value. */
    protected String valueType;

    /** Whether the keys are embedded. */
    protected boolean keysAreEmbedded;

    /** Whether the keys are serialised. */
    protected boolean keysAreSerialised;

    /** Whether the values are embedded. */
    protected boolean valuesAreEmbedded;

    /** Whether the values are serialised. */
    protected boolean valuesAreSerialised;

    protected String containsValueStmt;

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

    /**
     * Constructor.
     * @param storeMgr Manager for the store
     **/
    public AbstractMapStore(StoreManager storeMgr)
    {
        super(storeMgr);

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

    /**
     * Method to initialise the statements being used.
     * Subclasses should override the getXXXStmt() if they want to use an
     * alternative statement.
     **/
    protected void initialiseStatements()
    {
        containsValueStmt = getContainsValueStmt();
    }

    /**
     * Accessor for whether the keys are embedded or not.
     * If they are PC instances then returns false;
     * @return Whether the keys are embedded
     */
    public boolean keysAreEmbedded()
    {
        return keysAreEmbedded;
    }

    /**
     * Accessor for whether the keys are serialised or not.
     * If they are PC instances then returns false;
     * @return Whether the keys are serialised
     */
    public boolean keysAreSerialised()
    {
        return keysAreSerialised;
    }

    /**
     * Accessor for whether the values are embedded or not.
     * If they are PC instances then returns false;
     * @return Whether the values are embedded
     */
    public boolean valuesAreEmbedded()
    {
        return valuesAreEmbedded;
    }

    /**
     * Accessor for whether the values are serialised or not.
     * If they are PC instances then returns false;
     * @return Whether the values are serialised
     */
    public boolean valuesAreSerialised()
    {
        return valuesAreSerialised;
    }

    /**
     * Accessor for the key type for storing in this Map.
     * @return The type of the key
     **/
    public String getKeyType()
    {
        return keyType;
    }

    /**
     * Accessor for the value type for storing in this Map.
     * Types inherited from this (base) type can also be stored as values.
     * @return The type of the value (base class).
     **/
    public String getValueType()
    {
        return valueType;
    }

    // ------------------------- Public Methods --------------------------------
    /**
     * Method to check if a key exists in the Map.
     * @param sm State Manager for the map
     * @param key The key to check for.
     * @return Whether the key exists in the Map.
     **/
    public boolean containsKey(StateManager sm, Object key)
    {
        if( key == null )
        {
            //nulls not allowed
            return false;
        }
        try
        {
            getValue(sm, key);
            return true;
        }
        catch (NoSuchElementException e)
        {
            return false;
        }
    }

    /**
     * Method to check if a value exists in the Map.
     * @param sm State Manager for the map
     * @param value The value to check for.
     * @return Whether the value exists in the Map.
     **/
    public boolean containsValue(StateManager sm, Object value)
    {
        if( value == null )
        {
            //nulls not allowed
            return false;
        }
        if (!validateValueForReading(sm, value))
        {
            return false;
        }

        boolean exists = false;
        try
        {
            ObjectManager om = sm.getObjectManager();
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, containsValueStmt);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    jdbcPosition = populateValueInStatement(om, ps, value, jdbcPosition);

                    ResultSet rs = sqlControl.executeStatementQuery(mconn, containsValueStmt, ps);
                    try
                    {
                        if (rs.next())
                        {
                            exists = true;
                        }

                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056019",containsValueStmt),e);
        }

        return exists;
    }

    /**
     * Method to return the value for a key.
     * @param sm State Manager for the Map.
     * @param key The key of the object to retrieve.
     * @return The value for this key.
     **/
    public Object get(StateManager sm, Object key)
    {
        try
        {
            return getValue(sm, key);
        }
        catch (NoSuchElementException e)
        {
            return null;
        }
    }

    /**
     * Method to put all elements from a Map into our Map.
     * @param sm State Manager for the Map
     * @param m The Map to add
     **/
    public void putAll(StateManager sm, Map m)
    {
        Iterator i = m.entrySet().iterator();

        while (i.hasNext())
        {
            Map.Entry e = (Map.Entry)i.next();
            put(sm, e.getKey(), e.getValue());
        }
    }

    // --------------------------- Utility Methods -----------------------------
    /**
     * Generate statement to check if a value is contained in the Map.
     * <PRE>
     * SELECT OWNERCOL
     * FROM MAPTABLE
     * WHERE OWNERCOL=? AND VALUECOL = ?
     * </PRE>
     * @return Statement to check if a value is contained in the Map.
     */
    private String getContainsValueStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("SELECT ");
        for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
        }
        stmt.append(" FROM ");
        stmt.append(mapTable.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());
        }
       
        for (int i=0; i<valueMapping.getNumberOfDatastoreFields(); i++)
        {
            stmt.append(" AND ");
            stmt.append(valueMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)valueMapping.getDataStoreMapping(i)).getUpdateInputParameter());
        }
        return stmt.toString();
    }

    /**
     * Utility to validate the type of a key for storing in the Map.
     * @param clr The ClassLoaderResolver
     * @param key The key to check.
     **/
    protected void validateKeyType(ClassLoaderResolver clr, Object key)
    {
        if (key == null)
        {
            throw new NullPointerException(LOCALISER.msg("056062"));
        }

        if (!clr.isAssignableFrom(keyType, key.getClass()))
        {
            throw new ClassCastException(LOCALISER.msg("056064",key.getClass().getName(),keyType));
        }
    }

    /**
     * Utility to validate the type of a value for storing in the Map.
     * @param clr The ClassLoaderResolver
     * @param value The value to check.
     **/
    protected void validateValueType(ClassLoaderResolver clr, Object value)
    {
        if (value != null && !clr.isAssignableFrom(valueType, value.getClass()))
        {
            throw new ClassCastException(LOCALISER.msg("056065",value.getClass().getName(),valueType));
        }
    }

    /**
     * Utility to validate a key is ok for reading.
     * @param sm State Manager for the map.
     * @param key The key to check.
     * @return Whether it is validated.
     **/
    protected boolean validateKeyForReading(StateManager sm, Object key)
    {
        validateKeyType(sm.getObjectManager().getClassLoaderResolver(), key);

        if (!keysAreEmbedded && !keysAreSerialised)
        {
            ObjectManager om = sm.getObjectManager();
            if (key!=null && (!om.getApiAdapter().isPersistent(key) ||
                om != ObjectManagerHelper.getObjectManager(key)) && !om.getApiAdapter().isDetached(key))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Utility to validate a value is ok for reading.
     * @param sm State Manager for the map.
     * @param value The value to check.
     * @return Whether it is validated.
     **/
    protected boolean validateValueForReading(StateManager sm, Object value)
    {
        validateValueType(sm.getObjectManager().getClassLoaderResolver(), value);

        if (!valuesAreEmbedded && !valuesAreSerialised)
        {
            ObjectManager om = sm.getObjectManager();
            if (value != null && (!om.getApiAdapter().isPersistent(value) ||
                om != ObjectManagerHelper.getObjectManager(value)) && !om.getApiAdapter().isDetached(value))
            {
                return false;
            }
        }

        return true;
    }

    /**
     * Utility to validate a key is ok for writing (present in the datastore).
     * @param sm State Manager for the map.
     * @param key The key to check.
     */
    protected void validateKeyForWriting(StateManager sm, Object key)
    {
        validateKeyType(sm.getObjectManager().getClassLoaderResolver(), key);

        if (!keysAreEmbedded && !keysAreSerialised)
        {
            ObjectManager om = sm.getObjectManager();
            SCOUtils.validateObjectForWriting(om, key, null);
        }
    }

    /**
     * Utility to validate a value is ok for writing (present in the datastore).
     * @param sm State Manager for the map.
     * @param value The value to check.
     */
    protected void validateValueForWriting(StateManager sm, Object value)
    {
        validateValueType(sm.getObjectManager().getClassLoaderResolver(), value);
        if (!valuesAreEmbedded && !valuesAreSerialised)
        {
            ObjectManager om = sm.getObjectManager();
            SCOUtils.validateObjectForWriting(om, value, null);
        }
    }

    /**
     * Accessor for the Get statement.
     * @param ownerSm The owner StateManager
     * @param key The search key
     * @return The QueryStatement.
     **/
    protected QueryExpression getGetStatement(StateManager ownerSm, Object key)
    {
        return null;
    }

    /**
     * Method to retrieve a value from the Map given the key.
     * @param sm State Manager for the map.
     * @param key The key to retrieve the value for.
     * @return The value for this key
     * @throws NoSuchElementException if the value for the key was not found
     **/
    protected Object getValue(StateManager sm, Object key)
    throws NoSuchElementException
    {
        if (!validateKeyForReading(sm, key))
        {
            return null;
        }

        // Create the basic statement (without any selected columns)
        QueryExpression stmt = getGetStatement(sm,key);

        // Add the required field selections to the statement
        ResultObjectFactory rof = newResultObjectFactory(sm, stmt, false, true);

        ObjectManager om = sm.getObjectManager();
        Transaction tx = om.getTransaction();
        boolean useUpdateLock = ((Boolean)tx.getOptions().get("transaction.serializeReadObjects")).booleanValue();
        String statement = storeMgr.getStatementTextForQuery(stmt, useUpdateLock);

        Object value=null;
        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
                    {
                        boolean found = rs.next();
                        if (!found)
                        {
                            throw new NoSuchElementException();
                        }

                        if (valuesAreEmbedded || valuesAreSerialised)
                        {
                            int param[] = new int[valueMapping.getNumberOfDatastoreFields()];
                            for (int i = 0; i < param.length; ++i)
                            {
                                param[i] = i + 1;
                            }

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

                        SQLWarnings.log(rs);
                    }
                    finally
                    {
                        rs.close();
                    }
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new JPOXDataStoreException(LOCALISER.msg("056014",statement),e);
        }

        return value;
    }

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

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

    /**
     * Convenience method to populate the passed PreparedStatement with the field values from
     * the embedded key starting at the specified jdbc position.
     * @param sm State Manager of the owning container
     * @param key The embedded key
     * @param ps The PreparedStatement
     * @param jdbcPosition JDBC position in the statement to start at
     * @param joinTable The Join table where the values are embedded
     * @return The next JDBC position
     */
    protected int populateEmbeddedKeyFieldsInStatement(StateManager sm,
                                                       Object key,
                                                       PreparedStatement ps,
                                                       int jdbcPosition,
                                                       JoinTable joinTable)
    {
        EmbeddedKeyPCMapping embeddedMapping = (EmbeddedKeyPCMapping)keyMapping;
        StatementExpressionIndex[] statementExpressionIndex =
            new StatementExpressionIndex[kmd.getNoOfManagedMembers() + kmd.getNoOfInheritedManagedMembers()];
        int[] elementFieldNumbers = new int[embeddedMapping.getNumberOfJavaTypeMappings()];
        for (int i=0;i<embeddedMapping.getNumberOfJavaTypeMappings();i++)
        {
            JavaTypeMapping fieldMapping = embeddedMapping.getJavaTypeMapping(i);
            int absFieldNum = kmd.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, key, joinTable);
        elementSM.setPcObjectType(StateManager.EMBEDDED_MAP_KEY_PC);
        elementSM.provideFields(elementFieldNumbers, new ParameterSetter(elementSM, ps, statementExpressionIndex, true));

        return jdbcPosition;
    }

    /**
     * Convenience method to populate the passed PreparedStatement with the field values from
     * the embedded value starting at the specified jdbc position.
     * @param sm State Manager of the owning container
     * @param value The embedded value
     * @param ps The PreparedStatement
     * @param jdbcPosition JDBC position in the statement to start at
     * @param joinTable The Join table where the values are embedded
     * @return The next JDBC position
     */
    protected int populateEmbeddedValueFieldsInStatement(StateManager sm,
                                                         Object value,
                                                         PreparedStatement ps,
                                                         int jdbcPosition,
                                                         JoinTable joinTable)
    {
        EmbeddedValuePCMapping embeddedMapping = (EmbeddedValuePCMapping)valueMapping;
        StatementExpressionIndex[] statementExpressionIndex =
            new StatementExpressionIndex[vmd.getNoOfManagedMembers() + vmd.getNoOfInheritedManagedMembers()];
        int[] elementFieldNumbers = new int[embeddedMapping.getNumberOfJavaTypeMappings()];
        for (int i=0;i<embeddedMapping.getNumberOfJavaTypeMappings();i++)
        {
            JavaTypeMapping fieldMapping = embeddedMapping.getJavaTypeMapping(i);
            int absFieldNum = vmd.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, value, joinTable);
        elementSM.setPcObjectType(StateManager.EMBEDDED_MAP_VALUE_PC);
        elementSM.provideFields(elementFieldNumbers, new ParameterSetter(elementSM, ps, statementExpressionIndex, true));

        return jdbcPosition;
    }

    /**
     * Method to update a field of an embedded key.
     * @param sm State Manager of the owner
     * @param key The key to update
     * @param fieldNumber The number of the field to update
     * @param newValue The new value
     */
    public boolean updateEmbeddedKey(StateManager sm, Object key, int fieldNumber, Object newValue)
    {
        boolean modified = false;
        if (keyMapping != null && keyMapping instanceof EmbeddedKeyPCMapping)
        {
            String fieldName = vmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber).getName();
            if (fieldName == null)
            {
                // We have no mapping for this field so presumably is the owner field or a PK field
                return false;
            }
            JavaTypeMapping fieldMapping = ((EmbeddedKeyPCMapping)keyMapping).getJavaTypeMapping(fieldName);
            if (fieldMapping == null)
            {
                // We have no mapping for this field so presumably is the owner field or a PK field
                return false;
            }

            String stmt = getUpdateEmbeddedKeyStmt(fieldMapping);
            try
            {
                ObjectManager om = sm.getObjectManager();
                ManagedConnection mconn = storeMgr.getConnection(om);
                SQLController sqlControl = storeMgr.getSQLController();

                try
                {
                    PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, false);
                    try
                    {
                        int jdbcPosition = 1;
                        fieldMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, fieldMapping), key);
                        jdbcPosition += fieldMapping.getNumberOfDatastoreFields();
                        jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                        jdbcPosition = populateEmbeddedKeyFieldsInStatement(sm, key, ps, jdbcPosition, (JoinTable)mapTable);

                        sqlControl.executeStatementUpdate(mconn, stmt, ps, true);
                        modified = true;
                    }
                    finally
                    {
                        sqlControl.closeStatement(mconn, ps);
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (SQLException e)
            {
                e.printStackTrace();
                throw new JPOXDataStoreException(LOCALISER.msg("056010", stmt), e);
            }
        }

        return modified;
    }

    /**
     * Method to update a field of an embedded key.
     * @param sm State Manager of the owner
     * @param value The value to update
     * @param fieldNumber The number of the field to update
     * @param newValue The new value
     */
    public boolean updateEmbeddedValue(StateManager sm, Object value, int fieldNumber, Object newValue)
    {
        boolean modified = false;
        if (valueMapping != null && valueMapping instanceof EmbeddedValuePCMapping)
        {
            String fieldName = vmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber).getName();
            if (fieldName == null)
            {
                // We have no mapping for this field so presumably is the owner field or a PK field
                return false;
            }
            JavaTypeMapping fieldMapping = ((EmbeddedValuePCMapping)valueMapping).getJavaTypeMapping(fieldName);
            if (fieldMapping == null)
            {
                // We have no mapping for this field so presumably is the owner field or a PK field
                return false;
            }

            String stmt = getUpdateEmbeddedValueStmt(fieldMapping);
            try
            {
                ObjectManager om = sm.getObjectManager();
                ManagedConnection mconn = storeMgr.getConnection(om);
                SQLController sqlControl = storeMgr.getSQLController();

                try
                {
                    PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, stmt, false);
                    try
                    {
                        int jdbcPosition = 1;
                        fieldMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, fieldMapping), newValue);
                        jdbcPosition += fieldMapping.getNumberOfDatastoreFields();
                        jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                        jdbcPosition = populateEmbeddedValueFieldsInStatement(sm, value, ps, jdbcPosition, (JoinTable)mapTable);

                        sqlControl.executeStatementUpdate(mconn, stmt, ps, true);
                        modified = true;
                    }
                    finally
                    {
                        sqlControl.closeStatement(mconn, ps);
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (SQLException e)
            {
                e.printStackTrace();
                throw new JPOXDataStoreException(LOCALISER.msg("056011", stmt), e);
            }
        }

        return modified;
    }

    /**
     * Generate statement for update the field of an embedded key.
     * <PRE>
     * UPDATE MAPTABLE
     * SET EMBEDDEDKEYCOL1 = ?
     * WHERE OWNERCOL=?
     * AND EMBEDDEDKEYCOL1 = ?
     * AND EMBEDDEDKEYCOL2 = ? ...
     * </PRE>
     * @param fieldMapping The mapping for the field to be updated
     * @return Statement for updating an embedded key in the Set
     */
    protected String getUpdateEmbeddedKeyStmt(JavaTypeMapping fieldMapping)
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("UPDATE ");
        stmt.append(mapTable.toString());
        stmt.append(" SET ");
        for (int i=0; i<fieldMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(fieldMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)fieldMapping.getDataStoreMapping(i)).getUpdateInputParameter());
        }

        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());
        }

        EmbeddedKeyPCMapping embeddedMapping = (EmbeddedKeyPCMapping)keyMapping;
        for (int i=0;i<embeddedMapping.getNumberOfJavaTypeMappings();i++)
        {
            JavaTypeMapping m = embeddedMapping.getJavaTypeMapping(i);
            if (m != null)
            {
                for (int j=0;j<m.getNumberOfDatastoreFields();j++)
                {
                    stmt.append(" AND ");
                    stmt.append(m.getDataStoreMapping(j).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)m.getDataStoreMapping(j)).getUpdateInputParameter());
                }
            }
        }
        return stmt.toString();
    }

    /**
     * Generate statement for update the field of an embedded value.
     * <PRE>
     * UPDATE MAPTABLE
     * SET EMBEDDEDVALUECOL1 = ?
     * WHERE OWNERCOL=?
     * AND EMBEDDEDVALUECOL1 = ?
     * AND EMBEDDEDVALUECOL2 = ? ...
     * </PRE>
     * @param fieldMapping The mapping for the field to be updated
     * @return Statement for updating an embedded value in the Set
     */
    protected String getUpdateEmbeddedValueStmt(JavaTypeMapping fieldMapping)
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("UPDATE ");
        stmt.append(mapTable.toString());
        stmt.append(" SET ");
        for (int i=0; i<fieldMapping.getNumberOfDatastoreFields(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(fieldMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)fieldMapping.getDataStoreMapping(i)).getUpdateInputParameter());
        }

        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());
        }

        EmbeddedValuePCMapping embeddedMapping = (EmbeddedValuePCMapping)valueMapping;
        for (int i=0;i<embeddedMapping.getNumberOfJavaTypeMappings();i++)
        {
            JavaTypeMapping m = embeddedMapping.getJavaTypeMapping(i);
            if (m != null)
            {
                for (int j=0;j<m.getNumberOfDatastoreFields();j++)
                {
                    stmt.append(" AND ");
                    stmt.append(m.getDataStoreMapping(j).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)m.getDataStoreMapping(j)).getUpdateInputParameter());
                }
            }
        }
        return stmt.toString();
    }

    // ---------------------------- JDOQL Query methods ---------------------------------

    /**
     * Method to create a Result Object factory for extracting objects from a ResultSet.
     * @param sm State Manager of owner field
     * @param stmt The Query statement
     * @param ignoreCache Whether to ignore the cache
     * @param useFetchPlan Whether to retrieve the fetch plan fields or DFG
     * @return The Result Object factory
     */
    public ResultObjectFactory newResultObjectFactory(StateManager sm, QueryExpression stmt, boolean ignoreCache, boolean useFetchPlan)
    {
        MappedStoreManager storeMgr = (MappedStoreManager)sm.getObjectManager().getStoreManager();
        ClassLoaderResolver clr = sm.getObjectManager().getClassLoaderResolver();
        if (valuesAreEmbedded || valuesAreSerialised)
        {
            // Value = Embedded, Serialised
            // valueTable is null here so why return this ?
            return new PersistentIDROF(this.valueTable, null, vmd, null, null, null, ignoreCache, false,
                stmt.hasMetaDataExpression(), null, clr.classForName(valueType));
        }
        else
        {
            // Value = PC

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

            StatementExpressionIndex[] statementExpressionIndex = null;
            int[] prefetchFieldNumbers = null;
            if (useFetchPlan)
            {
                // Select the FetchPlan fields
                FetchPlan fp = sm.getObjectManager().getFetchPlan();
                fp.manageFetchPlanForClass(vmd);
                FetchPlanForClass fpc = fp.getFetchPlanForClass(vmd);
                int fieldNumbers[] = fpc.getFieldsInActualFetchPlan();
                int fn[] = new int[fieldNumbers.length];
                int prefetchFieldCount = 0;
                int fieldCount = vmd.getNoOfInheritedManagedMembers() + vmd.getNoOfManagedMembers();
               
                statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                for (int i=0; i<fieldNumbers.length; ++i)
                {
                    JavaTypeMapping m = valueTable.getFieldMapping(vmd.getMetaDataForManagedMemberAtAbsolutePosition(fieldNumbers[i]));
                    if (m != null)
                    {
                        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
            {
                AbstractClassMetaData cmd = vmd;
                if (cmd.getIdentityType() == IdentityType.APPLICATION)
                {
                    prefetchFieldNumbers = new int[cmd.getPKMemberPositions().length];
                    int fieldCount = cmd.getNoOfInheritedManagedMembers() + cmd.getNoOfManagedMembers();
                    statementExpressionIndex = new StatementExpressionIndex[fieldCount];
                    for (int i = 0; i < prefetchFieldNumbers.length; ++i)
                    {
                        prefetchFieldNumbers[i] = cmd.getPKMemberPositions()[i];
                        JavaTypeMapping m = valueTable.getFieldMapping(cmd.getMetaDataForManagedMemberAtAbsolutePosition(prefetchFieldNumbers[i]));
                        if (m != null) // field is not stored in the table, e.g List, Set, etc or is transactional
                        {
                            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(storeMgr.getDatastoreClass(getValueType(), clr),
                prefetchFieldNumbers, vmd, statementExpressionIndex, datastoreIndex, versionIndex,
                ignoreCache, iterateUsingDiscriminator, stmt.hasMetaDataExpression(), null, clr.classForName(valueType));
        }
    }

    /**
     * Method used where a Query uses map.isEmpty().
     * @param qs The QueryStatement
     * @param mapping The mapping of the java type
     * @param ownerTe The owner table expression
     * @param mapTableAlias The aliasfor the "Map" table.
     * @return A subquery
     **/
    public QueryExpression getExistsSubquery(QueryExpression qs,
                                            JavaTypeMapping mapping,
                                            LogicSetExpression ownerTe,
                                            DatastoreIdentifier mapTableAlias)
    {
        QueryExpression stmt = dba.newQueryStatement(mapTable, mapTableAlias, qs.getClassLoaderResolver());
        stmt.setParent(qs);

        // Join to the owner
        ScalarExpression ownerExpr = mapping.newScalarExpression(stmt, ownerTe);
        ScalarExpression ownerInMapExpr = ownerMapping.newScalarExpression(stmt, stmt.getTableExpression(mapTableAlias));
        stmt.andCondition(ownerExpr.eq(ownerInMapExpr));

        stmt.select(mapTableAlias, valueMapping);

        return stmt;
    }

    /**
     * Query utility to generate a subquery for the size() of the map.
     * @param qs The query statement
     * @param mapping mapping of the field
     * @param ownerTe Expression for the table
     * @param mapTableAlias alias for the map table
     * @return The query statement
     */
    public QueryExpression getSizeSubquery(QueryExpression qs,
                                           JavaTypeMapping mapping,
                                           LogicSetExpression ownerTe,
                                           DatastoreIdentifier mapTableAlias)
    {
        QueryExpression stmt = dba.newQueryStatement(mapTable, mapTableAlias, qs.getClassLoaderResolver());
        stmt.setParent(qs);

        // Join for the owner
        ScalarExpression ownerExpr = mapping.newScalarExpression(stmt, ownerTe);
        ScalarExpression ownerInCollectionExpr = ownerMapping.newScalarExpression(stmt, stmt.getTableExpression(mapTableAlias));
        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;
    }

    /**
     * Used as part of the Querying of Maps where a get(Key) is used.
     * @param stmt The Query Statement to apply the join
     * @param parentStmt the parent Query Statement. If there is no parent, <code>parentStmt</code> must be equals to <code>stmt</code>
     * @param ownerMapping Mapping for the owner.
     * @param te Table Expression for the owner
     * @param mapTableAlias  The SQL alias to assign to the expression or to the main table.
     * @param filteredKeyType The Class Type for the filtered key
     * @param keyTableAlias  The SQL alias to assign to the expression or to the key table.
     * @param valueTableAlias The SQL alias to assign to the expression or to the value table.
     * @return an array with 2 elements of QueryColumnList. The first element
     * contains the columns from the key mapping and the second element the
     * columns from the value mapping
     */
    public ScalarExpression[] joinKeysToGet(
                                QueryExpression stmt,
                                QueryExpression parentStmt,
                                JavaTypeMapping ownerMapping,
                                LogicSetExpression te,
                                DatastoreIdentifier mapTableAlias,
                                Class filteredKeyType,
                                DatastoreIdentifier keyTableAlias,
                                DatastoreIdentifier valueTableAlias)
    {
        ClassLoaderResolver clr = stmt.getClassLoaderResolver();
        Class value_class = clr.classForName(valueType);

        return joinKeysValuesTo(stmt,parentStmt,ownerMapping,te,mapTableAlias,filteredKeyType,value_class,null,null,keyTableAlias,valueTableAlias);
    }
}
TOP

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

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.