org.babyfish.hibernate.collection.spi.persistence.SetBasePersistence.java Source code

Java tutorial

Introduction

Here is the source code for org.babyfish.hibernate.collection.spi.persistence.SetBasePersistence.java

Source

/*
 * BabyFish, Object Model Framework for Java and JPA.
 * https://github.com/babyfish-ct/babyfish
 *
 * Copyright (c) 2008-2015, Tao Chen
 *
 * 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.
 *
 * Please visit "http://opensource.org/licenses/LGPL-3.0" to know more.
 *
 * 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.
 */
package org.babyfish.hibernate.collection.spi.persistence;

import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.babyfish.collection.ArrayList;
import org.babyfish.collection.HashMap;
import org.babyfish.collection.MACollections;
import org.babyfish.collection.MASet;
import org.babyfish.collection.TreeMap;
import org.babyfish.collection.UnifiedComparator;
import org.babyfish.collection.XCollection;
import org.babyfish.collection.XMap;
import org.babyfish.collection.XSet;
import org.babyfish.collection.spi.laziness.QueuedOperationType;
import org.babyfish.lang.Arguments;
import org.babyfish.lang.IllegalProgramException;
import org.babyfish.lang.Ref;
import org.babyfish.util.LazyResource;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.internal.JoinHelper;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.loader.CollectionAliases;
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.persister.entity.OuterJoinLoadable;
import org.hibernate.type.Type;

/**
 * @author Tao Chen
 */
public abstract class SetBasePersistence<E> extends AbstractBasePersistence<E> {

    private static final long serialVersionUID = 4884987773691896886L;

    private static final LazyResource<CommonResource> LAZY_COMMON_RESOURCE = LazyResource.of(CommonResource.class);

    private static final String MIDDLE_TABLE_ALIAS = "babyfish_mt_alias_";

    private transient List<E> tempList;

    protected SetBasePersistence() {

    }

    protected abstract void setBase(MASet<E> base);

    protected abstract MASet<E> getBase();

    protected abstract UnifiedComparator<? super E> unifiedComparator();

    protected abstract XMap<E, QueuedOperationType> getQueuedOperations();

    protected abstract void performQueuedOperations();

    @SuppressWarnings("unchecked")
    @Override
    public Serializable getSnapshot(CollectionPersister persister) throws HibernateException {
        XSet<E> baseSet = this.getBase();
        UnifiedComparator<? super E> unifiedComparator = baseSet.unifiedComparator();
        Map<E, E> clonedMap;
        if (unifiedComparator.comparator() != null) {
            clonedMap = new TreeMap<E, E>(unifiedComparator.comparator(), unifiedComparator.comparator());
        } else {
            clonedMap = new HashMap<E, E>(unifiedComparator.equalityComparator(),
                    unifiedComparator.equalityComparator(), baseSet.size() + 1, 1.F);
        }
        for (E e : baseSet) {
            E copied = (E) persister.getElementType().deepCopy(e, persister.getFactory());
            clonedMap.put(copied, copied);
        }
        return (Serializable) clonedMap;

    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
        Set<E> baseSet = this.getBase();
        Type elementType = persister.getElementType();
        java.util.Map<E, E> sn = (java.util.Map<E, E>) this.getSnapshot();
        if (sn.size() != baseSet.size()) {
            return false;
        } else {
            Iterator<E> iter = baseSet.iterator();
            while (iter.hasNext()) {
                Object test = iter.next();
                Object oldValue = sn.get(test);
                if (oldValue == null || elementType.isDirty(oldValue, test, this.getSession())) {
                    return false;
                }
            }
            return true;
        }
    }

    @Override
    public boolean needsInserting(Object entry, int i, Type elemType) throws HibernateException {
        Map<?, ?> sn = (Map<?, ?>) getSnapshot();
        Object oldValue = sn.get(entry);
        // note that it might be better to iterate the snapshot but this is safe,
        // assuming the user implements equals() properly, as required by the Set
        // contract!
        return oldValue == null || elemType.isDirty(oldValue, entry, this.getSession());
    }

    @Override
    public boolean needsUpdating(Object entry, int i, Type elemType) throws HibernateException {
        return false;
    }

    @Override
    public boolean isSnapshotEmpty(Serializable snapshot) {
        return ((Map<?, ?>) snapshot).isEmpty();
    }

    @Override
    public void beginRead() {
        this.setInitializing(true);
        this.tempList = new ArrayList<E>(this.unifiedComparator());
    }

    @SuppressWarnings("unchecked")
    @Override
    public E readFrom(ResultSet rs, CollectionPersister persister, CollectionAliases descriptor, Object owner)
            throws HibernateException, SQLException {
        E element = (E) persister.readElement(rs, owner, descriptor.getSuffixedElementAliases(), this.getSession());
        if (element != null) {
            this.tempList.add(element);
        }
        return element;
    }

