com.impetus.kundera.persistence.AssociationBuilder.java Source code

Java tutorial

Introduction

Here is the source code for com.impetus.kundera.persistence.AssociationBuilder.java

Source

/*******************************************************************************
 * * Copyright 2012 Impetus Infotech.
 *  *
 *  * 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.
 ******************************************************************************/
package com.impetus.kundera.persistence;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.impetus.kundera.client.Client;
import com.impetus.kundera.client.EnhanceEntity;
import com.impetus.kundera.graph.Node;
import com.impetus.kundera.graph.ObjectGraphUtils;
import com.impetus.kundera.index.DocumentIndexer;
import com.impetus.kundera.index.LuceneQueryUtils;
import com.impetus.kundera.lifecycle.states.ManagedState;
import com.impetus.kundera.metadata.KunderaMetadataManager;
import com.impetus.kundera.metadata.MetadataUtils;
import com.impetus.kundera.metadata.model.EntityMetadata;
import com.impetus.kundera.metadata.model.JoinTableMetadata;
import com.impetus.kundera.metadata.model.Relation;
import com.impetus.kundera.metadata.model.Relation.ForeignKey;
import com.impetus.kundera.persistence.context.MainCache;
import com.impetus.kundera.persistence.context.PersistenceCacheManager;
import com.impetus.kundera.property.PropertyAccessException;
import com.impetus.kundera.property.PropertyAccessorHelper;
import com.impetus.kundera.utils.ObjectUtils;

/**
 * This class is responsible for building association for given entities.
 * 
 * @author vivek.mishra
 */
final class AssociationBuilder {

    private static Log log = LogFactory.getLog(AssociationBuilder.class);

    /**
     * Populates entities related via join table for <code>entity</code>
     * 
     * @param entity
     * @param entityMetadata
     * @param delegator
     * @param relation
     */
    void populateRelationFromJoinTable(Object entity, EntityMetadata entityMetadata, PersistenceDelegator delegator,
            Relation relation) {

        JoinTableMetadata jtMetadata = relation.getJoinTableMetadata();
        String joinTableName = jtMetadata.getJoinTableName();

        Set<String> joinColumns = jtMetadata.getJoinColumns();
        Set<String> inverseJoinColumns = jtMetadata.getInverseJoinColumns();

        String joinColumnName = (String) joinColumns.toArray()[0];
        String inverseJoinColumnName = (String) inverseJoinColumns.toArray()[0];

        // EntityMetadata relMetadata =
        // delegator.getMetadata(relation.getTargetEntity());

        Client pClient = delegator.getClient(entityMetadata);
        String entityId = PropertyAccessorHelper.getId(entity, entityMetadata);
        List<?> foreignKeys = pClient.getColumnsById(joinTableName, joinColumnName, inverseJoinColumnName,
                entityId);

        List childrenEntities = new ArrayList();
        for (Object foreignKey : foreignKeys) {
            EntityMetadata childMetadata = delegator.getMetadata(relation.getTargetEntity());

            Object child = delegator.find(relation.getTargetEntity(), foreignKey);
            Object obj = child instanceof EnhanceEntity && child != null ? ((EnhanceEntity) child).getEntity()
                    : child;

            // If child has any bidirectional relationship, process them here
            Field biDirectionalField = getBiDirectionalField(entity.getClass(), relation.getTargetEntity());
            boolean isBidirectionalRelation = (biDirectionalField != null);

            if (isBidirectionalRelation && obj != null) {

                String columnValue = PropertyAccessorHelper.getId(obj, childMetadata);
                Object[] pKeys = pClient.findIdsByColumn(joinTableName, joinColumnName, inverseJoinColumnName,
                        columnValue, entityMetadata.getEntityClazz());
                List parents = delegator.find(entity.getClass(), pKeys);
                PropertyAccessorHelper.set(obj, biDirectionalField,
                        ObjectUtils.getFieldInstance(parents, biDirectionalField));
            }

            childrenEntities.add(obj);
        }

        Field childField = relation.getProperty();

        try {
            PropertyAccessorHelper.set(entity, childField,
                    PropertyAccessorHelper.isCollection(childField.getType())
                            ? ObjectUtils.getFieldInstance(childrenEntities, childField)
                            : childrenEntities.get(0));
            PersistenceCacheManager.addEntityToPersistenceCache(entity, delegator, entityId);
        } catch (PropertyAccessException ex) {
            throw new EntityReaderException(ex);
        }

    }

