Package org.datanucleus.store.rdbms.scostore

Source Code of org.datanucleus.store.rdbms.scostore.RDBMSJoinMapStore

/**********************************************************************
Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:
    ...
**********************************************************************/
package org.datanucleus.store.rdbms.scostore;

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

import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.Transaction;
import org.datanucleus.exceptions.NucleusDataStoreException;
import org.datanucleus.query.expression.Expression;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.connection.ManagedConnection;
import org.datanucleus.store.mapped.StatementClassMapping;
import org.datanucleus.store.mapped.StatementMappingIndex;
import org.datanucleus.store.mapped.StatementParameterMapping;
import org.datanucleus.store.mapped.exceptions.MappedDatastoreException;
import org.datanucleus.store.mapped.mapping.EmbeddedKeyPCMapping;
import org.datanucleus.store.mapped.mapping.MappingHelper;
import org.datanucleus.store.mapped.mapping.ReferenceMapping;
import org.datanucleus.store.mapped.mapping.SerialisedMapping;
import org.datanucleus.store.mapped.mapping.SerialisedPCMapping;
import org.datanucleus.store.mapped.mapping.SerialisedReferenceMapping;
import org.datanucleus.store.mapped.scostore.JoinMapStore;
import org.datanucleus.store.mapped.scostore.MapEntrySetStore;
import org.datanucleus.store.mapped.scostore.MapKeySetStore;
import org.datanucleus.store.mapped.scostore.MapValueSetStore;
import org.datanucleus.store.query.ResultObjectFactory;
import org.datanucleus.store.rdbms.JDBCUtils;
import org.datanucleus.store.rdbms.RDBMSStoreManager;
import org.datanucleus.store.rdbms.SQLController;
import org.datanucleus.store.rdbms.mapping.RDBMSMapping;
import org.datanucleus.store.rdbms.sql.SQLStatement;
import org.datanucleus.store.rdbms.sql.SQLStatementHelper;
import org.datanucleus.store.rdbms.sql.SQLTable;
import org.datanucleus.store.rdbms.sql.StatementGenerator;
import org.datanucleus.store.rdbms.sql.UnionStatementGenerator;
import org.datanucleus.store.rdbms.sql.expression.SQLExpression;
import org.datanucleus.store.rdbms.sql.expression.SQLExpressionFactory;
import org.datanucleus.store.rdbms.table.JoinTable;
import org.datanucleus.store.rdbms.table.MapTable;

/**
* RDBMS-specific implementation of an {@link JoinMapStore}.
*/
public class RDBMSJoinMapStore extends JoinMapStore
{
    private String putStmt;
    private String updateStmt;
    private String removeStmt;
    private String clearStmt;

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

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

    private StatementClassMapping getMappingDef = null;
    private StatementParameterMapping getMappingParams = null;

    /**
     * Constructor for the backing store of a join map for RDBMS.
     * @param mapTable Join table for the Map
     * @param clr The ClassLoaderResolver
     */
    public RDBMSJoinMapStore(MapTable mapTable, ClassLoaderResolver clr)
    {
        super(mapTable, clr, mapTable.getOwnerMapping(), mapTable.getKeyMapping(), mapTable.getValueMapping(),
            mapTable.getOrderMapping(), mapTable.getKeyType(), mapTable.isEmbeddedKey(),
            mapTable.isSerialisedKey(), mapTable.getValueType(), mapTable.isEmbeddedValue(),
            mapTable.isSerialisedValue(), mapTable.getOwnerMemberMetaData(),
            new RDBMSAbstractMapStoreSpecialization(LOCALISER, clr, (RDBMSStoreManager)mapTable.getStoreManager()));

        specialization.initialise(this);
        putStmt = getPutStmt();
        updateStmt = getUpdateStmt();
        removeStmt = getRemoveStmt();
        clearStmt = getClearStmt();
    }

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

