Package org.jpox.store.rdbms.scostore

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

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

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

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


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

import java.lang.reflect.Array;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import org.jpox.ClassLoaderResolver;
import org.jpox.ManagedConnection;
import org.jpox.ObjectManager;
import org.jpox.StateManager;
import org.jpox.exceptions.JPOXDataStoreException;
import org.jpox.exceptions.JPOXUserException;
import org.jpox.metadata.AbstractMemberMetaData;
import org.jpox.metadata.ArrayMetaData;
import org.jpox.store.mapped.DatastoreClass;
import org.jpox.store.mapped.DatastoreContainerObject;
import org.jpox.store.mapped.DatastoreIdentifier;
import org.jpox.store.mapped.expression.LogicSetExpression;
import org.jpox.store.mapped.expression.QueryExpression;
import org.jpox.store.mapped.expression.ScalarExpression;
import org.jpox.store.mapped.mapping.JavaTypeMapping;
import org.jpox.store.mapped.mapping.MappingConsumer;
import org.jpox.store.mapped.mapping.Mappings;
import org.jpox.store.query.IncompatibleQueryElementTypeException;
import org.jpox.store.rdbms.RDBMSManager;
import org.jpox.store.rdbms.SQLController;
import org.jpox.store.rdbms.mapping.RDBMSMapping;
import org.jpox.store.rdbms.query.UnionIteratorStatement;
import org.jpox.util.JPOXLogger;

/**
* Backing store for an array that is formed by a foreign key in the table of the
* element type. Only supported when the element is a PersistenceCapable type (since
* that has its own element table, capable of having a FK!)
*
* @version $Revision: 1.33 $
*/
public class FKArrayStore extends AbstractArrayStore
{
    /** Statement for updating a foreign key in a 1-N unidirectional */
    private String updateFkStmt;

    /** Statement for nullifying a FK in the element. */
    private String clearNullifyStmt;