    /**
     * @param entity
     * @param pd
     * @param relation
     * @param relationValue
     */
    void populateRelationFromValue(Object entity, PersistenceDelegator pd, Relation relation, Object relationValue,
            EntityMetadata childMetadata) {
        Class<?> childClass = relation.getTargetEntity();

        Object child = pd.find(childClass, relationValue.toString());
        child = child != null && child instanceof EnhanceEntity ? ((EnhanceEntity) child).getEntity() : child;

        if (child != null) {
            PropertyAccessorHelper.set(entity, relation.getProperty(), child);

            // If child has any bidirectional relationship, process them here
            Field biDirectionalField = getBiDirectionalField(entity.getClass(), relation.getTargetEntity());
            boolean isBidirectionalRelation = (biDirectionalField != null);

            if (isBidirectionalRelation) {
                Relation reverseRelation = childMetadata.getRelation(biDirectionalField.getName());

                if (relation.getType().equals(ForeignKey.ONE_TO_ONE)) {
                    PropertyAccessorHelper.set(child, reverseRelation.getProperty(), entity);
                } else {
                    String childId = PropertyAccessorHelper.getId(child, childMetadata);
                    EntityMetadata reverseEntityMetadata = KunderaMetadataManager
                            .getEntityMetadata(entity.getClass());
                    populateRelationViaQuery(child, pd, childId, reverseRelation, relation.getJoinColumnName(),
                            reverseEntityMetadata);
                }

            }
        }
    }

    /**
     * @param entity
     * @param pd
     * @param entityId
     * @param relation
     * @param relationName
     */
    void populateRelationViaQuery(Object entity, PersistenceDelegator pd, String entityId, Relation relation,
            String relationName, EntityMetadata childMetadata) {
        Class<?> childClass = relation.getTargetEntity();
        Client childClient = pd.getClient(childMetadata);

        List associatedObjects = null;

        // Since ID is stored at other side of the relationship, we have to
        // query that table

        if (MetadataUtils.useSecondryIndex(childClient.getPersistenceUnit())) {
            // Pass this entity id as a value to be searched for
            associatedObjects = pd.find(childClass, entityId, relationName);
        } else {
            associatedObjects = getAssociatedEntitiesFromLucene(entity, entityId, childClass, childClient);
        }

        List associatedEntities = new ArrayList();
        if (associatedObjects != null && !associatedObjects.isEmpty()) {
            for (Object o : associatedObjects) {
                if (o instanceof EnhanceEntity) {
                    associatedEntities.add(((EnhanceEntity) o).getEntity());
                } else {
                    associatedEntities.add(o);
                }
            }
            setAssociatedEntities(entity, relation.getProperty(), associatedEntities);
        }

        // If child has any bidirectional relationship, process them here
        Field biDirectionalField = getBiDirectionalField(entity.getClass(), relation.getTargetEntity());
        boolean isBidirectionalRelation = (biDirectionalField != null);

        if (isBidirectionalRelation && associatedEntities != null) {
            Relation reverseRelation = childMetadata.getRelation(biDirectionalField.getName());

            for (Object child : associatedEntities) {
                // String childId = PropertyAccessorHelper.getId(child,
                // childMetadata);
                // EntityMetadata reverseEntityMetadata =
                // KunderaMetadataManager.getEntityMetadata(entity.getClass());

                // populateRelationFromValue(child, pd, reverseRelation,
                // entityId, childMetadata);
                PropertyAccessorHelper.set(child, reverseRelation.getProperty(), entity);
            }

        }

        if (associatedEntities != null) {
            // Save children entities to persistence cache
            MainCache mainCache = (MainCache) pd.getPersistenceCache().getMainCache();

            for (Object child : associatedEntities) {
                Object childId = PropertyAccessorHelper.getId(child, childMetadata);

                String nodeId = ObjectGraphUtils.getNodeId(childId, childMetadata.getEntityClazz());
                Node node = new Node(nodeId, childMetadata.getEntityClazz(), new ManagedState(),
                        pd.getPersistenceCache());
                node.setData(child);
                node.setPersistenceDelegator(pd);
                mainCache.addNodeToCache(node);
            }
        }

        // Recursively find associated entities
        if ((childMetadata.getRelationNames() == null || childMetadata.getRelationNames().isEmpty())
                && !childMetadata.isRelationViaJoinTable()) {
            // There is no relation (not even via Join Table), nothing to do
            log.info("Nothing to do, simply moving to next:");
        }

        else if (associatedEntities != null) {
            // These entities has associated entities, find them recursively.
            for (Object associatedEntity : associatedEntities) {
                associatedEntity = pd.getReader(childClient).recursivelyFindEntities(associatedEntity, null,
                        childMetadata, pd);
            }
        }

    }

