io.pelle.mango.db.dao.BaseEntityDAO.java Source code

Java tutorial

Introduction

Here is the source code for io.pelle.mango.db.dao.BaseEntityDAO.java

Source

/**
 * Copyright (c) 2013 Christian Pelster.
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Christian Pelster - initial API and implementation
 */
package io.pelle.mango.db.dao;

import static io.pelle.mango.client.base.vo.query.SelectQuery.selectFrom;

import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.base.Joiner;
import com.google.common.base.Optional;

import io.pelle.mango.client.base.db.vos.IInfoVOEntity;
import io.pelle.mango.client.base.vo.IBaseEntity;
import io.pelle.mango.client.base.vo.query.CountQuery;
import io.pelle.mango.client.base.vo.query.DeleteQuery;
import io.pelle.mango.client.base.vo.query.SelectQuery;
import io.pelle.mango.client.base.vo.query.expressions.ExpressionFactory;
import io.pelle.mango.db.IUser;
import io.pelle.mango.db.copy.ObjectFieldDescriptor;
import io.pelle.mango.db.copy.ObjectFieldIterator;
import io.pelle.mango.db.query.ServerCountQuery;
import io.pelle.mango.db.util.DBUtil;
import io.pelle.mango.db.util.EntityVOMapper;
import io.pelle.mango.db.voquery.EntityClassQuery;
import io.pelle.mango.server.base.IBaseClientEntity;

@Component
public class BaseEntityDAO extends BaseDAO<IBaseEntity> {

    private static Logger LOG = Logger.getLogger(BaseEntityDAO.class);

    @Autowired(required = false)
    private List<IEntityCallback> entityCallbacks = new ArrayList<IEntityCallback>();