    /**
     * Constructor.
     * @param fmd Field MetaData for the field that this represents
     * @param storeMgr The Store Manager in use
     * @param clr The ClassLoaderResolver
     */
    public FKArrayStore(AbstractMemberMetaData fmd, RDBMSManager storeMgr, ClassLoaderResolver clr)
    {
        super(storeMgr, clr);

        setOwnerMemberMetaData(fmd);
        ArrayMetaData arrmd = fmd.getArray();
        if (arrmd == null)
        {
            throw new JPOXUserException(LOCALISER.msg("056000", fmd.getFullFieldName()));
        }

        // Load the element class
        elementType = fmd.getType().getComponentType().getName();
        Class element_class = clr.classForName(elementType);

        if (storeMgr.getOMFContext().getTypeManager().isReferenceType(element_class))
        {
            // Take the metadata for the first implementation of the reference type
            emd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForImplementationOfReference(element_class,null,clr);
            if (emd != null)
            {
                // Pretend we have a relationship with this one implementation
                elementType = emd.getFullClassName();
            }
        }
        else
        {
            // Check that the element class has MetaData
            emd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForClass(element_class, clr);
        }
        if (emd == null)
        {
            throw new JPOXUserException(LOCALISER.msg("056003", element_class.getName(), fmd.getFullFieldName()));
        }

        elementInfo = getElementInformationForClass();
        if (elementInfo != null && elementInfo.length > 1)
        {
            throw new JPOXUserException(LOCALISER.msg("056045",
                ownerMemberMetaData.getFullFieldName()));
        }

        elementMapping = elementInfo[0].getDatastoreClass().getIDMapping(); // Just use the first element type as the guide for the element mapping
        elementsAreEmbedded = false; // Can't embed element when using FK relation
        elementsAreSerialised = false; // Can't serialise element when using FK relation

        // Get the field in the element table (if any)
        String mappedByFieldName = fmd.getMappedBy();
        if (mappedByFieldName != null)
        {
            // 1-N FK bidirectional
            // The element class has a field for the owner.
            AbstractMemberMetaData eofmd = storeMgr.getOMFContext().getMetaDataManager().getMetaDataForMember(element_class, clr, mappedByFieldName);
            if (eofmd == null)
            {
                throw new JPOXUserException(LOCALISER.msg("056024", fmd.getFullFieldName(),
                    mappedByFieldName, element_class.getName()));
            }

            // Check that the type of the element "mapped-by" field is consistent with the owner type
            if (!clr.isAssignableFrom(eofmd.getType(), fmd.getAbstractClassMetaData().getFullClassName()))
            {
                throw new JPOXUserException(LOCALISER.msg("056025", fmd.getFullFieldName(),
                    eofmd.getFullFieldName(), eofmd.getTypeName(), fmd.getAbstractClassMetaData().getFullClassName()));
            }

            String ownerFieldName = eofmd.getName();
            ownerMapping = elementInfo[0].getDatastoreClass().getFieldMapping(eofmd);
            if (ownerMapping == null)
            {
                throw new JPOXUserException(LOCALISER.msg("056046",
                    fmd.getAbstractClassMetaData().getFullClassName(), fmd.getName(), elementType, ownerFieldName));
            }
            if (isEmbeddedMapping(ownerMapping))
            {
                throw new JPOXUserException(LOCALISER.msg("056026",
                    ownerFieldName, elementType, eofmd.getTypeName(), fmd.getClassName()));
            }
        }
        else
        {
            // 1-N FK unidirectional
            // The element class knows nothing about the owner (but the table has external mappings)
            ownerMapping = elementInfo[0].getDatastoreClass().getExternalMapping(fmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK);
            if (ownerMapping == null)
            {
                throw new JPOXUserException(LOCALISER.msg("056047",
                    fmd.getAbstractClassMetaData().getFullClassName(), fmd.getName(), elementType));
            }
        }

        orderMapping = elementInfo[0].getDatastoreClass().getExternalMapping(fmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_INDEX);
        if (orderMapping == null)
        {
            throw new JPOXUserException(LOCALISER.msg("056048",
                fmd.getAbstractClassMetaData().getFullClassName(), fmd.getName(), elementType));
        }

        relationDiscriminatorMapping = elementInfo[0].getDatastoreClass().getExternalMapping(fmd, MappingConsumer.MAPPING_TYPE_EXTERNAL_FK_DISCRIM);
        if (relationDiscriminatorMapping != null)
        {
            relationDiscriminatorValue = fmd.getValueForExtension("relation-discriminator-value");
            if (relationDiscriminatorValue == null)
            {
                // No value defined so just use the field name
                relationDiscriminatorValue = fmd.getFullFieldName();
            }
        }

        // TODO Cater for multiple element tables
        containerTable = elementInfo[0].getDatastoreClass();
        if (fmd.getMappedBy() != null && ownerMapping.getDatastoreContainer() != containerTable)
        {
            // Element and owner don't have consistent tables so use the one with the mapping
            // e.g collection is of subclass, yet superclass has the link back to the owner
            containerTable = ownerMapping.getDatastoreContainer();
        }
    }

