org.apereo.portal.jpa.BaseJpaDao.java Source code

Java tutorial

Introduction

Here is the source code for org.apereo.portal.jpa.BaseJpaDao.java

Source

/**
 * Licensed to Apereo under one or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information regarding copyright ownership. Apereo
 * licenses this file to you 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 the
 * following location:
 *
 * <p>http://www.apache.org/licenses/LICENSE-2.0
 *
 * <p>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.apereo.portal.jpa;

import com.google.common.base.Function;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import org.hibernate.LockOptions;
import org.hibernate.NaturalIdLoadAccess;
import org.hibernate.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionOperations;

/**
 * Base class for JPA DAOs in the portal that contains common functions.
 *
 * @author Eric Dalquist
 */
public abstract class BaseJpaDao implements InitializingBean, ApplicationContextAware {
    private static final String QUERY_SUFFIX = ".Query";

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    private ApplicationContext applicationContext;

    protected abstract EntityManager getEntityManager();

    protected abstract TransactionOperations getTransactionOperations();

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
    }

    protected final <T> ParameterExpression<T> createParameterExpression(Class<T> paramClass, String name) {
        final EntityManager entityManager = this.getEntityManager();
        final EntityManagerFactory entityManagerFactory = entityManager.getEntityManagerFactory();
        final CriteriaBuilder criteriaBuilder = entityManagerFactory.getCriteriaBuilder();

        return criteriaBuilder.parameter(paramClass, name);
    }

    /**
     * Factory method for creating a {@link CriteriaQuery} employing standards and best practices in
     * general use within the portal. Query objects returned from this method should normally be
     * passed to {@link createCachedQuery}; this step is important for the sake of scalability.
     */
    protected final <T> CriteriaQuery<T> createCriteriaQuery(Function<CriteriaBuilder, CriteriaQuery<T>> builder) {
        final EntityManager entityManager = this.getEntityManager();
        final EntityManagerFactory entityManagerFactory = entityManager.getEntityManagerFactory();
        final CriteriaBuilder criteriaBuilder = entityManagerFactory.getCriteriaBuilder();

        final CriteriaQuery<T> criteriaQuery = builder.apply(criteriaBuilder);

        //Do in TX so the EM gets closed correctly
        final TransactionOperations transactionOperations = this.getTransactionOperations();
        transactionOperations.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                entityManager.createQuery(criteriaQuery); //pre-compile critera query to avoid race conditions when setting aliases
            }
        });

        return criteriaQuery;
    }

    /**
     * Common logic for creating and configuring JPA queries
     *
     * @param criteriaQuery The criteria to create the query from
     */
    protected final <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) {
        return this.getEntityManager().createQuery(criteriaQuery);
    }

    /**
     * Important common logic for creating and configuring JPA queries cached in EhCache. This step
     * is important for the sake of scalability.
     *
     * @param criteriaQuery The criteria to create a cached query from
     */
    protected final <T> TypedQuery<T> createCachedQuery(CriteriaQuery<T> criteriaQuery) {
        final TypedQuery<T> query = this.getEntityManager().createQuery(criteriaQuery);
        final String cacheRegion = getCacheRegionName(criteriaQuery);
        query.setHint("org.hibernate.cacheable", true);
        query.setHint("org.hibernate.cacheRegion", cacheRegion);
        return query;
    }

    /**
     * Utility for creating queries based on naturalId. The caller MUST be annotated with {@link
     * org.apereo.portal.jpa.OpenEntityManager} or {@link Transactional} so that the Hibernate
     * specific extensions are available.
     */
    protected final <T> NaturalIdQuery<T> createNaturalIdQuery(Class<T> entityType) {
        final EntityManager entityManager = this.getEntityManager();
        final Session session;
        try {
            session = entityManager.unwrap(Session.class);
        } catch (IllegalStateException e) {
            throw new IllegalStateException(
                    "The DAO Method that calls createNaturalIdQuery must be annotated with @OpenEntityManager or @Transactional",
                    e);
        }
        final NaturalIdLoadAccess naturalIdLoadAccess = session.byNaturalId(entityType);
        return new NaturalIdQuery<T>(entityType, naturalIdLoadAccess);
    }

    /**
     * Creates the cache region name for the criteria query
     *
     * @param criteriaQuery The criteria to create the cache name for
     */
    protected final <T> String getCacheRegionName(CriteriaQuery<T> criteriaQuery) {
        final Set<Root<?>> roots = criteriaQuery.getRoots();
        final Class<?> cacheRegionType = roots.iterator().next().getJavaType();
        final String cacheRegion = cacheRegionType.getName() + QUERY_SUFFIX;

        if (roots.size() > 1) {
            logger.warn("Query " + criteriaQuery + " in " + this.getClass() + " has " + roots.size()
                    + " roots. The first will be used to generated the cache region name: " + cacheRegion);
        }
        return cacheRegion;
    }

    /**
     * Build a query for an entity using its naturalId
     *
     * @param <T> The entity type to return
     */
    public static final class NaturalIdQuery<T> {
        private final Class<T> type;
        private final NaturalIdLoadAccess naturalIdLoadAccess;

        public NaturalIdQuery(Class<T> type, NaturalIdLoadAccess naturalIdLoadAccess) {
            this.type = type;
            this.naturalIdLoadAccess = naturalIdLoadAccess;
        }

        /** @see NaturalIdLoadAccess#with(LockOptions) */
        public NaturalIdQuery<T> with(LockOptions lockOptions) {
            naturalIdLoadAccess.with(lockOptions);
            return this;
        }

        /**
         * Set a naturalId parameter using the JPA2 MetaModel API
         *
         * @see NaturalIdLoadAccess#using(String, Object)
         */
        public <P> NaturalIdQuery<T> using(Attribute<? super T, P> attribute, P value) {
            naturalIdLoadAccess.using(attribute.getName(), value);
            return this;
        }

        /** @see NaturalIdLoadAccess#getReference() */
        public T getReference() {
            return type.cast(naturalIdLoadAccess.getReference());
        }

        /** @see NaturalIdLoadAccess#load() */
        public T load() {
            return type.cast(naturalIdLoadAccess.load());
        }
    }
}