org.tynamo.hibernate.services.HibernatePersistenceServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.tynamo.hibernate.services.HibernatePersistenceServiceImpl.java

Source

/*
 * 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 org.tynamo.hibernate.services;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.tapestry5.grid.GridDataSource;
import org.apache.tapestry5.hibernate.HibernateGridDataSource;
import org.apache.tapestry5.hibernate.HibernateSessionManager;
import org.apache.tapestry5.ioc.services.PropertyAccess;
import org.apache.tapestry5.services.PropertyConduitSource;
import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.TransientObjectException;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.tynamo.descriptor.CollectionDescriptor;
import org.tynamo.descriptor.TynamoClassDescriptor;
import org.tynamo.descriptor.TynamoPropertyDescriptor;
import org.tynamo.hibernate.QueryParameter;
import org.tynamo.internal.services.GenericPersistenceService;
import org.tynamo.services.DescriptorService;

@SuppressWarnings("unchecked")
public class HibernatePersistenceServiceImpl extends GenericPersistenceService
        implements HibernatePersistenceService {

    private Logger logger;
    private DescriptorService descriptorService;
    private HibernateSessionManager sessionManager;
    private PropertyConduitSource propertyConduitSource;

    public HibernatePersistenceServiceImpl(Logger logger, DescriptorService descriptorService,
            HibernateSessionManager sessionManager, PropertyAccess propertyAccess,
            PropertyConduitSource propertyConduitSource) {
        super(propertyAccess);
        this.logger = logger;
        this.descriptorService = descriptorService;
        this.propertyConduitSource = propertyConduitSource;

        // we need a sessionmanager because Tapestry session proxy doesn't implement Hibernate's SessionImplementator interface
        this.sessionManager = sessionManager;
    }

    private Session getSession() {
        return sessionManager.getSession();
    }

    /**
     * https://trails.dev.java.net/servlets/ReadMsg?listName=users&msgNo=1226
     * <p/>
     * Very often I find myself writing:
     * <code>
     * Object example = new Object(); example.setProperty(uniqueValue);
     * List objects = ((TynamoPage)getPage()).getPersistenceService().getInstances(example);
     * (MyObject)objects.get(0);
     * </code>
     * when, in fact, I know that the single property I populated my example object with should be unique, and thus only
     * one object should be returned
     *
     * @param type          The type to use to check for security restrictions.
     * @param detachedCriteria
     * @return
     */
    public <T> T getInstance(final Class<T> type, DetachedCriteria detachedCriteria) {
        final DetachedCriteria criteria = alterCriteria(type, detachedCriteria);
        return (T) criteria.getExecutableCriteria(getSession()).uniqueResult();
    }

    /**
     * (non-Javadoc)
     *
     * @see org.tynamo.services.PersistenceService#getInstance(Class,Serializable)
     */

    public <T> T getInstance(final Class<T> type, final Serializable id) {
        DetachedCriteria criteria = DetachedCriteria.forClass(type).add(Restrictions.idEq(id));
        return getInstance(type, criteria);
    }

    /**
     * Execute an HQL query.
     *
     * @param queryString a query expressed in Hibernate's query language
     * @return a List of entities containing the results of the query execution
     */
    public List findByQuery(String queryString) {
        return findByQuery(queryString, new QueryParameter[0]);
    }

    /**
     * Execute an HQL query.
     *
     * @param queryString a query expressed in Hibernate's query language
     * @param parameters  the (optional) parameters for the query.
     * @return a List of entities containing the results of the query execution
     */
    public List findByQuery(String queryString, QueryParameter... parameters) {
        return findByQuery(queryString, 0, 0, parameters);
    }

    /**
     * Execute an HQL query.
     *
     * @param queryString a query expressed in Hibernate's query language
     * @param startIndex  the index of the first item to be retrieved
     * @param maxResults  the number of items to be retrieved, if <code>0</code> it retrieves ALL the items
     * @param parameters  the (optional) parameters for the query.
     * @return a List of entities containing the results of the query execution
     */
    public List findByQuery(String queryString, int startIndex, int maxResults, QueryParameter... parameters) {
        Query query = getSession().createQuery(queryString);
        for (QueryParameter parameter : parameters) {
            parameter.applyNamedParameterToQuery(query);
        }

        if (maxResults > 0)
            query.setMaxResults(maxResults);

        if (startIndex > 0)
            query.setFirstResult(startIndex);

        if (logger.isDebugEnabled())
            logger.debug(query.getQueryString());

        return query.list();
    }

    /**
     * (non-Javadoc)
     *
     * @see org.tynamo.services.PersistenceService#getInstances(java.lang.Class)
     */

    public <T> List<T> getInstances(final Class<T> type) {
        return getSession().createCriteria(type).list();
    }

    public <T> List<T> getInstances(final Class<T> type, int startIndex, int maxResults) {
        return getInstances(type, DetachedCriteria.forClass(type), startIndex, maxResults);
    }

    /**
     * (non-Javadoc)
     *
     * @see org.tynamo.services.PersistenceService#save(java.lang.Object)
     */
    public <T> T save(T instance) {
        TynamoClassDescriptor tynamoclassdescriptor = descriptorService.getClassDescriptor(instance.getClass());
        // @todo: org.hibernate.MappingException: Unknown entity

        /* check isTransient to avoid merging on entities not persisted yet. TRAILS-33 */
        if (!tynamoclassdescriptor.getHasCyclicRelationships() || isTransient(instance, tynamoclassdescriptor)) {
            getSession().saveOrUpdate(instance);
        } else {
            instance = (T) getSession().merge(instance);
        }
        return instance;
    }

    public void removeAll(Collection collection) {
        //      getSession().deleteAll(collection);
    }

    public void remove(Object instance) {
        getSession().delete(instance);
    }

    public <T> List<T> getInstances(Class<T> type, DetachedCriteria criteria) {
        criteria = alterCriteria(type, criteria);
        criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        return criteria.getExecutableCriteria(getSession()).list();
    }

    public void reattach(Object model) {
        getSession().lock(model, LockMode.NONE);
    }

    public Serializable getIdentifier(final Object data, final TynamoClassDescriptor classDescriptor) {
        return (Serializable) getPropertyAccess().get(data, classDescriptor.getIdentifierDescriptor().getName());
    }

    public Serializable getIdentifier(final Object data) {
        return getIdentifier(data, descriptorService.getClassDescriptor(data.getClass()));
    }

    public boolean isTransient(Object data, TynamoClassDescriptor classDescriptor) {
        try {
            return getIdentifier(data, classDescriptor) == null;
        } catch (TransientObjectException e) {
            return true;
        }
    }

    public List getInstances(final Object example, final TynamoClassDescriptor classDescriptor) {
        //create Criteria instance
        DetachedCriteria searchCriteria = DetachedCriteria.forClass(example.getClass());
        searchCriteria = alterCriteria(example.getClass(), searchCriteria);

        //loop over the example object's PropertyDescriptors
        for (TynamoPropertyDescriptor propertyDescriptor : classDescriptor.getPropertyDescriptors()) {
            //only add a Criterion to the Criteria instance if this property is searchable
            if (propertyDescriptor.isSearchable()) {
                String propertyName = propertyDescriptor.getName();
                Class propertyClass = propertyDescriptor.getPropertyType();
                Object value = getPropertyAccess().get(example, propertyName);

                //only add a Criterion to the Criteria instance if the value for this property is non-null
                if (value != null) {
                    if (String.class.isAssignableFrom(propertyClass) && ((String) value).length() > 0) {
                        searchCriteria.add(Restrictions.like(propertyName, value.toString(), MatchMode.ANYWHERE));
                    }
                    /**
                     * 'one'-end of many-to-one, one-to-one
                     *
                     * Just match the identifier
                     */
                    else if (propertyDescriptor.isObjectReference()) {
                        Serializable identifierValue = getIdentifier(value,
                                descriptorService.getClassDescriptor(propertyDescriptor.getBeanType()));
                        searchCriteria.createCriteria(propertyName).add(Restrictions.idEq(identifierValue));
                    } else if (propertyClass.isPrimitive()) {
                        //primitive types: ignore zeroes in case of numeric types, ignore booleans anyway (TODO come up with something...)
                        if (!propertyClass.equals(boolean.class) && ((Number) value).longValue() != 0) {
                            searchCriteria.add(Restrictions.eq(propertyName, value));
                        }
                    } else if (propertyDescriptor.isCollection()) {
                        //one-to-many or many-to-many
                        CollectionDescriptor collectionDescriptor = (CollectionDescriptor) propertyDescriptor;
                        TynamoClassDescriptor collectionClassDescriptor = descriptorService
                                .getClassDescriptor(collectionDescriptor.getElementType());
                        if (collectionClassDescriptor != null) {
                            String identifierName = collectionClassDescriptor.getIdentifierDescriptor().getName();
                            Collection<Serializable> identifierValues = new ArrayList<Serializable>();
                            Collection associatedItems = (Collection) value;
                            if (associatedItems != null && associatedItems.size() > 0) {
                                for (Object o : associatedItems) {
                                    identifierValues.add(getIdentifier(o, collectionClassDescriptor));
                                }
                                //add a 'value IN collection' restriction
                                searchCriteria.createCriteria(propertyName)
                                        .add(Restrictions.in(identifierName, identifierValues));
                            }
                        }
                    }
                }
            }
        }
        searchCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        // FIXME This won't work because the shadow proxy doesn't implement SessionImplementor
        // that session is casted to. Maybe we should inject SessionManager instead
        // and obtain the Session from it
        return searchCriteria.getExecutableCriteria(getSession()).list();
    }

    public int count(Class type) {
        return count(type, DetachedCriteria.forClass(type));
    }

    public int count(Class type, DetachedCriteria detachedCriteria) {
        detachedCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        final DetachedCriteria criteria = alterCriteria(type, detachedCriteria);
        Criteria executableCriteria = criteria.getExecutableCriteria(getSession())
                .setProjection(Projections.rowCount());
        return ((Long) executableCriteria.uniqueResult()).intValue();
    }

    public <T> List<T> getInstances(Class<T> type, final DetachedCriteria detachedCriteria, final int startIndex,
            final int maxResults) {
        return getInstances(alterCriteria(type, detachedCriteria), startIndex, maxResults);
    }

    public List getInstances(final DetachedCriteria detachedCriteria, final int startIndex, final int maxResults) {
        detachedCriteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);
        Criteria executableCriteria = detachedCriteria.getExecutableCriteria(getSession());
        if (startIndex >= 0) {
            executableCriteria.setFirstResult(startIndex);
        }
        if (maxResults > 0) {
            executableCriteria.setMaxResults(maxResults);
        }
        return executableCriteria.list();
    }

    /**
     * This hook allows subclasses to modify the query criteria, such as for security
     *
     * @param detachedCriteria The original Criteria query
     * @return The modified Criteria query for execution
     */
    protected DetachedCriteria alterCriteria(Class type, DetachedCriteria detachedCriteria) {
        return detachedCriteria;
    }

    /**
     * @see org.tynamo.hibernate.services.HibernatePersistenceService#saveOrUpdate(java.lang.Object)
     */

    public <T> T merge(T instance) {
        return (T) getSession().merge(instance);
    }

    /**
     * @see org.tynamo.hibernate.services.HibernatePersistenceService#saveOrUpdate(java.lang.Object)
     */
    public <T> T saveOrUpdate(T instance) {
        getSession().saveOrUpdate(instance);
        return instance;
    }

    public <T> T addToCollection(CollectionDescriptor descriptor, T element, Object collectionOwner) {
        Class elementType = descriptor.getElementType();
        String addMethod = descriptor.getAddExpression() != null ? descriptor.getAddExpression()
                : "add" + elementType.getSimpleName();

        try {
            Method method = descriptor.getBeanType().getMethod(addMethod, new Class[] { elementType });
            method.invoke(collectionOwner, element);
            return element;

        } catch (NoSuchMethodException e) {
            // do nothing;
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }

        Collection collection = (Collection) getPropertyAccess().get(collectionOwner, descriptor.getName());
        if (!(descriptor.isChildRelationship() && (collection instanceof List) && (collection.contains(element)))) {
            collection.add(element);
        }
        return element;

    }

    public List getOrphanInstances(CollectionDescriptor descriptor, Object owner) {
        if (descriptor.getInverseProperty() != null && descriptor.isOneToMany()) {
            Criteria criteria = getSession().createCriteria(descriptor.getElementType());
            TynamoClassDescriptor elementDescriptor = descriptorService
                    .getClassDescriptor(descriptor.getBeanType());
            String idProperty = elementDescriptor.getIdentifierDescriptor().getName();
            if (owner != null) {
                criteria.add(Restrictions.disjunction().add(Restrictions.isNull(descriptor.getInverseProperty()))
                        .add(Restrictions.eq(descriptor.getInverseProperty() + "." + idProperty,
                                getIdentifier(owner, elementDescriptor))));
            } else {
                criteria.add(Restrictions.isNull(descriptor.getInverseProperty()));
            }
            return criteria.list();
        }
        return getInstances(descriptor.getElementType());
    }

    @Override
    public <T> GridDataSource getGridDataSource(Class<T> type) {
        return new HibernateGridDataSource(getSession(), type);
    }
}