    /**
     * Generates the statement for clearing items by nulling the owner link out.
     * The statement will be
     * <PRE>
     * UPDATE ARRAYTABLE SET OWNERCOL=NULL, INDEXCOL=NULL [,DISTINGUISHER=NULL]
     * WHERE OWNERCOL=? [AND DISTINGUISHER=?]
     * </PRE>
     * when there is only one element table, and will be
     * <PRE>
     * UPDATE ? SET OWNERCOL=NULL, INDEXCOL=NULL [,DISTINGUISHER=NULL]
     * WHERE OWNERCOL=? [AND DISTINGUISHER=?]
     * </PRE>
     * when there is more than 1 element table.
     * @return The Statement for clearing items for the owner.
     */
    protected String getClearNullifyStmt()
    {
        if (clearNullifyStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("UPDATE ");
            if (elementInfo.length > 1)
            {
                stmt.append("?");
            }
            else
            {
                stmt.append(elementInfo[0].getDatastoreClass().toString());
            }
            stmt.append(" SET ");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(", ");
                }
                stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString() + " = NULL");
            }
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(", ");
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString() + " = NULL");
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(", ");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString() + " = NULL");
                }
            }

            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());
                }
            }
            clearNullifyStmt = stmt.toString();
        }
        return clearNullifyStmt;
    }

    /**
     * Generate statement for updating the owner, index columns in an inverse 1-N.
     * Will result in the statement
     * <PRE>
     * UPDATE ELEMENTTABLE SET FK_COL_1=?, FK_COL_2=?, FK_IDX=? [,DISTINGUISHER=?]
     * WHERE ELEMENT_ID=?
     * </PRE>
     * when we have a single element table, and
     * <PRE>
     * UPDATE ? SET FK_COL_1=?, FK_COL_2=?, FK_IDX=? [,DISTINGUISHER=?]
     * WHERE ELEMENT_ID=?
     * </PRE>
     * when we have multiple element tables possible.
     * @return Statement for updating the owner/index of an element in an inverse 1-N
     */
    private String getUpdateFkStmt()
    {
        if (updateFkStmt == null)
        {
            StringBuffer stmt = new StringBuffer();
            stmt.append("UPDATE ");
            if (elementInfo.length > 1)
            {
                stmt.append("?");
            }
            else
            {
                stmt.append(elementInfo[0].getDatastoreClass().toString());
            }
            stmt.append(" SET ");
            for (int i=0; i<ownerMapping.getNumberOfDatastoreFields(); i++)
            {
                if (i > 0)
                {
                    stmt.append(",");
                }
                stmt.append(ownerMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)ownerMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            for (int i=0; i<orderMapping.getNumberOfDatastoreFields(); i++)
            {
                stmt.append(",");
                stmt.append(orderMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                stmt.append(" = ");
                stmt.append(((RDBMSMapping)orderMapping.getDataStoreMapping(i)).getUpdateInputParameter());
            }
            if (relationDiscriminatorMapping != null)
            {
                for (int i=0; i<relationDiscriminatorMapping.getNumberOfDatastoreFields(); i++)
                {
                    stmt.append(",");
                    stmt.append(relationDiscriminatorMapping.getDataStoreMapping(i).getDatastoreField().getIdentifier().toString());
                    stmt.append(" = ");
                    stmt.append(((RDBMSMapping)relationDiscriminatorMapping.getDataStoreMapping(i)).getUpdateInputParameter());
                }
            }

            stmt.append(" WHERE ");

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

        return updateFkStmt;
    }

    /**
     * Update a FK and element position in the element.
     * @param sm StateManager for the owner
     * @param element The element to update
     * @param owner The owner object to set in the FK
     * @param index The index position (or -1 if not known)
     * @return Whether it was performed successfully
     */
    private boolean updateElementFk(StateManager sm, Object element, Object owner, int index)
    {
        if (element == null)
        {
            return false;
        }

        boolean retval;
        ObjectManager om = sm.getObjectManager();
        String updateFkStmt = getUpdateFkStmt();
        try
        {
            ManagedConnection mconn = storeMgr.getConnection(om);
            SQLController sqlControl = storeMgr.getSQLController();
            try
            {
                PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, updateFkStmt, false);
                try
                {
                    int jdbcPosition = 1;
                    if (elementInfo.length > 1)
                    {
                        DatastoreClass table = storeMgr.getDatastoreClass(element.getClass().getName(), clr);
                        if (table != null)
                        {
                            ps.setString(jdbcPosition++, table.toString());
                        }
                        else
                        {
                            JPOXLogger.PERSISTENCE.info(">> InverseArrayStore.updateElementFK : need to set table in statement but dont know table where to store " + element);
                        }
                    }
                    if (owner == null)
                    {
                        ownerMapping.setObject(om, ps, Mappings.getParametersIndex(jdbcPosition, ownerMapping), null);
                        jdbcPosition += ownerMapping.getNumberOfDatastoreFields();
                    }
                    else
                    {
                        jdbcPosition = populateOwnerInStatement(sm, om, ps, jdbcPosition);
                    }
                    jdbcPosition = populateOrderInStatement(om, ps, index, jdbcPosition);
                    if (relationDiscriminatorMapping != null)
                    {
                        jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                    }
                    jdbcPosition = populateElementInStatement(om, ps, element, jdbcPosition);

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

        return retval;
    }

    /**
     * Method to clear the Array.
     * This is called when the container object is being deleted and the elements are to be removed (maybe for dependent field).
     * @param ownerSM The state manager
     **/
    public void clear(StateManager ownerSM)
    {
        boolean deleteElements = false;
        if (ownerMemberMetaData.getArray().isDependentElement())
        {
            // Elements are dependent and can't exist on their own, so delete them all
            JPOXLogger.DATASTORE.debug(LOCALISER.msg("056034"));
            deleteElements = true;
        }
        else
        {
            if (ownerMapping.isNullable() && orderMapping.isNullable())
            {
                // Field is not dependent, and nullable so we null the FK
                JPOXLogger.DATASTORE.debug(LOCALISER.msg("056036"));
                deleteElements = false;
            }
            else
            {
                // Field is not dependent, and not nullable so we just delete the elements
                JPOXLogger.DATASTORE.debug(LOCALISER.msg("056035"));
                deleteElements = true;
            }
        }

        if (deleteElements)
        {
            ownerSM.getObjectManager().getApiAdapter().isLoaded(ownerSM, ownerMemberMetaData.getAbsoluteFieldNumber()); // Make sure the field is loaded
            Object[] value = (Object[]) ownerSM.provideField(ownerMemberMetaData.getAbsoluteFieldNumber());
            if (value != null && value.length > 0)
            {
                ownerSM.getObjectManager().deleteObjects(value);
            }
        }
        else
        {
            // TODO If the relation is bidirectional we need to clear the owner in the element
            String clearNullifyStmt = getClearNullifyStmt();
            try
            {
                ObjectManager om = ownerSM.getObjectManager();
                ManagedConnection mconn = storeMgr.getConnection(om);
                SQLController sqlControl = storeMgr.getSQLController();
                try
                {
                    PreparedStatement ps = sqlControl.getStatementForUpdate(mconn, clearNullifyStmt, false);
                    try
                    {
                        int jdbcPosition = 1;
                        jdbcPosition = populateOwnerInStatement(ownerSM, om, ps, jdbcPosition);
                        if (relationDiscriminatorMapping != null)
                        {
                            jdbcPosition = populateRelationDiscriminatorInStatement(om, ps, jdbcPosition);
                        }
                        sqlControl.executeStatementUpdate(mconn, clearNullifyStmt, ps, true);
                    }
                    finally
                    {
                        sqlControl.closeStatement(mconn, ps);
                    }
                }
                finally
                {
                    mconn.release();
                }
            }
            catch (SQLException e)
            {
                throw new JPOXDataStoreException(LOCALISER.msg("056013",clearNullifyStmt),e);
            }
        }
    }

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

        // Check that all elements are inserted
        for (int i=0;i<Array.getLength(array);i++)
        {
            validateElementForWriting(ownerSM, Array.get(array, i), null);
        }

        // Update the FK and position of all elements
        int length = Array.getLength(array);
        for (int i=0;i<length;i++)
        {
            Object obj = Array.get(array, i);
            updateElementFk(ownerSM, obj, ownerSM.getObject(), i);
        }

        return true;
    }

    /**
     * Accessor for the iterator statement to retrieve the element(s) for the array.
     * @param ownerSM The StateManager
     * @return The Query Statement.
     **/
    protected QueryExpression getIteratorStatement(StateManager ownerSM)
    {
        if (elementInfo == null)
        {
            return null;
        }

        final ClassLoaderResolver clr = ownerSM.getObjectManager().getClassLoaderResolver();
        QueryExpression stmt = null;
        for (int i=0;i<elementInfo.length;i++)
        {
            final int elementNo = i;
            Class elementCls = clr.classForName(elementType);
            QueryExpression subStmt = new UnionIteratorStatement(
                clr, elementCls, true, this.storeMgr,
                elementCls, elementMapping, elementInfo[elementNo].getDatastoreClass(), false,
                null, true, false).getQueryStatement(null);
            if (stmt == null)
            {
                stmt = subStmt;
            }
            else
            {
                stmt.union(subStmt);
            }
        }

        // Apply condition on join-table owner field to filter by owner
        ScalarExpression ownerExpr = ownerMapping.newScalarExpression(stmt, stmt.getMainTableExpression());
        ScalarExpression ownerVal = ownerMapping.newLiteral(stmt, ownerSM.getObject());
        stmt.andCondition(ownerExpr.eq(ownerVal), true);

        // Apply condition on distinguisher field to filter by distinguisher (when present)
        if (relationDiscriminatorMapping != null)
        {
            ScalarExpression distinguisherExpr = relationDiscriminatorMapping.newScalarExpression(stmt, stmt.getMainTableExpression());
            ScalarExpression distinguisherVal = relationDiscriminatorMapping.newLiteral(stmt, relationDiscriminatorValue);
            stmt.andCondition(distinguisherExpr.eq(distinguisherVal), true);
        }

        // Order elements by their position in the array
        ScalarExpression exprIndex[] = new ScalarExpression[orderMapping.getNumberOfDatastoreFields()];
        boolean descendingOrder[] = new boolean[orderMapping.getNumberOfDatastoreFields()];
        exprIndex = orderMapping.newScalarExpression(stmt, stmt.getMainTableExpression()).getExpressionList().toArray();
        stmt.setOrdering(exprIndex, descendingOrder);

        return stmt;
    }
   
    /**
     * Method used in queries when contains() has been invoked.
     * @param stmt The Query Statement
     * @param parentStmt the parent Query Statement. If there is no parent, <code>parentStmt</code> must be equals to <code>stmt</code>
     * @param ownerMapping the mapping for the owner.
     * @param ownerTe Table Expression for the owner
     * @param listTableAlias  alias for the "List" table.
     * @param filteredElementType The Class Type for the filtered element
     * @param elmExpr The Expression for the element
     * @param elementTableAlias The SQL alias to assign to the expression for the element table.
     * @return expression to the join
     **/
    public ScalarExpression joinElementsTo(
                        QueryExpression stmt,
                        QueryExpression parentStmt,
                        JavaTypeMapping ownerMapping,
                        LogicSetExpression ownerTe,
                        DatastoreIdentifier listTableAlias,
                        Class filteredElementType,
                        ScalarExpression elmExpr,
                        DatastoreIdentifier elementTableAlias)
    {
        ClassLoaderResolver clr=stmt.getClassLoaderResolver();
        if (!clr.isAssignableFrom(elementType,filteredElementType) &&
            !clr.isAssignableFrom(filteredElementType,elementType))
        {
            throw new IncompatibleQueryElementTypeException(elementType, filteredElementType.getName());
        }

        // Join the element table on the owner ID column.
        DatastoreContainerObject filteredElementTable = storeMgr.getDatastoreClass(filteredElementType.getName(), stmt.getClassLoaderResolver());
        stmt.newTableExpression(filteredElementTable, elementTableAlias);

        ScalarExpression ownerExpr = ownerMapping.newScalarExpression(stmt,ownerTe);
        DatastoreIdentifier containerRangeVar = listTableAlias;
        if (stmt.getTableExpression(containerRangeVar) == null)
        {
            containerRangeVar = elementTableAlias;
        }

        ScalarExpression ownerSetExpr = this.ownerMapping.newScalarExpression(stmt,stmt.getTableExpression(containerRangeVar));
        stmt.newTableExpression(containerTable, containerRangeVar);

        stmt.andCondition(ownerExpr.eq(ownerSetExpr),true);

        JavaTypeMapping elementTableID = filteredElementTable.getIDMapping();
        return elementTableID.newScalarExpression(stmt,stmt.getTableExpression(containerRangeVar));

    }
   
}
TOP

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

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.