Package org.apache.ojb.broker.core

Source Code of org.apache.ojb.broker.core.MtoNBroker

package org.apache.ojb.broker.core;

/* Copyright 2003-2005 The Apache Software Foundation
*
* 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.
*/

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.sql.SQLException;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.ArrayUtils;
import org.apache.ojb.broker.MtoNImplementor;
import org.apache.ojb.broker.OJBRuntimeException;
import org.apache.ojb.broker.PersistenceBrokerException;
import org.apache.ojb.broker.PersistenceBrokerSQLException;
import org.apache.ojb.broker.accesslayer.ResultSetAndStatement;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.ClassDescriptor;
import org.apache.ojb.broker.metadata.CollectionDescriptor;
import org.apache.ojb.broker.metadata.DescriptorRepository;
import org.apache.ojb.broker.metadata.FieldDescriptor;
import org.apache.ojb.broker.metadata.JdbcType;
import org.apache.ojb.broker.query.Query;
import org.apache.ojb.broker.util.logging.Logger;
import org.apache.ojb.broker.util.logging.LoggerFactory;

/**
* Manage all stuff related to non-decomposed M:N association.
*
* @author <a href="mailto:thma@apache.org">Thomas Mahler<a>
* @author <a href="mailto:leandro@ibnetwork.com.br">Leandro Rodrigo Saad Cruz<a>
* @author <a href="mailto:mattbaird@yahoo.com">Matthew Baird<a>
* @author <a href="mailto:jbraeuchi@hotmail.com">Jakob Braeuchi</a>
* @author <a href="mailto:armin@codeAuLait.de">Armin Waibel</a>
* @version $Id: MtoNBroker.java,v 1.10.2.9 2005/12/21 22:25:00 tomdz Exp $
*/
public class MtoNBroker
{
    private Logger log = LoggerFactory.getLogger(MtoNBroker.class);

    private PersistenceBrokerImpl pb;
    /**
     * Used to store {@link GenericObject} while transaction running, used as
     * workaround for m:n insert problem.
     * TODO: find better solution for m:n handling
     */
    private List tempObjects = new ArrayList();

    public MtoNBroker(final PersistenceBrokerImpl broker)
    {
        this.pb = broker;
    }

    public void reset()
    {
        tempObjects.clear();
    }

    /**
     * Stores new values of a M:N association in a indirection table.
     *
     * @param cod        The {@link org.apache.ojb.broker.metadata.CollectionDescriptor} for the m:n relation
     * @param realObject The real object
     * @param otherObj   The referenced object
     * @param mnKeys     The all {@link org.apache.ojb.broker.core.MtoNBroker.Key} matching the real object
     */
    public void storeMtoNImplementor(CollectionDescriptor cod, Object realObject, Object otherObj, Collection mnKeys)
    {
        ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(realObject.getClass());
        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, realObject);
        String[] pkColumns = cod.getFksToThisClass();

        ClassDescriptor otherCld = pb.getDescriptorRepository().getDescriptorFor(ProxyHelper.getRealClass(otherObj));
        ValueContainer[] otherPkValues = pb.serviceBrokerHelper().getKeyValues(otherCld, otherObj);

        String[] otherPkColumns = cod.getFksToItemClass();
        String table = cod.getIndirectionTable();
        MtoNBroker.Key key = new MtoNBroker.Key(otherPkValues);

        if(mnKeys.contains(key))
        {
            return;
        }

        /*
        fix for OJB-76, composite M & N keys that have some fields common
        find the "shared" indirection table columns, values and remove these from m- or n- side
        */
        for(int i = 0; i < otherPkColumns.length; i++)
        {
            int index = ArrayUtils.indexOf(pkColumns, otherPkColumns[i]);
            if(index != -1)
            {
                // shared indirection table column found, remove this column from one side
                pkColumns = (String[]) ArrayUtils.remove(pkColumns, index);
                // remove duplicate value too
                pkValues = (ValueContainer[]) ArrayUtils.remove(pkValues, index);
            }
        }

