org.batoo.jpa.core.impl.collections.ManagedCollection.java Source code

Java tutorial

Introduction

Here is the source code for org.batoo.jpa.core.impl.collections.ManagedCollection.java

Source

/*
 * Copyright (c) 2012-2013, Batu Alp Ceylan
 *
 * This copyrighted material is made available to anyone wishing to use, modify,
 * copy, or redistribute it subject to the terms and conditions of the GNU
 * Lesser General Public License, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License
 * for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this distribution; if not, write to:
 * Free Software Foundation, Inc.
 * 51 Franklin Street, Fifth Floor
 * Boston, MA  02110-1301  USA
 */

package org.batoo.jpa.core.impl.collections;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.IdentityHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.mutable.MutableBoolean;
import org.batoo.common.BatooVersion;
import org.batoo.common.util.BatooUtils;
import org.batoo.jpa.core.impl.criteria.EntryImpl;
import org.batoo.jpa.core.impl.instance.ManagedInstance;
import org.batoo.jpa.core.impl.instance.Status;
import org.batoo.jpa.core.impl.manager.EntityManagerImpl;
import org.batoo.jpa.core.impl.manager.SessionImpl;
import org.batoo.jpa.core.impl.model.attribute.PluralAttributeImpl;
import org.batoo.jpa.core.impl.model.mapping.AssociationMappingImpl;
import org.batoo.jpa.core.impl.model.mapping.PluralAssociationMappingImpl;
import org.batoo.jpa.core.impl.model.mapping.PluralMappingEx;

import com.google.common.collect.Lists;

/**
 * Marker interface for managed collections.
 * 
 * @param <E>
 *            the element type of the collection
 * 
 * @author hceylan
 * @since 2.0.0
 */
public abstract class ManagedCollection<E> implements Serializable {
    private static final long serialVersionUID = BatooVersion.SERIAL_VERSION_UID;

    private transient boolean changed;
    private final transient ManagedInstance<?> managedInstance;
    private final transient PluralMappingEx<?, ?, E> mapping;
    private transient AssociationMappingImpl<?, ?, ?> inverse;
    private final transient int insertBatchSize;

    /**
     * 
     * @since 2.0.0
     */
    public ManagedCollection() {
        super();

        this.mapping = null;
        this.managedInstance = null;
        this.inverse = null;
        this.changed = false;

        this.insertBatchSize = 1;
    }

    /**
     * @param mapping
     *            the mapping
     * @param managedInstance
     *            the managed instance
     * 
     * @since 2.0.0
     */
    public ManagedCollection(PluralMappingEx<?, ?, E> mapping, ManagedInstance<?> managedInstance) {
        super();

        this.mapping = mapping;
        this.managedInstance = managedInstance;
        this.insertBatchSize = this.managedInstance.getSession().getEntityManager().getJdbcAdaptor()
                .getInsertBatchSize();

        if (mapping instanceof PluralAssociationMappingImpl) {
            this.inverse = ((PluralAssociationMappingImpl<?, ?, E>) mapping).getInverse();
        }
    }

    /**
     * Adds the child to the managed list without initialize checks.
     * 
     * @param child
     *            the child to add
     * @return if the child is added
     * 
     * @since 2.0.0
     */
    public abstract boolean addChild(EntryImpl<Object, ManagedInstance<?>> child);

    /**
     * Adds the child to the managed list without initialize checks.
     * 
     * @param child
     *            the child to add
     * @return if the child is added
     * 
     * @since 2.0.0
     */
    public abstract boolean addElement(EntryImpl<Object, ?> child);

    /**
     * Marks the collection as changed.
     * 
     * @since 2.0.0
     */
    protected final void changed() {
        if (!this.changed && (this.managedInstance != null)) {
            this.changed = true;

            this.managedInstance.setChanged(this.mapping);
        }
    }

    /**
     * Flushes the collection
     * 
     * @param connection
     *            the connection
     * @param removals
     *            true if the removals should be flushed and false for the additions
     * @param force
     *            true to force, effective only for insertions and for new entities.
     * @throws SQLException
     *             thrown in case of an SQL error
     * 
     * @since 2.0.0
     */
    public abstract void flush(Connection connection, boolean removals, boolean force) throws SQLException;

    /**
     * Returns the delegate collection.
     * 
     * @return the delegate collection
     * 
     * @since 2.0.0
     */
    public abstract Collection<E> getDelegate();

    /**
     * Returns the insertBatchSize of the ManagedCollection.
     * 
     * @return the insertBatchSize of the ManagedCollection
     * 
     * @since 2.0.0
     */
    protected int getInsertBatchSize() {
        return this.insertBatchSize;
    }

    /**
     * Returns the managed instance of the managed collection.
     * 
     * @return the managed instance of the managed collection
     * 
     * @since 2.0.0
     */
    public ManagedInstance<?> getManagedInstance() {
        return this.managedInstance;
    }

    /**
     * Returns the mapping of the managed collection.
     * 
     * @return the mapping of the managed collection
     * 
     * @since 2.0.0
     */
    protected PluralMappingEx<?, ?, E> getMapping() {
        return this.mapping;
    }

    /**
     * Return the items that are added.
     * 
     * @return the items that are added
     * 
     * @since 2.0.0
     */
    protected abstract Collection<E> getSnapshot();

