org.jasig.jpa.BaseJpaDao.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.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:
 *
 *   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.jasig.jpa;

import java.util.Set;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContext;
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.beans.factory.annotation.Autowired;
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;

import com.google.common.base.Function;

public abstract class BaseJpaDao implements InitializingBean, ApplicationContextAware {
    public static final String PERSISTENCE_UNIT_NAME = "BlackboardCollaborateDb";
    private static final String QUERY_SUFFIX = ".Query";

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

    private ApplicationContext applicationContext;
    private EntityManager entityManager;
    private TransactionOperations transactionOperations;

    @PersistenceContext(unitName = PERSISTENCE_UNIT_NAME)
    public final void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Autowired
    public final void setTransactionOperations(TransactionOperations transactionOperations) {
        this.transactionOperations = transactionOperations;
    }

    protected EntityManager getEntityManager() {
        return this.entityManager;
    }

    protected TransactionOperations getTransactionOperations() {
        return this.transactionOperations;
    }

    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) {
        final EntityManagerFactory entityManagerFactory = entityManager.getEntityManagerFactory();
        final CriteriaBuilder criteriaBuilder = entityManagerFactory.getCriteriaBuilder();

        return criteriaBuilder.parameter(paramClass);
    }

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

        return criteriaBuilder.parameter(paramClass, name);
    }

    protected final <T> CriteriaQuery<T> createCriteriaQuery(Function<CriteriaBuilder, CriteriaQuery<T>> builder) {
        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
        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.entityManager.createQuery(criteriaQuery);
    }

    /**
     * Common logic for creating and configuring JPA queries
     * 
     * @param criteriaQuery The criteria to create the query from
     */
    protected final <T> TypedQuery<T> createCachedQuery(CriteriaQuery<T> criteriaQuery) {
        final TypedQuery<T> query = this.entityManager.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 OpenEntityManager} or
     * {@link Transactional} so that the Hibernate specific extensions are available.
     */
    protected final <T> NaturalIdQuery<T> createNaturalIdQuery(Class<T> entityType) {
        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());
        }
    }
}