com.haulmont.cuba.core.sys.EntityManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.core.sys.EntityManagerImpl.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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.haulmont.cuba.core.sys;

import com.google.common.collect.Sets;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.chile.core.model.MetaProperty;
import com.haulmont.cuba.core.EntityManager;
import com.haulmont.cuba.core.Query;
import com.haulmont.cuba.core.TypedQuery;
import com.haulmont.cuba.core.entity.*;
import com.haulmont.cuba.core.global.*;
import com.haulmont.cuba.core.sys.listener.EntityListenerManager;
import com.haulmont.cuba.core.sys.listener.EntityListenerType;
import com.haulmont.cuba.core.sys.persistence.PersistenceImplSupport;
import com.haulmont.cuba.security.global.UserSession;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.reflect.FieldUtils;
import org.eclipse.persistence.internal.helper.CubaUtil;
import org.eclipse.persistence.sessions.UnitOfWork;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nullable;
import javax.persistence.EntityNotFoundException;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.util.*;

public class EntityManagerImpl implements EntityManager {

    protected javax.persistence.EntityManager delegate;

    protected UserSession userSession;
    protected Metadata metadata;
    protected EntityListenerManager entityListenerMgr;
    protected PersistenceImplSupport support;

    protected boolean softDeletion = true;

    private static final Logger log = LoggerFactory.getLogger(EntityManagerImpl.class);

    protected EntityManagerImpl(javax.persistence.EntityManager jpaEntityManager, UserSession userSession) {
        this.delegate = jpaEntityManager;
        this.userSession = userSession;
        this.metadata = AppBeans.get(Metadata.NAME);
        this.entityListenerMgr = AppBeans.get(EntityListenerManager.NAME);
        this.support = AppBeans.get(PersistenceImplSupport.NAME);
    }

    @Override
    public javax.persistence.EntityManager getDelegate() {
        return delegate;
    }

    @Override
    public boolean isSoftDeletion() {
        return softDeletion;
    }

    @Override
    public void setSoftDeletion(boolean softDeletion) {
        this.softDeletion = softDeletion;
        CubaUtil.setSoftDeletion(softDeletion);
        CubaUtil.setOriginalSoftDeletion(softDeletion);
    }

    @Override
    public void persist(Entity entity) {
        delegate.persist(entity);
        support.registerInstance(entity, this);
    }

    @Override
    public <T extends Entity> T merge(T entity) {
        log.debug("merge {}", entity);

        if (PersistenceHelper.isManaged(entity))
            return entity;

        String storeName = support.getStorageName(delegate.unwrap(UnitOfWork.class));
        entityListenerMgr.fireListener(entity, EntityListenerType.BEFORE_ATTACH, storeName);

        if ((PersistenceHelper.isNew(entity) || !PersistenceHelper.isDetached(entity)) && entity.getId() != null) {
            // if a new instance is passed to merge(), we suppose it is persistent but "not detached"
            Entity destEntity = findOrCreate(entity.getClass(), entity.getId());
            deepCopyIgnoringNulls(entity, destEntity, Sets.newIdentityHashSet());
            //noinspection unchecked
            return (T) destEntity;
        }

        T merged = internalMerge(entity);
        support.registerInstance(merged, this);
        return merged;
    }

    @Override
    @Deprecated
    public <T extends Entity> T merge(T entity, @Nullable View view) {
        T managed = merge(entity);
        if (view != null) {
            metadata.getTools().traverseAttributesByView(view, managed, (e, p) -> {
                /* do nothing, just fetch */ });
        }
        return managed;
    }

    @Override
    @Deprecated
    public <T extends Entity> T merge(T entity, @Nullable String viewName) {
        if (viewName != null) {
            return merge(entity, metadata.getViewRepository().getView(entity.getClass(), viewName));
        } else {
            return merge(entity);
        }
    }