        String[] cols = mergeColumns(pkColumns, otherPkColumns);
        String insertStmt = pb.serviceSqlGenerator().getInsertMNStatement(table, pkColumns, otherPkColumns);
        ValueContainer[] values = mergeContainer(pkValues, otherPkValues);
        GenericObject gObj = new GenericObject(table, cols, values);
        if(! tempObjects.contains(gObj))
        {
            pb.serviceJdbcAccess().executeUpdateSQL(insertStmt, cld, pkValues, otherPkValues);
            tempObjects.add(gObj);
        }
    }

    /**
     * get a Collection of Keys of already existing m:n rows
     *
     * @param cod
     * @param obj
     * @return Collection of Key
     */
    public List getMtoNImplementor(CollectionDescriptor cod, Object obj)
    {
        ResultSetAndStatement rs = null;
        ArrayList result = new ArrayList();
        ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);
        String[] pkColumns = cod.getFksToThisClass();
        String[] fkColumns = cod.getFksToItemClass();
        String table = cod.getIndirectionTable();

        String selectStmt = pb.serviceSqlGenerator().getSelectMNStatement(table, fkColumns, pkColumns);

        ClassDescriptor itemCLD = pb.getDescriptorRepository().getDescriptorFor(cod.getItemClass());
        Collection extents = pb.getDescriptorRepository().getAllConcreteSubclassDescriptors(itemCLD);
        if(extents.size() > 0)
        {
            itemCLD = (ClassDescriptor) extents.iterator().next();
        }
        FieldDescriptor[] itemClassPKFields = itemCLD.getPkFields();
        if(itemClassPKFields.length != fkColumns.length)
        {
            throw new PersistenceBrokerException("All pk fields of the element-class need to" +
                    " be declared in the indirection table. Element class is "
                    + itemCLD.getClassNameOfObject() + " with " + itemClassPKFields.length + " pk-fields." +
                    " Declared 'fk-pointing-to-element-class' elements in collection-descriptor are"
                    + fkColumns.length);
        }
        try
        {
            rs = pb.serviceJdbcAccess().executeSQL(selectStmt, cld, pkValues, Query.NOT_SCROLLABLE);
            while(rs.m_rs.next())
            {
                ValueContainer[] row = new ValueContainer[fkColumns.length];
                for(int i = 0; i < row.length; i++)
                {
                    row[i] = new ValueContainer(rs.m_rs.getObject(i + 1), itemClassPKFields[i].getJdbcType());
                }
                result.add(new MtoNBroker.Key(row));
            }
        }
        catch(PersistenceBrokerException e)
        {
            throw e;
        }
        catch(SQLException e)
        {
            throw new PersistenceBrokerSQLException(e);
        }
        finally
        {
            if(rs != null) rs.close();
        }
        return result;
    }

    /**
     * delete all rows from m:n table belonging to obj
     *
     * @param cod
     * @param obj
     */
    public void deleteMtoNImplementor(CollectionDescriptor cod, Object obj)
    {
        ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);
        String[] pkColumns = cod.getFksToThisClass();
        String table = cod.getIndirectionTable();
        String deleteStmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkColumns, null);
        pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, pkValues, null);
    }

    /**
     * deletes all rows from m:n table that are not used in relatedObjects
     *
     * @param cod
     * @param obj
     * @param collectionIterator
     * @param mnKeys
     */
    public void deleteMtoNImplementor(CollectionDescriptor cod, Object obj, Iterator collectionIterator, Collection mnKeys)
    {
        if(mnKeys.isEmpty() || collectionIterator == null)
        {
            return;
        }
        List workList = new ArrayList(mnKeys);
        MtoNBroker.Key relatedObjKeys;
        ClassDescriptor relatedCld = pb.getDescriptorRepository().getDescriptorFor(cod.getItemClass());
        Object relatedObj;

        // remove keys of relatedObject from the existing m:n rows in workList
        while(collectionIterator.hasNext())
        {
            relatedObj = collectionIterator.next();
            relatedObjKeys = new MtoNBroker.Key(pb.serviceBrokerHelper().getKeyValues(relatedCld, relatedObj, true));
            workList.remove(relatedObjKeys);
        }

        // delete all remaining keys in workList
        ClassDescriptor cld = pb.getDescriptorRepository().getDescriptorFor(obj.getClass());
        ValueContainer[] pkValues = pb.serviceBrokerHelper().getKeyValues(cld, obj);

        String[] pkColumns = cod.getFksToThisClass();
        String[] fkColumns = cod.getFksToItemClass();
        String table = cod.getIndirectionTable();
        String deleteStmt;

        ValueContainer[] fkValues;
        Iterator iter = workList.iterator();
        while(iter.hasNext())
        {
            fkValues = ((MtoNBroker.Key) iter.next()).m_containers;
            deleteStmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkColumns, fkColumns);
            pb.serviceJdbcAccess().executeUpdateSQL(deleteStmt, cld, pkValues, fkValues);
        }
    }

    /**
     * @param m2n
     */
    public void storeMtoNImplementor(MtoNImplementor m2n)
    {
        if(log.isDebugEnabled()) log.debug("Storing M2N implementor [" + m2n + "]");
        insertOrDeleteMtoNImplementor(m2n, true);
    }

    /**
     * @param m2n
     */
    public void deleteMtoNImplementor(MtoNImplementor m2n)
    {
        if(log.isDebugEnabled()) log.debug("Deleting M2N implementor [" + m2n + "]");
        insertOrDeleteMtoNImplementor(m2n, false);
    }


    /**
     * @see org.apache.ojb.broker.PersistenceBroker#deleteMtoNImplementor
     */
    private void insertOrDeleteMtoNImplementor(MtoNImplementor m2nImpl, boolean insert)
            throws PersistenceBrokerException
    {
        //look for a collection descriptor on left  such as left.element-class-ref='right'
        DescriptorRepository dr = pb.getDescriptorRepository();

        Object leftObject = m2nImpl.getLeftObject();
        Class leftClass = m2nImpl.getLeftClass();
        Object rightObject = m2nImpl.getRightObject();
        Class rightClass = m2nImpl.getRightClass();

        //are written per class, maybe referencing abstract classes or interfaces
        //so let's look for collection descriptors on the left class and try to
        // handle extents on teh right class
        ClassDescriptor leftCld = dr.getDescriptorFor(leftClass);
        ClassDescriptor rightCld = dr.getDescriptorFor(rightClass);
        //Vector leftColds = leftCld.getCollectionDescriptors();
        CollectionDescriptor wanted = m2nImpl.getLeftDescriptor();

        if(leftObject == null || rightObject == null)
        {
            //TODO: to be implemented, must change MtoNImplementor
            //deleteMtoNImplementor(wanted,leftObject) || deleteMtoNImplementor(wanted,rightObject)
            log.error("Can't handle MtoNImplementor in correct way, found a 'null' object");
        }
        else
        {
            //delete only one row
            ValueContainer[] leftPkValues = pb.serviceBrokerHelper().getKeyValues(leftCld, leftObject);
            ValueContainer[] rightPkValues = pb.serviceBrokerHelper().getKeyValues(rightCld, rightObject);
            String[] pkLeftColumns = wanted.getFksToThisClass();
            String[] pkRightColumns = wanted.getFksToItemClass();
            String table = wanted.getIndirectionTable();
            if(table == null) throw new PersistenceBrokerException("Can't remove MtoN implementor without an indirection table");

            String stmt;
            String[] cols = mergeColumns(pkLeftColumns, pkRightColumns);
            ValueContainer[] values = mergeContainer(leftPkValues, rightPkValues);
            if(insert)
            {
                stmt = pb.serviceSqlGenerator().getInsertMNStatement(table, pkLeftColumns, pkRightColumns);
                GenericObject gObj = new GenericObject(table, cols, values);
                if(!tempObjects.contains(gObj))
                {
                    pb.serviceJdbcAccess().executeUpdateSQL(stmt, leftCld, leftPkValues, rightPkValues);
                    tempObjects.add(gObj);
                }
            }
            else
            {
                stmt = pb.serviceSqlGenerator().getDeleteMNStatement(table, pkLeftColumns, pkRightColumns);
                pb.serviceJdbcAccess().executeUpdateSQL(stmt, leftCld, leftPkValues, rightPkValues);
            }
        }
    }

    private String[] mergeColumns(String[] first, String[] second)
    {
        String[] cols = new String[first.length + second.length];
        System.arraycopy(first, 0, cols, 0, first.length);
        System.arraycopy(second, 0, cols, first.length, second.length);
        return cols;
    }

    private ValueContainer[] mergeContainer(ValueContainer[] first, ValueContainer[] second)
    {
        ValueContainer[] values = new ValueContainer[first.length + second.length];
        System.arraycopy(first, 0, values, 0, first.length);
        System.arraycopy(second, 0, values, first.length, second.length);
        return values;
    }