    /**
     * Generate statement to add an item to the Map.
     * Adds a row to the link table, linking container with value object.
     * <PRE>
     * INSERT INTO MAPTABLE (VALUECOL, OWNERCOL, KEYCOL)
     * VALUES (?, ?, ?)
     * </PRE>
     * @return Statement to add an item to the Map.
     */
    private String getPutStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("INSERT INTO ");
        stmt.append(mapTable.toString());
        stmt.append(" (");
        for (int i=0; i<valueMapping.getNumberOfDatastoreMappings(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(valueMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
        }

        for (int i=0; i<ownerMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(",");
            stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
        }
        if (adapterMapping != null)
        {
            for (int i=0; i<adapterMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(",");
                stmt.append(adapterMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            }
        }

        for (int i=0; i<keyMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(",");
            stmt.append(keyMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
        }

        stmt.append(") VALUES (");
        for (int i=0; i<valueMapping.getNumberOfDatastoreMappings(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(((RDBMSMapping)valueMapping.getDatastoreMapping(i)).getInsertionInputParameter());
        }

        for (int i=0; i<ownerMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(",");
            stmt.append(((RDBMSMapping)ownerMapping.getDatastoreMapping(i)).getInsertionInputParameter());
        }
        if (adapterMapping != null)
        {
            for (int i=0; i<adapterMapping.getNumberOfDatastoreMappings(); i++)
            {
                stmt.append(",");
                stmt.append(((RDBMSMapping)adapterMapping.getDatastoreMapping(i)).getInsertionInputParameter());
            }
        }
        for (int i=0; i<keyMapping.getNumberOfDatastoreMappings(); i++)
        {
            stmt.append(",");
            stmt.append(((RDBMSMapping)keyMapping.getDatastoreMapping(i)).getInsertionInputParameter());
        }
        stmt.append(") ");

        return stmt.toString();
    }

    /**
     * Generate statement to update an item in the Map.
     * Updates the link table row, changing the value object for this key.
     * <PRE>
     * UPDATE MAPTABLE
     * SET VALUECOL=?
     * WHERE OWNERCOL=?
     * AND KEYCOL=?
     * </PRE>
     * @return Statement to update an item in the Map.
     */
    private String getUpdateStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("UPDATE ");
        stmt.append(mapTable.toString());
        stmt.append(" SET ");
        for (int i=0; i<valueMapping.getNumberOfDatastoreMappings(); i++)
        {
            if (i > 0)
            {
                stmt.append(",");
            }
            stmt.append(valueMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)valueMapping.getDatastoreMapping(i)).getUpdateInputParameter());
        }
        stmt.append(" WHERE ");

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

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

    /**
     * Generate statement to remove an item from the Map.
     * Deletes the link from the join table, leaving the value object in its
     * own table.
     * <PRE>
     * DELETE FROM MAPTABLE
     * WHERE OWNERCOL=?
     * AND KEYCOL=?
     * </PRE>
     * @return Return an item from the Map.
     */
    private String getRemoveStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("DELETE FROM ");
        stmt.append(mapTable.toString());
        stmt.append(" WHERE ");

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

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

    /**
     * Generate statement to clear the Map.
     * Deletes the links from the join table for this Map, leaving the value
     * objects in their own table(s).
     * <PRE>
     * DELETE FROM MAPTABLE
     * WHERE OWNERCOL=?
     * </PRE>
     * @return Statement to clear the Map.
     */
    private String getClearStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("DELETE FROM ");
        stmt.append(mapTable.toString());
        stmt.append(" WHERE ");
        for (int i=0; i<ownerMapping.getNumberOfDatastoreMappings(); i++)
        {
            if (i > 0)
            {
                stmt.append(" AND ");
            }
            stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)ownerMapping.getDatastoreMapping(i)).getUpdateInputParameter());
        }
        return stmt.toString();
    }

    /**
     * Method to retrieve a value from the Map given the key.
     * @param ownerSM State Manager for the owner of 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(ObjectProvider ownerSM, Object key)
    throws NoSuchElementException
    {
        if (!validateKeyForReading(ownerSM, key))
        {
            return null;
        }

        ExecutionContext ec = ownerSM.getExecutionContext();
        if (getStmtLocked == null)
        {
            synchronized (this) // Make sure this completes in case another thread needs the same info
            {
                // Generate the statement, and statement mapping/parameter information
                SQLStatement sqlStmt = getSQLStatementForGet(ownerSM);
                getStmtUnlocked = sqlStmt.getSelectStatement().toSQL();
                sqlStmt.addExtension("lock-for-update", true);
                getStmtLocked = sqlStmt.getSelectStatement().toSQL();
            }
        }

        Transaction tx = ec.getTransaction();
        String stmt = (tx.lockReadObjects() ? getStmtLocked : getStmtUnlocked);
        Object value = null;
        try
        {
            RDBMSStoreManager storeMgr = (RDBMSStoreManager)this.storeMgr;
            ManagedConnection mconn = storeMgr.getConnection(ec);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                // Create the statement and supply owner/key params
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);
                StatementMappingIndex ownerIdx = getMappingParams.getMappingForParameter("owner");
                int numParams = ownerIdx.getNumberOfParameterOccurrences();
                for (int paramInstance=0;paramInstance<numParams;paramInstance++)
                {
                    ownerIdx.getMapping().setObject(ec, ps,
                        ownerIdx.getParameterPositionsForOccurrence(paramInstance), ownerSM.getObject());
                }
                StatementMappingIndex keyIdx = getMappingParams.getMappingForParameter("key");
                numParams = keyIdx.getNumberOfParameterOccurrences();
                for (int paramInstance=0;paramInstance<numParams;paramInstance++)
                {
                    keyIdx.getMapping().setObject(ec, ps,
                        keyIdx.getParameterPositionsForOccurrence(paramInstance), key);
                }

                try
                {
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        boolean found = rs.next();
                        if (!found)
                        {
                            throw new NoSuchElementException();
                        }

                        if (valuesAreEmbedded || valuesAreSerialised)
                        {
                            int param[] = new int[valueMapping.getNumberOfDatastoreMappings()];
                            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 = ((JoinTable)mapTable).getOwnerMemberMetaData().getAbsoluteFieldNumber();
                                value = valueMapping.getObject(ec, rs, param, ownerSM, ownerFieldNumber);
                            }
                            else
                            {
                                // Value = Non-PC
                                value = valueMapping.getObject(ec, rs, param);
                            }
                        }
                        else if (valueMapping instanceof ReferenceMapping)
                        {
                            // Value = Reference (Interface/Object)
                            int param[] = new int[valueMapping.getNumberOfDatastoreMappings()];
                            for (int i = 0; i < param.length; ++i)
                            {
                                param[i] = i + 1;
                            }
                            value = valueMapping.getObject(ec, rs, param);
                        }
                        else
                        {
                            // Value = PC
                            ResultObjectFactory rof = storeMgr.newResultObjectFactory(vmd,
                                getMappingDef, false, null, clr.classForName(valueType));
                            value = rof.getObject(ec, rs);
                        }

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

    /**
     * Method to return an SQLStatement for retrieving the value for a key.
     * Selects the join table and optionally joins to the value table if it has its own table.
     * @param ownerSM StateManager for the owning object
     * @return The SQLStatement
     */
    protected SQLStatement getSQLStatementForGet(ObjectProvider ownerSM)
    {
        SQLStatement sqlStmt = null;

        final ClassLoaderResolver clr = ownerSM.getExecutionContext().getClassLoaderResolver();
        final Class valueCls = clr.classForName(this.valueType);
        RDBMSStoreManager storeMgr = (RDBMSStoreManager)this.storeMgr;
        if (valuesAreEmbedded || valuesAreSerialised)
        {
            // Value is stored in join table
            sqlStmt = new SQLStatement(storeMgr, mapTable, null, null);
            sqlStmt.setClassLoaderResolver(clr);
            sqlStmt.select(sqlStmt.getPrimaryTable(), valueMapping, null);
        }
        else
        {
            // Value is stored in own table
            getMappingDef = new StatementClassMapping();
            UnionStatementGenerator stmtGen =
                new UnionStatementGenerator(storeMgr, clr, valueCls, true, null, null, mapTable, null, valueMapping);
            stmtGen.setOption(StatementGenerator.OPTION_SELECT_NUCLEUS_TYPE);
            getMappingDef.setNucleusTypeColumnName(UnionStatementGenerator.NUC_TYPE_COLUMN);
            sqlStmt = stmtGen.getStatement();

            // Select the value field(s)
            SQLTable valueSqlTbl = sqlStmt.getTable(valueTable, sqlStmt.getPrimaryTable().getGroupName());
            SQLStatementHelper.selectFetchPlanOfSourceClassInStatement(sqlStmt, getMappingDef,
                ownerSM.getExecutionContext().getFetchPlan(), valueSqlTbl, vmd, 0);
        }

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

        // Apply condition on key
        if (keyMapping instanceof SerialisedMapping)
        {
            // if the keyMapping contains a BLOB column (or any other column not supported by the database
            // as primary key), uses like instead of the operator OP_EQ (=)
            // in future do not check if the keyMapping is of ObjectMapping, but use the database
            // adapter to check the data types not supported as primary key
            // if object mapping (BLOB) use like
            SQLExpression keyExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), keyMapping);
            SQLExpression keyVal = exprFactory.newLiteralParameter(sqlStmt, keyMapping, null, "KEY");
            sqlStmt.whereAnd(new org.datanucleus.store.rdbms.sql.expression.BooleanExpression(keyExpr,
                Expression.OP_LIKE, keyVal), true);
        }
        else
        {
            SQLExpression keyExpr = exprFactory.newExpression(sqlStmt, sqlStmt.getPrimaryTable(), keyMapping);
            SQLExpression keyVal = exprFactory.newLiteralParameter(sqlStmt, keyMapping, null, "KEY");
            sqlStmt.whereAnd(keyExpr.eq(keyVal), true);
        }

        // Input parameter(s) - owner, key
        int inputParamNum = 1;
        StatementMappingIndex ownerIdx = new StatementMappingIndex(ownerMapping);
        StatementMappingIndex keyIdx = new StatementMappingIndex(keyMapping);
        if (sqlStmt.getNumberOfUnions() > 0)
        {
            // Add parameter occurrence for each union of statement
            for (int j=0;j<sqlStmt.getNumberOfUnions()+1;j++)
            {
                int[] ownerPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
                for (int k=0;k<ownerPositions.length;k++)
                {
                    ownerPositions[k] = inputParamNum++;
                }
                ownerIdx.addParameterOccurrence(ownerPositions);

                int[] keyPositions = new int[keyMapping.getNumberOfDatastoreMappings()];
                for (int k=0;k<keyPositions.length;k++)
                {
                    keyPositions[k] = inputParamNum++;
                }
                keyIdx.addParameterOccurrence(keyPositions);
            }
        }
        else
        {
            int[] ownerPositions = new int[ownerMapping.getNumberOfDatastoreMappings()];
            for (int k=0;k<ownerPositions.length;k++)
            {
                ownerPositions[k] = inputParamNum++;
            }
            ownerIdx.addParameterOccurrence(ownerPositions);

            int[] keyPositions = new int[keyMapping.getNumberOfDatastoreMappings()];
            for (int k=0;k<keyPositions.length;k++)
            {
                keyPositions[k] = inputParamNum++;
            }
            keyIdx.addParameterOccurrence(keyPositions);
        }
        getMappingParams = new StatementParameterMapping();
        getMappingParams.addMappingForParameter("owner", ownerIdx);
        getMappingParams.addMappingForParameter("key", keyIdx);

        return sqlStmt;
    }

    protected void clearInternal(ObjectProvider ownerSM)
    {
        try
        {
            ExecutionContext ec = ownerSM.getExecutionContext();
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, clearStmt, false);
                try
                {
                    int jdbcPosition = 1;
                    BackingStoreHelper.populateOwnerInStatement(ownerSM, ec, ps, jdbcPosition, this);
                    sqlControl.executeStatementUpdate(mconn, clearStmt, ps, true);
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("056013",clearStmt),e);
        }
    }

    protected void removeInternal(ObjectProvider sm, Object key)
    {
        ExecutionContext ec = sm.getExecutionContext();
        try
        {
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, removeStmt, false);
                try
                {
                    int jdbcPosition = 1;
                    jdbcPosition = BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                    BackingStoreHelper.populateKeyInStatement(ec, ps, key, jdbcPosition, keyMapping);
                    sqlControl.executeStatementUpdate(mconn, removeStmt, ps, true);
                }
                finally
                {
                    sqlControl.closeStatement(mconn, ps);
                }
            }
            finally
            {
                mconn.release();
            }
        }
        catch (SQLException e)
        {
            throw new NucleusDataStoreException(LOCALISER.msg("056012",removeStmt),e);
        }
    }

    protected MapKeySetStore newMapKeySetStore()
    {
        return new RDBMSMapKeySetStore((MapTable)mapTable, this, clr);
    }

    protected MapValueSetStore newMapValueSetStore()
    {
        return new RDBMSMapValueSetStore((MapTable)mapTable, this, clr);
    }

    protected MapEntrySetStore newMapEntrySetStore()
    {
        return new RDBMSMapEntrySetStore((MapTable)mapTable, this, clr);
    }

    /**
     * Method to process an "update" statement (where the key already has a value in the join table).
     * @param ownerSM StateManager for the owner
     * @param conn The Connection
     * @param batched Whether we are batching it
     * @param key The key
     * @param value The new value
     * @param executeNow Whether to execute the statement now or wait til any batch
     * @throws MappedDatastoreException Thrown if an error occurs
     */
    protected void internalUpdate(ObjectProvider ownerSM, ManagedConnection conn, boolean batched, Object key, Object value,
                                  boolean executeNow) throws MappedDatastoreException
    {
        ExecutionContext ec = ownerSM.getExecutionContext();
        SQLController sqlControl = getStoreMgr().getSQLController();
        try {
            PreparedStatement ps = sqlControl.getStatementForUpdate(conn, updateStmt, false);
            try
            {
                int jdbcPosition = 1;
                if (valueMapping != null)
                {
                    jdbcPosition = BackingStoreHelper.populateValueInStatement(ec, ps, value,
                        jdbcPosition, valueMapping);
                }
                else
                {
                    jdbcPosition = BackingStoreHelper.populateEmbeddedValueFieldsInStatement(ownerSM, value,
                        ps, jdbcPosition, (JoinTable)mapTable, this);
                }
                jdbcPosition = BackingStoreHelper.populateOwnerInStatement(ownerSM, ec, ps, jdbcPosition,
                    this);
                jdbcPosition = BackingStoreHelper.populateKeyInStatement(ec, ps, key, jdbcPosition,
                    keyMapping);

                if (batched)
                {
                    ps.addBatch();
                }
                else
                {
                    sqlControl.executeStatementUpdate(conn, updateStmt, ps, true);
                }
            }
            finally
            {
                sqlControl.closeStatement(conn, ps);
            }
        }
        catch (SQLException e)
        {
            throw new MappedDatastoreException(getUpdateStmt(), e);
        }
    }

    /**
     * Method to process a "put" statement (where the key has no value in the join table).
     * @param ownerSM StateManager for the owner
     * @param conn The Connection
     * @param batched Whether we are batching it
     * @param key The key
     * @param value The value
     * @param executeNow Whether to execute the statement now or wait til batching
     * @return The return codes from any executed statement
     * @throws MappedDatastoreException Thrown if an error occurs
     */
    protected int[] internalPut(ObjectProvider ownerSM, ManagedConnection conn, boolean batched, Object key, Object value, boolean executeNow)
        throws MappedDatastoreException
    {
        ExecutionContext ec = ownerSM.getExecutionContext();
        SQLController sqlControl = getStoreMgr().getSQLController();
        try
        {
            PreparedStatement ps = sqlControl.getStatementForUpdate(conn, putStmt, false);
            try
            {
                int jdbcPosition = 1;
                if (valueMapping != null)
                {
                    jdbcPosition = BackingStoreHelper.populateValueInStatement(ec, ps, value,
                        jdbcPosition, valueMapping);
                }
                else
                {
                    jdbcPosition = BackingStoreHelper.populateEmbeddedValueFieldsInStatement(ownerSM, value,
                        ps, jdbcPosition, (JoinTable)mapTable, this);
                }
                jdbcPosition = BackingStoreHelper.populateOwnerInStatement(ownerSM, ec, ps,
                    jdbcPosition, this);
                if (adapterMapping != null)
                {
                    // Only set the adapter mapping if we have a new object
                    long nextIDAdapter = getNextIDForAdapterColumn(ownerSM);
                    adapterMapping.setObject(ec, ps, MappingHelper.getMappingIndices(jdbcPosition, adapterMapping),
                        Long.valueOf(nextIDAdapter));
                    jdbcPosition += adapterMapping.getNumberOfDatastoreMappings();
                }
                jdbcPosition = BackingStoreHelper.populateKeyInStatement(ec, ps, key, jdbcPosition, keyMapping);

                // Execute the statement
                return sqlControl.executeStatementUpdate(conn, putStmt, ps, true);
            }
            finally
            {
                sqlControl.closeStatement(conn, ps);
            }
        }
        catch (SQLException e)
        {
            throw new MappedDatastoreException(getPutStmt(), e);
        }
    }

    /**
     * Accessor for the higher id when elements primary key can't be part of
     * the primary key by datastore limitations like BLOB types can't be
     * primary keys.
     * @param sm State Manager for container
     * @return The next id
     */
    private int getNextIDForAdapterColumn(ObjectProvider sm)
    {
        int nextID;
        try
        {
            ExecutionContext ec = sm.getExecutionContext();
            ManagedConnection mconn = getStoreMgr().getConnection(ec);
            SQLController sqlControl = getStoreMgr().getSQLController();
            try
            {
                String stmt = getMaxAdapterColumnIdStmt();
                PreparedStatement ps = sqlControl.getStatementForQuery(mconn, stmt);

                try
                {
                    int jdbcPosition = 1;
                    BackingStoreHelper.populateOwnerInStatement(sm, ec, ps, jdbcPosition, this);
                    ResultSet rs = sqlControl.executeStatementQuery(mconn, stmt, ps);
                    try
                    {
                        if (!rs.next())
                        {
                            nextID = 1;
                        }
                        else
                        {
                            nextID = rs.getInt(1)+1;
                        }

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

        return nextID;
    }
    /**
     * Generate statement for obtaining the maximum id.
     * <PRE>
     * SELECT MAX(SCOID) FROM MAPTABLE
     * WHERE OWNERCOL=?
     * </PRE>
     * @return The Statement returning the higher id
     */
    private String getMaxAdapterColumnIdStmt()
    {
        StringBuffer stmt = new StringBuffer();
        stmt.append("SELECT MAX(" + adapterMapping.getDatastoreMapping(0).getDatastoreField().getIdentifier().toString() + ")");
        stmt.append(" FROM ");
        stmt.append(mapTable.toString());
        stmt.append(" WHERE ");
        for (int i=0; i<ownerMapping.getNumberOfDatastoreMappings(); i++)
        {
            if (i > 0)
            {
                stmt.append(" AND ");
            }
            stmt.append(ownerMapping.getDatastoreMapping(i).getDatastoreField().getIdentifier().toString());
            stmt.append(" = ");
            stmt.append(((RDBMSMapping)ownerMapping.getDatastoreMapping(i)).getUpdateInputParameter());
        }
        return stmt.toString();
    }
}
TOP

Related Classes of org.datanucleus.store.rdbms.scostore.RDBMSJoinMapStore

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.