    @Override
    public boolean endRead() {
        this.getBase().addAll(this.tempList);
        this.tempList = null;
        this.setInitialized();
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void beforeInitialize(CollectionPersister persister, int anticipatedSize) {
        Object instantiate = persister.getCollectionType().instantiate(anticipatedSize);
        if (!(instantiate instanceof MASet<?>)) {
            throw new IllegalProgramException(LAZY_COMMON_RESOURCE.get()
                    .illegalInstantiate(persister.getCollectionType().getClass(), MASet.class));
        }
        this.setBase((MASet<E>) instantiate);
    }

    @Override
    public boolean afterInitialize() {
        this.setInitialized();
        if (this.hasQueuedOperations()) {
            this.performQueuedOperations();
            return false;
        } else {
            return true;
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void initializeFromCache(CollectionPersister persister, Serializable disassembled, Object owner)
            throws HibernateException {
        Set<E> baseSet = this.getBase();
        Serializable[] arr = (Serializable[]) disassembled;
        int size = arr.length;
        this.beforeInitialize(persister, size);
        for (int i = 0; i < size; i++) {
            E element = (E) persister.getElementType().assemble(arr[i], this.getSession(), owner);
            if (element != null) {
                baseSet.add(element);
            }
        }
    }

    @Override
    public boolean empty() {
        return this.getBase().isEmpty();
    }

    @Override
    public Iterator<E> entries(CollectionPersister persister) {
        return MACollections.unmodifiable(this.getBase().iterator());
    }

    @Override
    public Serializable disassemble(CollectionPersister persister) throws HibernateException {
        Set<E> baseSet = this.getBase();
        Serializable[] result = new Serializable[baseSet.size()];
        Iterator<E> iter = baseSet.iterator();
        int i = 0;
        while (iter.hasNext()) {
            result[i++] = persister.getElementType().disassemble(iter.next(), this.getSession(), null);
        }
        return result;
    }

    @Override
    public boolean isRowUpdatePossible() {
        return false;
    }

    @Override
    public boolean isWrapper(Object collection) {
        return this.getBase() == collection;
    }

    @Override
    public Iterator<E> queuedAdditionIterator() {
        XMap<E, QueuedOperationType> map = this.getQueuedOperations();
        final Iterator<Entry<E, QueuedOperationType>> entryIterator = map.entrySet().iterator();
        return new Iterator<E>() {

            @Override
            public boolean hasNext() {
                return entryIterator.hasNext();
            }

            @Override
            public E next() {
                Entry<E, QueuedOperationType> e = entryIterator.next();
                return e.getValue() == QueuedOperationType.ATTACH ? e.getKey() : null;
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    @SuppressWarnings("unchecked")
    @Override
    public Iterator<?> getDeletes(CollectionPersister persister, boolean indexIsFormula) throws HibernateException {
        Set<E> baseSet = this.getBase();
        Type elementType = persister.getElementType();
        final Map<E, E> sn = (Map<E, E>) this.getSnapshot();
        List<E> deletes = new ArrayList<E>(this.unifiedComparator(), sn.size());
        Iterator<E> iter = sn.keySet().iterator();
        while (iter.hasNext()) {
            E test = iter.next();
            if (!baseSet.contains(test)) {
                // the element has been removed from the set
                deletes.add(test);
            }
        }
        iter = baseSet.iterator();
        while (iter.hasNext()) {
            E test = iter.next();
            E oldValue = sn.get(test);
            if (oldValue != null && elementType.isDirty(test, oldValue, this.getSession())) {
                // the element has changed
                deletes.add(oldValue);
            }
        }
        return deletes.iterator();
    }

    @Override
    public Collection<E> getQueuedOrphans(String entityName) {
        XMap<E, QueuedOperationType> map = this.getQueuedOperations();
        if (!map.isEmpty()) {
            XCollection<E> additions = new ArrayList<E>(map.keyUnifiedComparator(), map.size());
            XCollection<E> removals = new ArrayList<E>(map.keyUnifiedComparator(), map.size());
            for (Entry<E, QueuedOperationType> entry : map.entrySet()) {
                if (entry.getValue() == QueuedOperationType.ATTACH) {
                    additions.add(entry.getKey());
                } else {
                    removals.add(entry.getKey());
                }
            }
            return getOrphans(removals, additions, entityName, this.getSession());
        } else {
            return MACollections.<E>emptySet();
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public Collection<E> getOrphans(Serializable snapshot, String entityName) throws HibernateException {
        XMap<E, E> sn = (XMap<E, E>) snapshot;
        return getOrphans(sn.keySet(), this.getBase(), entityName, this.getSession());
    }

    @SuppressWarnings("unchecked")
    @Override
    public E getElement(Object entry) {
        return (E) entry;
    }

    @Override
    public final boolean entryExists(Object entry, int i) {
        return true;
    }

    @Deprecated
    @Override
    public final Object getIndex(Object entry, int i, CollectionPersister persister) {
        throw new UnsupportedOperationException();
    }

    @Deprecated
    @Override
    public final Object getSnapshotElement(Object entry, int i) {
        throw new UnsupportedOperationException("Sets don't support updating by element");
    }

    /**
     * This method is used to replace 
     * "org.hibernate.collection.AbstractPersistentCollection#readElementExistence(Object element)"
     * @param element The example element to be read
     * @return The ref or readed element
     * <ul>
     *  <li>NonNull: Read successfully, check the value of ref to check the read value is null or not</li>
     *  <li>Null: Read failed</li>
     * </ul>
     */
    @SuppressWarnings("unchecked")
    public Ref<E> visionallyRead(E element) {

        Arguments.mustNotBeNull("element", element);
        String role = this.getNonNullRole();

        SessionImplementor session = this.getSession();
        if (session == null || !session.isOpen() || !session.isConnected()) {
            return null;
        }

        SessionFactoryImplementor sessionFactory = session.getFactory();
        QueryableCollection collection = (QueryableCollection) sessionFactory.getCollectionPersister(role);
        EntityPersister elementPersister = collection.getElementPersister();
        Object elementId = elementPersister.getIdentifier(element, this.getSession());
        if (elementId == null) {
            return new Ref<>();
        }
        if (elementPersister.getEntityMetamodel().getIdentifierProperty().getUnsavedValue()
                .isUnsaved((Serializable) elementId)) {
            return new Ref<>();
        }

        CriteriaImpl criteria = new CriteriaImpl(elementPersister.getEntityName(), session);

        /*
         * Add the condition of element.
         */
        criteria.add(Restrictions.idEq(elementId));

        //ownerKey, not ownerId
        Object ownerKey = collection.getCollectionType().getKeyOfOwner(this.getOwner(), session);
        //In Hibernate, isOneToMany means that there is no middle table
        //The @OneToMany of JPA with middle table is consider as many-to-many in Hibernate
        if (sessionFactory.getCollectionPersister(role).isOneToMany()) {
            String[] joinOwnerColumns = collection.getKeyColumnNames();
            StringBuilder sqlBuilder = new StringBuilder();
            for (int i = 0; i < joinOwnerColumns.length; i++) {
                if (i != 0) {
                    sqlBuilder.append(" and ");
                }
                sqlBuilder.append("{alias}.").append(joinOwnerColumns[i]).append(" = ?");
            }
            criteria.add(Restrictions.sqlRestriction(sqlBuilder.toString(), ownerKey, collection.getKeyType()));
        } else {
            String lhsPropertyName = collection.getCollectionType().getLHSPropertyName();
            int lhsPropertyIndex = -1;
            if (lhsPropertyName != null) {
                String[] propertyNames = collection.getOwnerEntityPersister().getPropertyNames();
                for (int i = propertyNames.length - 1; i >= 0; i--) {
                    if (propertyNames[i].equals(lhsPropertyName)) {
                        lhsPropertyIndex = i;
                        break;
                    }
                }
            }
            String[] lhsColumnNames = JoinHelper.getLHSColumnNames(collection.getCollectionType(), lhsPropertyIndex,
                    (OuterJoinLoadable) elementPersister, sessionFactory);
            String[] joinElementColumnNames = collection.getElementColumnNames();
            String[] joinOwnerColumnNames = collection.getKeyColumnNames();
            StringBuilder subQueryBuilder = new StringBuilder();
            subQueryBuilder.append("exists(select * from ").append(collection.getTableName()).append(" as ")
                    .append(MIDDLE_TABLE_ALIAS).append(" where ");
            for (int i = 0; i < joinElementColumnNames.length; i++) {
                if (i != 0) {
                    subQueryBuilder.append(" and ");
                }
                subQueryBuilder.append("{alias}.").append(lhsColumnNames[i]).append(" = ")
                        .append(MIDDLE_TABLE_ALIAS).append('.').append(joinElementColumnNames[i]);
            }
            for (int i = 0; i < joinOwnerColumnNames.length; i++) {
                subQueryBuilder.append(" and ").append(MIDDLE_TABLE_ALIAS).append(".")
                        .append(joinOwnerColumnNames[i]).append(" = ?");
            }
            subQueryBuilder.append(')');
            criteria.add(
                    Restrictions.sqlRestriction(subQueryBuilder.toString(), ownerKey, collection.getKeyType()));
        }
        FlushMode oldFlushMode = session.getFlushMode();
        session.setFlushMode(FlushMode.MANUAL);
        try {
            return new Ref<>((E) criteria.uniqueResult());
        } finally {
            session.setFlushMode(oldFlushMode);
        }
    }
}