    /**
     * Initializes the managed collection.
     * 
     * @since 2.0.0
     */
    public abstract void initialize();

    /**
     * Returns if the list is initialized.
     * 
     * @return true if the list is initialized, false otherwise
     * 
     * @since 2.0.0
     */
    public abstract boolean isInitialized();

    /**
     * Merges the collection with the entity
     * 
     * @param entityManager
     *            the entity manager
     * @param instance
     *            the new entity
     * @param requiresFlush
     *            if an implicit flush is required
     * @param processed
     *            registry of processed entities
     * @param instances
     *            the persisted instances
     * 
     * @since 2.0.0
     */
    @SuppressWarnings({ "unchecked", "rawtypes" })
    public void mergeWith(EntityManagerImpl entityManager, Object instance, MutableBoolean requiresFlush,
            IdentityHashMap<Object, Object> processed, LinkedList<ManagedInstance<?>> instances) {
        final ArrayList<E> mergedChildren = Lists.newArrayList();

        final Object children = this.mapping.get(instance);

        // if it is a managed collection and not initialized then skip the merge
        if ((children instanceof ManagedCollection) && !((ManagedCollection<E>) children).isInitialized()) {
            return;
        }

        final Collection<E> collection;
        if (children instanceof Collection) {
            collection = (Collection<E>) children;
        } else {
            collection = ((Map<?, E>) children).values();
        }

        // merge all the new children
        for (final E child : collection) {
            mergedChildren.add(entityManager.mergeImpl(child, requiresFlush, processed, instances,
                    this.mapping.cascadesMerge()));
        }

        // make a snapshot
        this.snapshot();
        final Collection<E> delegate = this.getDelegate();

        boolean changed = false;

        final SessionImpl session = entityManager.getSession();

        final PluralAssociationMappingImpl<?, ?, ?> inversePluralMapping = (this.inverse != null)
                && (this.inverse.getAttribute() instanceof PluralAttributeImpl) ? //
                        (PluralAssociationMappingImpl<?, ?, ?>) this.inverse : null;

        // TODO needs to be overridden by ManagedMap
        // add the new children
        for (int i = 0; i < mergedChildren.size(); i++) {
            final E child = mergedChildren.get(i);
            if (!delegate.contains(child)) {
                this.getDelegate().add(child);

                if (this.inverse != null) {
                    if (inversePluralMapping != null) {
                        final Collection inverseCollection = (Collection<?>) inversePluralMapping.get(child);
                        if (!inverseCollection.contains(this.managedInstance.getInstance())) {
                            inverseCollection.add(this.managedInstance.getInstance());
                        }
                    } else {
                        this.inverse.set(session.get(child).getInstance(), this.managedInstance.getInstance());
                    }
                }

                changed = true;
            }
        }

        // remove the non existent children
        final ArrayList<E> delegateList = Lists.newArrayList(delegate);
        for (int i = 0; i < delegateList.size(); i++) {
            final E child = delegateList.get(i);
            if (!mergedChildren.contains(child)) {
                this.removeChild(child);

                if (this.inverse != null) {
                    if (inversePluralMapping != null) {
                        final Collection inverseCollection = (Collection<?>) inversePluralMapping.get(child);
                        inverseCollection.remove(this.managedInstance.getInstance());
                    } else {
                        this.inverse.set(session.get(child).getInstance(), null);
                    }
                }

                changed = true;
            }
        }

        if (changed) {
            this.changed();
        }
    }

    /**
     * Persists the entities that have been added to the collection.
     * 
     * @param entityManager
     *            the entity manager
     * 
     * @since 2.0.0
     */
    public void persistAdditions(EntityManagerImpl entityManager) {
        final Collection<E> added = BatooUtils.subtract(this.getDelegate(), this.getSnapshot());
        for (final E e : added) {
            entityManager.persist(e);
        }
    }

    /**
     * Refreshes the children of the managed collection.
     * 
     * @since 2.0.0
     */
    public abstract void refreshChildren();

    /**
     * Removes the child from the managed list without initialize checks.
     * 
     * @param child
     *            the child to add
     * 
     * @since 2.0.0
     */
    protected abstract void removeChild(E child);

    /**
     * @param connection
     *            the connection
     * @param removals
     *            true if the removals should be flushed and false for the additions
     * @return returns true if the instance has been removed
     * @throws SQLException
     *             thrown in case of an SQL error
     * 
     * @since 2.0.0
     */
    protected boolean removed(Connection connection, boolean removals) throws SQLException {
        // if the instance removed remove all the relations
        if (removals && (this.managedInstance.getStatus() == Status.REMOVED)) {
            this.mapping.detachAll(connection, this.managedInstance);

            return true;
        }

        return false;
    }

    /**
     * Removes the entities that have been orphaned by removal from the collection.
     * 
     * @param entityManager
     *            the entity manager
     * 
     * @since 2.0.0
     */
    public void removeOrphans(EntityManagerImpl entityManager) {
        final List<E> removed = BatooUtils.subtract(this.getSnapshot(), this.getDelegate());
        for (int i = 0; i < removed.size(); i++) {
            entityManager.remove(removed.get(i));
        }
    }

    /**
     * Clears the changed status.
     * 
     * @since 2.0.0
     */
    protected void reset() {
        this.changed = false;
    }

    /**
     * Makes a snapshot of the collection
     * 
     * @since 2.0.0
     */
    protected abstract void snapshot();
}