    @Override
    public <T extends IBaseEntity> T create(T entity) {

        beforeCreate(entity);

        if (entity instanceof IBaseClientEntity) {

            populateClients(((IBaseClientEntity) entity), new ArrayList<IBaseClientEntity>());

            LOG.debug(String.format("creating entity '%s' for client '%s'", entity.getClass().getName(),
                    getCurrentUser().getClient()));
        } else {
            LOG.debug(String.format("creating entity '%s'", entity.getClass().getName()));
        }

        if (entity instanceof IInfoVOEntity) {

            IInfoVOEntity infoEntity = (IInfoVOEntity) entity;

            if (SecurityContextHolder.getContext().getAuthentication() != null
                    && SecurityContextHolder.getContext().getAuthentication().getPrincipal() != null) {
                infoEntity.setUpdateUser(
                        SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
                infoEntity.setCreateUser(
                        SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString());
            }

            Date now = new Date();
            infoEntity.setCreateDate(now);
            infoEntity.setUpdateDate(now);
        }

        T result = mergeRecursive(entity);

        return result;

    }

    protected void beforeCreate(IBaseEntity entity) {
        for (IEntityCallback entityCallback : this.entityCallbacks) {
            if (entityCallback.supports(entity.getClass())) {
                entityCallback.beforeCreate(entity);
            }
        }
    }

    protected void beforeSave(IBaseEntity entity) {
        for (IEntityCallback entityCallback : this.entityCallbacks) {
            if (entityCallback.supports(entity.getClass())) {
                entityCallback.beforeSave(entity);
            }
        }
    }

    @Override
    public <T extends IBaseEntity> void delete(T entity) {
        LOG.debug(String.format("deleting entity '%s' with id '%d'", entity.getClass().getName(), entity.getId()));

        // TODO warn if entities with embedded element collections are deleted

        DeleteQuery query = DeleteQuery.deleteFrom(entity.getClass()).where(ExpressionFactory
                .createLongExpression(entity.getClass(), IBaseEntity.ID_FIELD_NAME, entity.getId()));
        deleteQuery(query);
    }

    private void deleteTables(List<String> tableNames) {

        for (String tableName : tableNames) {
            String nativeDeleteQuery = String.format("delete from %s", tableName);
            entityManager.createNativeQuery(nativeDeleteQuery).executeUpdate();
        }
    }

    @Override
    public <T extends IBaseEntity> void deleteAll(Class<T> entityClass) {

        if (LOG.isDebugEnabled()) {
            LOG.debug(String.format("deleting everything for entity '%s'", entityClass.getName()));
        }

        List<String> elementCollections = EntityClassQuery.createQuery(entityClass).getElementCollections();
        if (LOG.isDebugEnabled() && !elementCollections.isEmpty()) {
            LOG.debug(String.format("deleting element collections '%s' for entity '%s'",
                    Joiner.on(", ").join(elementCollections), entityClass.getName()));
            deleteTables(elementCollections);
        }

        List<String> oneToManyJoinTables = EntityClassQuery.createQuery(entityClass).getOneToManyJoinTables();
        if (LOG.isDebugEnabled() && !oneToManyJoinTables.isEmpty()) {
            LOG.debug(String.format("deleting one to many join tables '%s' for entity '%s'",
                    Joiner.on(", ").join(oneToManyJoinTables), entityClass.getName()));
            deleteTables(oneToManyJoinTables);
        }

        DeleteQuery query = DeleteQuery.deleteFrom(entityClass);
        deleteQuery(query);
    }

    @SuppressWarnings("unchecked")
    public <T extends IBaseEntity> List<T> getAll(Class<T> entityClass) {
        return (List<T>) getResultListInternal(selectFrom(entityClass), entityManager);
    }

    private IUser getCurrentUser() {
        try {
            return (IUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();

        } catch (Exception e) {
            return null;
        }
    }

    @SuppressWarnings("unchecked")
    private void populateClients(IBaseClientEntity clientEntity, List<IBaseClientEntity> visited) {
        try {
            clientEntity.setClient(getCurrentUser().getClient());
            visited.add(clientEntity);

            for (Map.Entry<String, Object> entry : ((Map<String, Object>) PropertyUtils.describe(clientEntity))
                    .entrySet()) {

                String propertyName = entry.getKey();
                PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(clientEntity,
                        propertyName);

                if (propertyDescriptor != null) {
                    Object attribute = PropertyUtils.getSimpleProperty(clientEntity, propertyName);

                    if (attribute != null && IBaseClientEntity.class.isAssignableFrom(attribute.getClass())) {
                        if (!visited.contains(attribute)) {
                            populateClients((IBaseClientEntity) attribute, visited);
                        }
                    }

                    if (attribute != null && List.class.isAssignableFrom(attribute.getClass())) {
                        List<?> list = (List<?>) attribute;

                        for (Object listElement : list) {
                            if (IBaseClientEntity.class.isAssignableFrom(listElement.getClass())) {
                                if (!visited.contains(attribute)) {
                                    populateClients((IBaseClientEntity) listElement, visited);
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("error setting client", e);
        }
    }

    @Override
    public <T extends IBaseEntity> T read(long id, Class<T> entityClass) {

        T entity = this.entityManager.find(entityClass, id);

        if (entity instanceof IBaseClientEntity) {

            IUser user = getCurrentUser();

            LOG.debug(String.format("retrieving entity '%s' with id %d for client '%s'", entityClass.getName(), id,
                    getCurrentUser().getClient()));

            if (((IBaseClientEntity) entity).getClient().getId() == user.getClient().getId()) {
                return entity;
            } else {
                throw new AccessDeniedException(
                        String.format("entity '%s' not allowed for client '%s'", entity, user.getClient()));
            }
        } else {
            LOG.debug(String.format("retrieving entity '%s' with id %d", entityClass.getName(), id));

            return entity;
        }

    }

    @Override
    public <T extends IBaseEntity> T save(T entity) {

        beforeSave(entity);

        if (entity instanceof IBaseClientEntity) {
            ((IBaseClientEntity) entity).setClient(getCurrentUser().getClient());

            LOG.debug(
                    String.format("saving '%s' for client '%s'", entity.getClass(), getCurrentUser().getClient()));
        } else {
            // throw new AccessDeniedException(String.format(
            // "entity '%s' not allowed for client '%s'", entity,
            // getCurrentUser().getClient()));
        }

        if (entity instanceof IInfoVOEntity) {

            IInfoVOEntity infoEntity = (IInfoVOEntity) entity;

            if (SecurityContextHolder.getContext().getAuthentication() != null) {

                Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

                if (principal != null) {
                    infoEntity.setUpdateUser(principal.toString());
                }
            }

            infoEntity.setUpdateDate(new Date());
        }

        T result = mergeRecursive(entity);

        return result;
    }

    private <T extends IBaseEntity> T mergeRecursive(T entity) {
        return mergeRecursive(entity, "/");
    }

    @SuppressWarnings("unchecked")
    private <T extends IBaseEntity> T mergeRecursive(T entity, String currentPath) {

        // TODO use dirty paths?
        for (ObjectFieldDescriptor fieldDescriptor : new ObjectFieldIterator(entity)) {

            Object sourceValue = fieldDescriptor.getSourceValue(entity);
            if (sourceValue instanceof IBaseEntity) {

                checkLoaded(entity, fieldDescriptor);

                IBaseEntity entityAttribute = (IBaseEntity) sourceValue;
                IBaseEntity mergedEntityAttribute = mergeRecursive(entityAttribute, currentPath);

                try {
                    fieldDescriptor.getSourceWriteMethod().invoke(entity, mergedEntityAttribute);
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

            } else if (sourceValue instanceof List) {

                checkLoaded(entity, fieldDescriptor);

                List<Object> sourceList = (List<Object>) sourceValue;

                for (int i = 0; i < sourceList.size(); i++) {

                    Object listItem = sourceList.get(i);

                    if (listItem instanceof IBaseEntity) {
                        IBaseEntity baseEntityListItem = (IBaseEntity) listItem;
                        IBaseEntity mergedEntityListItem = mergeRecursive(baseEntityListItem, currentPath);
                        sourceList.set(i, mergedEntityListItem);
                    } else {
                        sourceList.set(i, listItem);
                    }
                }

                continue;
            }
        }

        return this.entityManager.merge(entity);

    }

    private <T extends IBaseEntity> void checkLoaded(T entity, ObjectFieldDescriptor fieldDescriptor) {
        if (!entity.isNew() && !entity.getMetadata().isLoaded(fieldDescriptor.getFieldName())
                && entity.getMetadata().hasChanges()) {
            throw new RuntimeException(String.format("entity '%s' is dirty but has unloaded attribute '%s'",
                    entity.getClass(), fieldDescriptor.getFieldName()));
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T extends IBaseEntity> List<T> filter(SelectQuery<T> selectQuery) {
        return (List<T>) getResultListInternal(selectQuery, entityManager);
    }

    @Override
    public <T extends IBaseEntity> Optional<T> read(SelectQuery<T> selectQuery) {
        List<T> result = filter(selectQuery);

        if (result.size() == 1) {
            return Optional.of(result.get(0));
        } else {
            return Optional.<T>absent();
        }
    }

    @Override
    public <T extends IBaseEntity> long count(CountQuery<T> countQuery) {
        return (long) entityManager
                .createQuery(ServerCountQuery.adapt(countQuery).getJPQL(EntityVOMapper.getInstance()))
                .getSingleResult();
    }

    @Override
    public <T extends IBaseEntity> long count(Class<T> entityClass) {
        return count(CountQuery.countFrom(entityClass));
    }

    @Override
    public <T extends IBaseEntity> void delete(Class<T> entityClass, long id) {
        T entity = read(id, entityClass);
        delete(entity);
    }

    @Override
    public <T extends IBaseEntity> List<T> searchByNaturalKey(Class<T> entityClass, String naturalKey) {
        List<T> result = filter(DBUtil.getNaturalKeyQuery(entityClass, naturalKey));
        return result;
    }

    @Override
    public <T extends IBaseEntity> T getNewVO(String className, Map<String, String> properties) {

        Class<? extends IBaseEntity> entityClass = EntityVOMapper.getInstance().getEntityClass(className);

        try {
            T entity = (T) ConstructorUtils.invokeConstructor(entityClass, new Object[0]);

            return entity;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}