    /**
     * Retrieves associated entities via running query into Lucene indexing.
     */
    private List getAssociatedEntitiesFromLucene(Object entity, String entityId, Class<?> childClass,
            Client childClient) {
        List associatedEntities;
        // Lucene query, where entity class is child class, parent class is
        // entity's class
        // and parent Id is entity ID! that's it!
        String query = LuceneQueryUtils.getQuery(DocumentIndexer.PARENT_ID_CLASS,
                entity.getClass().getCanonicalName().toLowerCase(), DocumentIndexer.PARENT_ID_FIELD, entityId,
                childClass.getCanonicalName().toLowerCase());

        Map<String, String> results = childClient.getIndexManager().search(query);
        Set<String> rsSet = new HashSet<String>(results.values());

        if (childClass.equals(entity.getClass())) {
            associatedEntities = (List<Object>) childClient.findAll(childClass, rsSet.toArray(new String[] {}));
        } else {
            associatedEntities = (List<Object>) childClient.findAll(childClass, rsSet.toArray(new String[] {}));
        }
        return associatedEntities;
    }

    /**
     * Returns associated bi-directional field.
     * 
     * @param originalClazz
     *            Original class
     * @param referencedClass
     *            Referenced class.
     */
    public Field getBiDirectionalField(Class originalClazz, Class referencedClass) {
        Field[] fields = referencedClass.getDeclaredFields();
        Class<?> clazzz = null;
        Field biDirectionalField = null;
        for (Field field : fields) {
            clazzz = field.getType();
            if (PropertyAccessorHelper.isCollection(clazzz)) {
                ParameterizedType type = (ParameterizedType) field.getGenericType();
                Type[] types = type.getActualTypeArguments();
                clazzz = (Class<?>) types[0];
            }
            if (clazzz.equals(originalClazz)) {
                biDirectionalField = field;
                break;
            }
        }

        return biDirectionalField;
    }

    /**
     * Sets associated entities to <code>entity</code>
     * 
     * @param entity
     * @param f
     * @param associatedEntities
     * @return
     * @throws PropertyAccessException
     */
    private Set<?> setAssociatedEntities(Object entity, Field f, List<?> associatedEntities)
            throws PropertyAccessException {
        Set chids = new HashSet();
        if (associatedEntities != null) {
            chids = new HashSet(associatedEntities);
            PropertyAccessorHelper.set(entity, f,
                    PropertyAccessorHelper.isCollection(f.getType())
                            ? ObjectUtils.getFieldInstance(associatedEntities, f)
                            : associatedEntities.get(0));
        }
        return chids;
    }

}