// ************************************************************************
// inner class
// ************************************************************************

    /**
     * This is a helper class to model a Key of an Object
     */
    private static final class Key
    {
        final ValueContainer[] m_containers;

        Key(final ValueContainer[] containers)
        {
            m_containers = new ValueContainer[containers.length];

            for(int i = 0; i < containers.length; i++)
            {
                Object value = containers[i].getValue();
                JdbcType type = containers[i].getJdbcType();

                // BRJ:
                // convert all Numbers to Long to simplify equals
                // Long(100) is not equal to Integer(100)
                //
                // could lead to problems when Floats are used as key
                // converting to String could be a better alternative
                if(value instanceof Number)
                {
                    value = new Long(((Number) value).longValue());
                }

                m_containers[i] = new ValueContainer(value, type);
            }
        }

        public boolean equals(Object other)
        {
            if(other == this)
            {
                return true;
            }
            if(!(other instanceof Key))
            {
                return false;
            }

            Key otherKey = (Key) other;
            EqualsBuilder eb = new EqualsBuilder();

            eb.append(m_containers, otherKey.m_containers);
            return eb.isEquals();
        }

        public int hashCode()
        {
            HashCodeBuilder hb = new HashCodeBuilder();
            hb.append(m_containers);

            return hb.toHashCode();
        }
    }



    // ************************************************************************
    // inner class
    // ************************************************************************
    private static final class GenericObject
    {
         private String tablename;
        private String[] columnNames;
        private ValueContainer[] values;

        public GenericObject(String tablename, String[] columnNames, ValueContainer[] values)
        {
            this.tablename = tablename;
            this.columnNames = columnNames;
            this.values = values;
            if(values != null && columnNames.length != values.length)
            {
                throw new OJBRuntimeException("Column name array and value array have NOT same length");
            }
        }

        public boolean equals(Object obj)
        {
            if(this == obj)
            {
                return true;
            }
            boolean result = false;
            if(obj instanceof GenericObject)
            {
                GenericObject other = (GenericObject) obj;
                result = (tablename.equalsIgnoreCase(other.tablename)
                        && (columnNames != null)
                        && (other.columnNames != null)
                        && (columnNames.length == other.columnNames.length));

                if(result)
                {
                    for (int i = 0; i < columnNames.length; i++)
                    {
                        int otherIndex = other.indexForColumn(columnNames[i]);
                        if(otherIndex < 0)
                        {
                            result = false;
                            break;
                        }
                        result = values[i].equals(other.values[otherIndex]);
                        if(!result) break;
                    }
                }
            }
            return result;
        }

        int indexForColumn(String name)
        {
            int result = -1;
            for (int i = 0; i < columnNames.length; i++)
            {
                if(columnNames[i].equals(name))
                {
                    result = i;
                    break;
                }
            }
            return result;
        }

        public int hashCode()
        {
            return super.hashCode();
        }

        public ValueContainer getValueFor(String columnName)
        {
            try
            {
                return values[indexForColumn(columnName)];
            }
            catch(Exception e)
            {
                throw new OJBRuntimeException("Can't find value for column " + columnName
                        + (indexForColumn(columnName) < 0 ? ". Column name was not found" : ""), e);
            }
        }

        public String getTablename()
        {
            return tablename;
        }

        public String[] getColumnNames()
        {
            return columnNames;
        }

        public ValueContainer[] getValues()
        {
            return values;
        }

        public void setValues(ValueContainer[] values)
        {
            this.values = values;
        }

        public String toString()
        {
            return new ToStringBuilder(this)
                    .append("tableName", tablename)
                    .append("columnNames", columnNames)
                    .append("values", values)
                    .toString();
        }
    }
}
TOP

Related Classes of org.apache.ojb.broker.core.MtoNBroker

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.