com.uni.dao.etc.UniJpaRepository.java Source code

Java tutorial

Introduction

Here is the source code for com.uni.dao.etc.UniJpaRepository.java

Source

package com.uni.dao.etc;

/*
 * Copyright 2008-2012 the original author or authors.
 *
 * 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.
 */

import static org.springframework.data.jpa.repository.query.QueryUtils.COUNT_QUERY_STRING;
import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING;
import static org.springframework.data.jpa.repository.query.QueryUtils.EXISTS_QUERY_STRING;
import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind;
import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString;
import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders;
import static org.springframework.data.jpa.repository.utils.JpaClassUtils.isEntityManagerOfType;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.hibernate.ejb.HibernateQuery;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.query.QueryExtractor;
import org.springframework.data.jpa.repository.query.QueryUtils;
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.LockMetadataProvider;
import org.springframework.util.Assert;

/**
 * Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
 * you a more sophisticated interface than the plain {@link EntityManager} .
 * 
 * @author Oliver Gierke
 * @author Eberhard Wolff
 * @param <T> the type of the entity to handle
 * @param <ID> the type of the entity's identifier
 */
public class UniJpaRepository<T, ID extends Serializable>
        implements JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

    private final JpaEntityInformation<T, ?> entityInformation;
    protected final EntityManager em;
    private final PersistenceProvider provider;

    private LockMetadataProvider lockMetadataProvider;

    /**
     * Creates a new {@link UniJpaRepository} to manage objects of the given {@link JpaEntityInformation}.
     * 
     * @param entityInformation must not be {@literal null}.
     * @param entityManager must not be {@literal null}.
     */
    public UniJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {

        Assert.notNull(entityInformation);
        Assert.notNull(entityManager);

        this.entityInformation = entityInformation;
        this.em = entityManager;
        this.provider = PersistenceProvider.fromEntityManager(entityManager);
    }

    /**
     * Creates a new {@link UniJpaRepository} to manage objects of the given domain type.
     * 
     * @param domainClass must not be {@literal null}.
     * @param em must not be {@literal null}.
     */
    public UniJpaRepository(Class<T> domainClass, EntityManager em) {
        this(JpaEntityInformationSupport.getMetadata(domainClass, em), em);
    }

    /**
     * Configures a custom {@link LockMetadataProvider} to be used to detect {@link LockModeType}s to be applied to
     * queries.
     * 
     * @param lockMetadataProvider
     */
    public void setLockMetadataProvider(LockMetadataProvider lockMetadataProvider) {
        this.lockMetadataProvider = lockMetadataProvider;
    }

    private Class<T> getDomainClass() {
        return entityInformation.getJavaType();
    }

    private String getDeleteAllQueryString() {
        return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());
    }

    private String getCountQueryString() {

        String countQuery = String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s");
        return getQueryString(countQuery, entityInformation.getEntityName());
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.io.Serializable)
     */
    public void delete(ID id) {

        Assert.notNull(id, "The given id must not be null!");

        if (!exists(id)) {
            throw new EmptyResultDataAccessException(
                    String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1);
        }

        delete(findOne(id));
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Object)
     */
    public void delete(T entity) {

        Assert.notNull(entity, "The entity must not be null!");
        em.remove(em.contains(entity) ? entity : em.merge(entity));
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable)
     */
    public void delete(Iterable<? extends T> entities) {

        Assert.notNull(entities, "The given Iterable of entities not be null!");

        for (T entity : entities) {
            delete(entity);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java.lang.Iterable)
     */
    public void deleteInBatch(Iterable<T> entities) {

        Assert.notNull(entities, "The given Iterable of entities not be null!");

        if (!entities.iterator().hasNext()) {
            return;
        }

        applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em)
                .executeUpdate();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.Repository#deleteAll()
     */
    public void deleteAll() {

        for (T element : findAll()) {
            delete(element);
        }
    }

    /* 
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch()
     */
    public void deleteAllInBatch() {
        em.createQuery(getDeleteAllQueryString()).executeUpdate();
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * org.springframework.data.repository.Repository#readById(java.io.Serializable
     * )
     */
    public T findOne(ID id) {

        Assert.notNull(id, "The given id must not be null!");
        return em.find(getDomainClass(), id);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#exists(java.io.Serializable)
     */
    public boolean exists(ID id) {

        Assert.notNull(id, "The given id must not be null!");

        if (entityInformation.getIdAttribute() != null) {

            String placeholder = provider.getCountQueryPlaceholder();
            String entityName = entityInformation.getEntityName();
            String idAttributeName = entityInformation.getIdAttribute().getName();
            String existsQuery = String.format(EXISTS_QUERY_STRING, placeholder, entityName, idAttributeName);

            TypedQuery<Long> query = em.createQuery(existsQuery, Long.class);
            query.setParameter("id", id);

            return query.getSingleResult() == 1;
        } else {
            return findOne(id) != null;
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#findAll()
     */
    public List<T> findAll() {
        return getQuery(null, (Sort) null).getResultList();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#findAll(ID[])
     */
    public List<T> findAll(Iterable<ID> ids) {

        return getQuery(new Specification<T>() {
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<?> path = root.get(entityInformation.getIdAttribute());
                return path.in(cb.parameter(Iterable.class, "ids"));
            }
        }, (Sort) null).setParameter("ids", ids).getResultList();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#findAll(org.springframework.data.domain.Sort)
     */
    public List<T> findAll(Sort sort) {
        return getQuery(null, sort).getResultList();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.PagingAndSortingRepository#findAll(org.springframework.data.domain.Pageable)
     */
    public Page<T> findAll(Pageable pageable) {

        if (null == pageable) {
            return new PageImpl<T>(findAll());
        }

        return findAll(null, pageable);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne(org.springframework.data.jpa.domain.Specification)
     */
    public T findOne(Specification<T> spec) {

        try {
            return getQuery(spec, (Sort) null).getSingleResult();
        } catch (NoResultException e) {
            return null;
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification)
     */
    public List<T> findAll(Specification<T> spec) {
        return getQuery(spec, (Sort) null).getResultList();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Pageable)
     */
    public Page<T> findAll(Specification<T> spec, Pageable pageable) {

        TypedQuery<T> query = getQuery(spec, pageable);
        return pageable == null ? new PageImpl<T>(query.getResultList()) : readPage(query, pageable, spec);
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Sort)
     */
    public List<T> findAll(Specification<T> spec, Sort sort) {

        return getQuery(spec, sort).getResultList();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#count()
     */
    public long count() {
        return em.createQuery(getCountQueryString(), Long.class).getSingleResult();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification)
     */
    public long count(Specification<T> spec) {

        return getCountQuery(spec).getSingleResult();
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.repository.CrudRepository#save(java.lang.Object)
     */
    public <S extends T> S save(S entity) {

        if (entityInformation.isNew(entity)) {
            em.persist(entity);
            return entity;
        } else {
            return em.merge(entity);
        }
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#saveAndFlush(java.lang.Object)
     */
    public T saveAndFlush(T entity) {

        T result = save(entity);
        flush();

        return result;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#save(java.lang.Iterable)
     */
    public <S extends T> List<S> save(Iterable<S> entities) {

        List<S> result = new ArrayList<S>();

        if (entities == null) {
            return result;
        }

        for (S entity : entities) {
            result.add(save(entity));
        }

        return result;
    }

    /*
     * (non-Javadoc)
     * @see org.springframework.data.jpa.repository.JpaRepository#flush()
     */
    public void flush() {

        em.flush();
    }

    public T findOneBy(String fieldName, String fieldValue) {
        String stringQuery = "SELECT u FROM " + getDomainClass().getCanonicalName() + " u " + "WHERE u." + fieldName
                + " = ?1";
        System.out.println(stringQuery);
        TypedQuery<T> query = em.createQuery(stringQuery, getDomainClass());
        query.setParameter(1, fieldValue);
        try {
            return query.getSingleResult();
        } catch (NoResultException e) {
            return null;
        } catch (NonUniqueResultException e) {
            return null;
        }
    }

    /**
     * Reads the given {@link TypedQuery} into a {@link Page} applying the given {@link Pageable} and
     * {@link Specification}.
     * 
     * @param query must not be {@literal null}.
     * @param spec can be {@literal null}.
     * @param pageable can be {@literal null}.
     * @return
     */
    private Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {

        query.setFirstResult(pageable.getOffset());
        query.setMaxResults(pageable.getPageSize());

        Long total = QueryUtils.executeCountQuery(getCountQuery(spec));
        List<T> content = total > pageable.getOffset() ? query.getResultList() : Collections.<T>emptyList();

        return new PageImpl<T>(content, pageable, total);
    }

    /**
     * Creates a new {@link TypedQuery} from the given {@link Specification}.
     * 
     * @param spec can be {@literal null}.
     * @param pageable can be {@literal null}.
     * @return
     */
    private TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) {

        Sort sort = pageable == null ? null : pageable.getSort();
        return getQuery(spec, sort);
    }

    /**
     * Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}.
     * 
     * @param spec can be {@literal null}.
     * @param sort can be {@literal null}.
     * @return
     */
    private TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {

        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<T> query = builder.createQuery(getDomainClass());

        Root<T> root = applySpecificationToCriteria(spec, query);
        query.select(root);

        if (sort != null) {
            query.orderBy(toOrders(sort, root, builder));
        }

        return applyLockMode(em.createQuery(query));
    }

    /**
     * Creates a new count query for the given {@link Specification}.
     * 
     * @param spec can be {@literal null}.
     * @return
     */
    private TypedQuery<Long> getCountQuery(Specification<T> spec) {

        CriteriaBuilder builder = em.getCriteriaBuilder();
        CriteriaQuery<Long> query = builder.createQuery(Long.class);

        Root<T> root = applySpecificationToCriteria(spec, query);
        query.select(builder.count(root));

        return em.createQuery(query);
    }

    /**
     * Applies the given {@link Specification} to the given {@link CriteriaQuery}.
     * 
     * @param spec can be {@literal null}.
     * @param query must not be {@literal null}.
     * @return
     */
    private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, CriteriaQuery<S> query) {

        Assert.notNull(query);
        Root<T> root = query.from(getDomainClass());

        if (spec == null) {
            return root;
        }

        CriteriaBuilder builder = em.getCriteriaBuilder();
        Predicate predicate = spec.toPredicate(root, query, builder);

        if (predicate != null) {
            query.where(predicate);
        }

        return root;
    }

    private TypedQuery<T> applyLockMode(TypedQuery<T> query) {

        LockModeType type = lockMetadataProvider == null ? null : lockMetadataProvider.getLockModeType();
        return type == null ? query : query.setLockMode(type);
    }

    /*
     * NESTED
     */

    public enum PersistenceProvider implements QueryExtractor {

        /**
         * Hibernate persistence provider.
         */
        HIBERNATE("org.hibernate.ejb.HibernateEntityManager") {

            public String extractQueryString(Query query) {

                return ((HibernateQuery) query).getHibernateQuery().getQueryString();
            }

            /**
             * Return custom placeholder ({@code *}) as Hibernate does create invalid queries for count queries for objects with
             * compound keys.
             * 
             * @see HHH-4044
             * @see HHH-3096
             */
            @Override
            protected String getCountQueryPlaceholder() {

                return "*";
            }
        },

        //      /**
        //       * EclipseLink persistence provider.
        //       */
        //      ECLIPSELINK("org.eclipse.persistence.jpa.JpaEntityManager") {
        //
        //         public String extractQueryString(Query query) {
        //
        //            return ((JpaQuery<?>) query).getDatabaseQuery().getJPQLString();
        //         }
        //
        //      },
        //
        //      /**
        //       * OpenJpa persistence provider.
        //       */
        //      OPEN_JPA("org.apache.openjpa.persistence.OpenJPAEntityManager") {
        //
        //         public String extractQueryString(Query query) {
        //
        //            return ((OpenJPAQuery<?>) query).getQueryString();
        //         }
        //      },

        /**
         * Unknown special provider. Use standard JPA.
         */
        GENERIC_JPA("javax.persistence.EntityManager") {

            public String extractQueryString(Query query) {

                return null;
            }

            @Override
            public boolean canExtractQuery() {

                return false;
            }
        };

        private String entityManagerClassName;

        /**
         * Creates a new {@link PersistenceProvider}.
         * 
         * @param entityManagerClassName the name of the provider specific {@link EntityManager} implementation
         */
        private PersistenceProvider(String entityManagerClassName) {

            this.entityManagerClassName = entityManagerClassName;
        }

        /**
         * Determines the {@link PersistenceProvider} from the given {@link EntityManager}. If no special one can be
         * determined {@value #GENERIC_JPA} will be returned.
         * 
         * @param em must not be {@literal null}.
         * @return will never be {@literal null}.
         */
        public static PersistenceProvider fromEntityManager(EntityManager em) {

            Assert.notNull(em);

            for (PersistenceProvider provider : values()) {
                if (isEntityManagerOfType(em, provider.entityManagerClassName)) {
                    return provider;
                }
            }

            return GENERIC_JPA;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * org.springframework.data.jpa.repository.query.QueryExtractor#canExtractQuery
         * ()
         */
        public boolean canExtractQuery() {

            return true;
        }

        /**
         * Returns the placeholder to be used for simple count queries. Default implementation returns {@code *}.
         * 
         * @return
         */
        protected String getCountQueryPlaceholder() {

            return "x";
        }
    }
}