    @Override
    public void remove(Entity entity) {
        log.debug("remove {}", entity);

        if (PersistenceHelper.isDetached(entity)) {
            entity = internalMerge(entity);
        }
        if (entity instanceof SoftDelete && softDeletion) {
            TimeSource timeSource = AppBeans.get(TimeSource.NAME);
            ((SoftDelete) entity).setDeleteTs(timeSource.currentTimestamp());
            ((SoftDelete) entity)
                    .setDeletedBy(userSession != null ? userSession.getUser().getLogin() : "<unknown>");
        } else {
            delegate.remove(entity);
            if (entity instanceof BaseGenericIdEntity) {
                BaseEntityInternalAccess.setRemoved((BaseGenericIdEntity) entity, true);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Entity<K>, K> T find(Class<T> entityClass, K id) {
        Preconditions.checkNotNullArgument(entityClass, "entityClass is null");
        Preconditions.checkNotNullArgument(id, "id is null");

        Object realId = getRealId(id);
        log.debug("find {} by id={}", entityClass.getSimpleName(), realId);
        MetaClass metaClass = metadata.getExtendedEntities().getEffectiveMetaClass(entityClass);
        Class<T> javaClass = metaClass.getJavaClass();

        T entity = delegate.find(javaClass, realId);
        if (entity instanceof SoftDelete && ((SoftDelete) entity).isDeleted() && isSoftDeletion())
            return null; // in case of entity cache
        else
            return entity;
    }

    @Nullable
    @Override
    public <T extends Entity<K>, K> T find(Class<T> entityClass, K id, View... views) {
        Preconditions.checkNotNullArgument(entityClass, "entityClass is null");
        Preconditions.checkNotNullArgument(id, "id is null");

        MetaClass metaClass = metadata.getExtendedEntities().getEffectiveMetaClass(entityClass);
        return findWithViews(metaClass, id, Arrays.asList(views));
    }

    @Nullable
    @Override
    public <T extends Entity<K>, K> T find(Class<T> entityClass, K id, String... viewNames) {
        View[] viewArray = new View[viewNames.length];
        for (int i = 0; i < viewNames.length; i++) {
            viewArray[i] = metadata.getViewRepository().getView(entityClass, viewNames[i]);
        }
        return find(entityClass, id, viewArray);
    }

    protected <T extends Entity> T findWithViews(MetaClass metaClass, Object id, List<View> views) {
        Object realId = getRealId(id);
        log.debug("find {} by id={}, views={}", metaClass.getJavaClass().getSimpleName(), realId, views);

        String pkName = metadata.getTools().getPrimaryKeyName(metaClass);
        if (pkName == null)
            throw new IllegalStateException("Cannot determine PK name for entity " + metaClass);

        Query query = createQuery(String.format("select e from %s e where e.%s = ?1", metaClass.getName(), pkName));
        ((QueryImpl) query).setSingleResultExpected(true);
        query.setParameter(1, realId);
        for (View view : views) {
            query.addView(view);
        }
        //noinspection unchecked
        return (T) query.getFirstResult();
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Entity<K>, K> T getReference(Class<T> clazz, K id) {
        Class<T> effectiveClass = metadata.getExtendedEntities().getEffectiveClass(clazz);

        T reference = delegate.getReference(effectiveClass, getRealId(id));
        BaseEntityInternalAccess.setNew((BaseGenericIdEntity) reference, false);
        return reference;
    }

    protected <T> TypedQuery<T> createQueryInstance(boolean isNative, Class<T> resultClass) {
        return new QueryImpl<>(this, isNative, resultClass);
    }

    @Override
    public Query createQuery() {
        return createQueryInstance(false, null);
    }

    @Override
    public Query createQuery(String qlStr) {
        Query query = createQueryInstance(false, null);
        query.setQueryString(qlStr);
        return query;
    }

    @Override
    public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) {
        TypedQuery<T> query = createQueryInstance(false, resultClass);
        query.setQueryString(qlString);
        return query;
    }

    @Override
    public Query createNativeQuery() {
        return createQueryInstance(true, null);
    }

    @Override
    public Query createNativeQuery(String sql) {
        Query query = createQueryInstance(true, null);
        query.setQueryString(sql);
        return query;
    }

    @Override
    public <T> TypedQuery<T> createNativeQuery(String sql, Class<T> resultClass) {
        TypedQuery<T> query = createQueryInstance(true, resultClass);
        query.setQueryString(sql);
        return query;
    }

    @Override
    @Deprecated
    public void fetch(Entity entity, View view) {
    }

    @Nullable
    @Override
    public <T extends Entity<K>, K> T reload(Class<T> entityClass, K id, String... viewNames) {
        Preconditions.checkNotNullArgument(entityClass, "entityClass is null");
        Preconditions.checkNotNullArgument(id, "id is null");

        if (id instanceof IdProxy && ((IdProxy) id).get() == null) {
            return null;
        }

        T entity = find(entityClass, id, viewNames);
        return entity;
    }

    @SuppressWarnings("unchecked")
    @Nullable
    @Override
    public <T extends Entity> T reload(T entity, String... viewNames) {
        Preconditions.checkNotNullArgument(entity, "entity is null");

        if (entity.getId() instanceof IdProxy && ((IdProxy) entity.getId()).get() == null) {
            return null;
        }

        Entity resultEntity = find(entity.getClass(), entity.getId(), viewNames);
        return (T) resultEntity;
    }

    @Override
    public <T extends Entity> T reloadNN(T entity, String... viewNames) {
        T reloaded = reload(entity, viewNames);
        if (reloaded == null)
            throw new EntityNotFoundException("Entity " + entity + " has been deleted");
        return reloaded;
    }

    @Override
    public void flush() {
        log.debug("flush");
        support.fireEntityListeners(this, false);
        delegate.flush();
    }

    @Override
    public Connection getConnection() {
        return delegate.unwrap(Connection.class);
    }

    /**
     * Copies all property values from source to dest excluding null values.
     */
    protected void deepCopyIgnoringNulls(Entity source, Entity dest, Set<Entity> visited) {
        if (visited.contains(source))
            return;
        visited.add(source);

        MetadataTools metadataTools = metadata.getTools();
        for (MetaProperty srcProperty : source.getMetaClass().getProperties()) {
            String name = srcProperty.getName();

            if (!PersistenceHelper.isLoaded(source, name)) {
                continue;
            }

            if (srcProperty.isReadOnly()) {
                continue;
            }

            Object value = source.getValue(name);
            if (value == null) {
                continue;
            }

            if (srcProperty.getRange().isClass() && !metadataTools.isEmbedded(srcProperty)) {
                if (!metadataTools.isOwningSide(srcProperty))
                    continue;

                Class refClass = srcProperty.getRange().asClass().getJavaClass();
                if (!metadataTools.isPersistent(refClass))
                    continue;

                if (srcProperty.getRange().getCardinality().isMany()) {
                    if (!metadataTools.isOwningSide(srcProperty))
                        continue;
                    //noinspection unchecked
                    Collection<Entity> srcCollection = (Collection) value;
                    Collection<Entity> dstCollection = dest.getValue(name);
                    if (dstCollection == null)
                        throw new RuntimeException("Collection is null: " + srcProperty);
                    boolean equal = srcCollection.size() == dstCollection.size();
                    if (equal) {
                        if (srcProperty.getRange().isOrdered()) {
                            equal = Arrays.equals(srcCollection.toArray(), dstCollection.toArray());
                        } else {
                            equal = CollectionUtils.isEqualCollection(srcCollection, dstCollection);
                        }
                    }
                    if (!equal) {
                        dstCollection.clear();
                        for (Entity srcRef : srcCollection) {
                            Entity reloadedRef = findOrCreate(refClass, srcRef.getId());
                            dstCollection.add(reloadedRef);
                            deepCopyIgnoringNulls(srcRef, reloadedRef, visited);
                        }
                    }
                } else {
                    Entity srcRef = (Entity) value;
                    Entity destRef = dest.getValue(name);
                    if (srcRef.equals(destRef)) {
                        deepCopyIgnoringNulls(srcRef, destRef, visited);
                    } else {
                        Entity reloadedRef = findOrCreate(refClass, srcRef.getId());
                        dest.setValue(name, reloadedRef);
                        deepCopyIgnoringNulls(srcRef, reloadedRef, visited);
                    }
                }
            } else if (metadataTools.isEmbedded(srcProperty)) {
                Entity srcRef = (Entity) value;
                Entity destRef = dest.getValue(name);
                if (destRef != null) {
                    deepCopyIgnoringNulls(srcRef, destRef, visited);
                } else {
                    Entity newRef = (Entity) metadata.create(srcProperty.getRange().asClass().getJavaClass());
                    dest.setValue(name, newRef);
                    deepCopyIgnoringNulls(srcRef, newRef, visited);
                }
            } else {
                dest.setValue(name, value);
            }
        }
    }

    protected <T extends Entity> T findOrCreate(Class<T> entityClass, Object id) {
        Entity reloadedRef = find(entityClass, id);
        if (reloadedRef == null) {
            reloadedRef = metadata.create(entityClass);
            if (reloadedRef instanceof BaseGenericIdEntity) {
                ((BaseGenericIdEntity) reloadedRef).setId(id);
            }
            persist(reloadedRef);
        }
        //noinspection unchecked
        return (T) reloadedRef;
    }

    protected <T extends Entity> T internalMerge(T entity) {
        try {
            CubaUtil.setSoftDeletion(false);
            CubaUtil.setOriginalSoftDeletion(false);

            UUID uuid = null;
            if (entity.getId() instanceof IdProxy) {
                uuid = ((IdProxy) entity.getId()).getUuid();
            }

            T merged = delegate.merge(entity);

            if (entity.getId() instanceof IdProxy && uuid != null
                    && !uuid.equals(((IdProxy) merged.getId()).getUuid())) {
                ((IdProxy) merged.getId()).setUuid(uuid);
            }

            // copy non-persistent attributes to the resulting merged instance
            for (MetaProperty property : metadata.getClassNN(entity.getClass()).getProperties()) {
                if (metadata.getTools().isNotPersistent(property) && !property.isReadOnly()) {
                    // copy using reflection to avoid executing getter/setter code
                    Field field = FieldUtils.getDeclaredField(entity.getClass(), property.getName(), true);
                    if (field != null) {
                        try {
                            Object value = FieldUtils.readField(field, entity);
                            if (value != null) {
                                FieldUtils.writeField(field, merged, value);
                            }
                        } catch (IllegalAccessException e) {
                            throw new RuntimeException(
                                    "Error copying non-persistent attribute value to merged instance", e);
                        }
                    }
                }
            }

            return merged;
        } finally {
            CubaUtil.setSoftDeletion(softDeletion);
            CubaUtil.setOriginalSoftDeletion(softDeletion);
        }
    }

    protected Object getRealId(Object id) {
        return id instanceof IdProxy ? ((IdProxy) id).getNN() : id;
    }
}