All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.springframework.data.jpa.repository.support.GenericJpaRepositoryImpl Maven / Gradle / Ivy

package org.springframework.data.jpa.repository.support;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.persistence.EntityManager;
import javax.persistence.EntityNotFoundException;
import javax.persistence.Query;
import javax.persistence.Search;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Persistable;
import org.springframework.data.domain.Sort;
import org.springframework.data.envers.repository.support.DefaultRevisionMetadata;
import org.springframework.data.history.AnnotationRevisionMetadata;
import org.springframework.data.history.Revision;
import org.springframework.data.history.RevisionMetadata;
import org.springframework.data.history.RevisionSort;
import org.springframework.data.history.Revisions;
import org.springframework.data.history.SimpleRevisionEntityInformation;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.domain.Specifications;
import org.springframework.data.jpa.repository.GenericJpaRepository;
import org.springframework.data.jpa.support.DataTablesInput;
import org.springframework.data.jpa.support.DataTablesOutput;
import org.springframework.data.jpa.support.DataTablesUtils;
import org.springframework.data.querydsl.EntityPathResolver;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.util.Assert;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.core.Join;
import com.querydsl.core.dml.DMLClause;
import com.querydsl.core.types.CollectionExpression;
import com.querydsl.core.types.EntityPath;
import com.querydsl.core.types.Expression;
import com.querydsl.core.types.MapExpression;
import com.querydsl.core.types.Path;
import com.querydsl.core.types.Predicate;
import com.querydsl.core.types.dsl.PathBuilder;
import com.querydsl.jpa.JPQLQuery;

/**
 * 
 * findTop1ByNameAllIgnoringCase
 * findAllByEnabledTrueAndCodeGroupEnabledTrueAndCodeGroupIdOrderBySequenceAsc
 * findAllByEnabledTrueOrderByNameAsc
 * findOneByClientIdIgnoreCase
 * findAllByEnabledTrueAndCodeGroupEnabledTrueAndCodeGroupIdIgnoreCaseOrderBySequenceAsc
 * 
 * ID extends java.io.Serializable & Comparable
 * TRUNCATE
 * org.hibernate.envers.revision_class
 * 
 * 
 * @see org.springframework.data.repository.RepositoryDefinition#domainClass()
 * @see org.springframework.data.repository.RepositoryDefinition#idClass()
 * @see org.springframework.data.jpa.repository.support.SimpleJpaRepository
 * @see org.springframework.data.jpa.repository.support.QueryDslJpaRepository#QueryDslJpaRepository(JpaEntityInformation,
 * EntityManager)
 * @see org.springframework.data.jpa.repository.support.QueryDslJpaRepository#QueryDslJpaRepository(JpaEntityInformation,
 * EntityManager, EntityPathResolver)
 * @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation#JpaMetamodelEntityInformation(Class,
 * javax.persistence.metamodel.Metamodel)
 * @see javax.persistence.EntityManager#getMetamodel()
 * @see org.springframework.data.jpa.repository.support.QueryDslRepositorySupport
 * @see com.mysema.query.jpa.impl.JPADeleteClause#JPADeleteClause(EntityManager,
 * EntityPath)
 * @see com.mysema.query.jpa.impl.JPAUpdateClause#JPAUpdateClause(EntityManager,
 * EntityPath)
 * @see org.springframework.data.envers.repository.support.EnversRevisionRepositoryImpl
 * @see org.springframework.data.domain.ExampleMatcher#matching()
 * @see org.springframework.data.domain.ExampleMatcher#withMatcher(String,
 * org.springframework.data.domain.ExampleMatcher.GenericPropertyMatcher)
 * @see org.springframework.data.domain.Example#of(Object,
 * org.springframework.data.domain.ExampleMatcher)
 * @see org.springframework.orm.jpa.JpaObjectRetrievalFailureException
 * @see org.hibernate.cfg.AvailableSettings#STATEMENT_BATCH_SIZE
 * @see org.hibernate.envers.configuration.EnversSettings#REVISION_LISTENER
 */
public class GenericJpaRepositoryImpl, ID extends Serializable> extends QueryDslJpaRepository implements GenericJpaRepository {
  protected final Log logger = LogFactory.getLog(getClass());
  private final EntityManager entityManager;
  private final EntityPath path;
  private final PathBuilder builder;
  private final Querydsl querydsl;
  private final Integer size;
  private final Class revisionEntityClass;

  public GenericJpaRepositoryImpl(JpaEntityInformation entityInformation, EntityManager entityManager) {
    super(entityInformation, entityManager);
    this.entityManager = entityManager;
    this.path = SimpleEntityPathResolver.INSTANCE.createPath(entityInformation.getJavaType());
    this.builder = new PathBuilder(this.path.getType(), this.path.getMetadata());
    this.querydsl = new Querydsl(entityManager, this.builder);
    Map properties = entityManager.getEntityManagerFactory().getProperties();
    this.revisionEntityClass = SimpleRevisionEntityInformation.INSTANCE.createRevisionEntityClass(String.valueOf(properties.getOrDefault("org.hibernate.envers.revision_class", "")),
        entityManager.getClass().getClassLoader());
    this.size = (Integer) entityManager.getEntityManagerFactory().getProperties().get("hibernate.jdbc.batch_size");
  }

  /**
   * 
  public Optional findById(ID id) throws EntityNotFoundException {
    Optional result = super.findById(id);
    return Optional.ofNullable(result.orElseThrow(() -> new EntityNotFoundException("Unable to find " + getDomainClass() + " with id " + id)));
  }
  
  return findById(id).orElseThrow(() -> new EntityNotFoundException("Unable to find " + getDomainClass() + " with id " + id));
   * 
   */
  @Override
  public T findOne(ID id) throws EntityNotFoundException {
    T result = super.findOne(id);
    if (result == null) {
      throw new EntityNotFoundException("Unable to find " + getDomainClass() + " with id " + id);
    }
    return result;
  }

  @Override
  public boolean exists(Predicate predicate, Join... joins) {
    return createJoinQuery(predicate, joins).fetchCount() > 0;
  }

  @Override
  public long count(Predicate predicate, Join... joins) {
    return createJoinQuery(predicate, joins).fetchCount();
  }

  /**
   * 
  super.findById(id).get()
   * 
   */
  @Override
  public T getOne(ID id) {
    return super.findOne(id);
  }

  @Override
  public T findOne(Predicate predicate, Join... joins) {
    return createJoinQuery(predicate, joins).select(this.path).fetchOne();
  }

  @Override
  public  RT findOne(Expression expression, Predicate predicate, Join... joins) {
    return createJoinQuery(predicate, joins).select(expression).fetchOne();
  }

  @Override
  public  List findAll(Expression expression, Predicate predicate, Sort sort, Join... joins) {
    return this.querydsl.applySorting(sort, createJoinQuery(predicate, joins).select(expression)).fetch();
  }

  @Override
  public  Page findAll(Expression expression, Predicate predicate, Pageable pageable, Join... joins) {
    JPQLQuery countQuery = createJoinQuery(predicate, joins);
    JPQLQuery query = this.querydsl.applyPagination(pageable, createJoinQuery(predicate, joins).select(expression));

    long total = countQuery.fetchCount();
    List content = pageable == null || total > pageable.getOffset() ? query.fetch() : Collections.emptyList();
    return new PageImpl(content, pageable, total);
  }

  @Override
  public List findAll(Predicate predicate, Sort sort, Join... joins) {
    return this.querydsl.applySorting(sort, createJoinQuery(predicate, joins).select(this.path)).fetch();
  }

  @Override
  public Page findAll(Predicate predicate, Pageable pageable, Join... joins) {
    JPQLQuery countQuery = createJoinQuery(predicate, joins);
    JPQLQuery query = this.querydsl.applyPagination(pageable, createJoinQuery(predicate, joins).select(this.path));

    long total = countQuery.fetchCount();
    List content = pageable == null || total > pageable.getOffset() ? query.fetch() : Collections.emptyList();
    return new PageImpl(content, pageable, total);
  }

  @Override
  public List findAll(Sort sort, JPQLQuery query) {
    return this.querydsl.applySorting(sort, query.select(this.path)).fetch();
  }

  @Override
  public Page findAll(Pageable pageable, JPQLQuery query) {
    JPQLQuery countQuery = query;

    long total = countQuery.fetchCount();
    List content = pageable == null || total > pageable.getOffset() ? this.querydsl.applyPagination(pageable, query.select(this.path)).fetch() : Collections.emptyList();
    return new PageImpl(content, pageable, total);
  }

  @Override
  public  List findAll(Expression expression, Sort sort, JPQLQuery query) {
    return this.querydsl.applySorting(sort, query.select(expression)).fetch();
  }

  @Override
  public  Page findAll(Expression expression, Pageable pageable, JPQLQuery query) {
    JPQLQuery countQuery = query;

    long total = countQuery.fetchCount();
    List content = pageable == null || total > pageable.getOffset() ? this.querydsl.applyPagination(pageable, query.select(expression)).fetch() : Collections.emptyList();
    return new PageImpl(content, pageable, total);
  }

  @Override
  public long execute(DMLClause clause) {
    return clause.execute();
  }

  @SuppressWarnings({ "unchecked", "rawtypes" })
  private JPQLQuery createJoinQuery(Predicate predicate, Join... joins) {
    JPQLQuery query = super.createQuery(predicate);
    for (Join join : joins) {
      Expression target = join.getTarget();
      Predicate[] predicates = join.getPredicates();
      Path alias = join.getAlias();
      boolean fetchJoin = join.isFetchJoin();
      switch (join.getJoinType()) {
      case INNERJOIN:
        if (target instanceof EntityPath) {
          query = alias == null ? query.innerJoin((EntityPath) target) : query.innerJoin((EntityPath) target, alias);
        }
        if (target instanceof CollectionExpression) {
          query = alias == null ? query.innerJoin((CollectionExpression) target) : query.innerJoin((CollectionExpression) target, alias);
        }
        if (target instanceof MapExpression) {
          query = alias == null ? query.innerJoin((MapExpression) target) : query.innerJoin((MapExpression) target, alias);
        }
        if (predicates != null) {
          query = query.on(predicates);
        }
        if (fetchJoin) {
          query = query.fetchJoin();
        }
        break;
      case LEFTJOIN:
        if (target instanceof EntityPath) {
          query = alias == null ? query.leftJoin((EntityPath) target) : query.leftJoin((EntityPath) target, alias);
        }
        if (target instanceof CollectionExpression) {
          query = alias == null ? query.leftJoin((CollectionExpression) target) : query.leftJoin((CollectionExpression) target, alias);
        }
        if (target instanceof MapExpression) {
          query = alias == null ? query.leftJoin((MapExpression) target) : query.leftJoin((MapExpression) target, alias);
        }
        if (predicates != null) {
          query = query.on(predicates);
        }
        if (fetchJoin) {
          query = query.fetchJoin();
        }
        break;
      case RIGHTJOIN:
        if (target instanceof EntityPath) {
          query = alias == null ? query.rightJoin((EntityPath) target) : query.rightJoin((EntityPath) target, alias);
        }
        if (target instanceof CollectionExpression) {
          query = alias == null ? query.rightJoin((CollectionExpression) target) : query.rightJoin((CollectionExpression) target, alias);
        }
        if (target instanceof MapExpression) {
          query = alias == null ? query.rightJoin((MapExpression) target) : query.rightJoin((MapExpression) target, alias);
        }
        if (predicates != null) {
          query = query.on(predicates);
        }
        if (fetchJoin) {
          query = query.fetchJoin();
        }
        break;
      default:
        break;
      }
    }
    return query;
  }

  @Override
  public int executeUpdate(Query query) {
    return query.executeUpdate();
  }

  @Override
  public List getResultList(Query query) {
    return query.getResultList();
  }

  @Override
  public Object getSingleResult(Query query) {
    return query.getSingleResult();
  }

  /**
   * @see javax.persistence.EntityManager#persist(Object)
   */
  @Override
  public  List save(Iterable entities) {
    if (this.size == null) {
      return super.save(entities);
    }
    List result = new ArrayList();
    if (entities == null) {
      return result;
    }
    int i = 0;
    for (S entity : entities) {
      result.add(save(entity));
      if (++i % this.size == 0) {
        flush();
        this.entityManager.clear();
      }
    }
    return result;
  }

  @Override
  public T findOne(JPQLQuery query) {
    return query.select(path).fetchOne();
  }

  @Override
  public  RT findOne(Expression expression, JPQLQuery query) {
    return query.select(expression).fetchOne();
  }

  @Override
  public DataTablesOutput findAll(DataTablesInput input, Specification specification) {
    return findAll(input, null, specification);
  }

  @Override
  public  DataTablesOutput findAll(DataTablesInput input, Converter converter, Specification specification) {
    DataTablesOutput output = new DataTablesOutput();
    output.setDraw(input.getDraw());
    if (input.getLength() == 0) {
      return output;
    }

    try {
      Specifications spec = Specifications.where(new Specification() {
        @Override
        public javax.persistence.criteria.Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
          return null;
        }
      });
      if (specification != null) {
        spec.and(specification);
      }
      long recordsTotal = count(spec);
      if (recordsTotal == 0) {
        return output;
      }
      output.setRecordsTotal(recordsTotal);

      Page data = findAll(spec.and(SpecificationFactory.createSpecification(input, AnnotationUtils.findAnnotation(getDomainClass(), Search.class))), DataTablesUtils.getPageable(input));

      @SuppressWarnings("unchecked")
      List content = converter == null ? (List) data.getContent() : data.map(converter).getContent();
      output.setData(content);
      output.setRecordsFiltered(data.getTotalElements());

    }
    catch (Exception e) {
      if (logger.isWarnEnabled()) {
        logger.warn(e, e);
      }
      output.setError(e.toString());
    }

    return output;
  }

  @Override
  public DataTablesOutput findAll(DataTablesInput input, Predicate predicate, Join... joins) {
    return findAll(input, null, predicate, joins);
  }

  @Override
  public  DataTablesOutput findAll(DataTablesInput input, Converter converter, Predicate predicate, Join... joins) {
    DataTablesOutput output = new DataTablesOutput();
    output.setDraw(input.getDraw());
    if (input.getLength() == 0) {
      return output;
    }

    try {
      BooleanBuilder expression = new BooleanBuilder();
      if (predicate != null) {
        expression.and(predicate);
      }
      long recordsTotal = count(expression.getValue(), joins);
      if (recordsTotal == 0) {
        return output;
      }
      output.setRecordsTotal(recordsTotal);

      Page data = findAll(expression.and(PredicateFactory.createPredicate(this.builder, input, AnnotationUtils.findAnnotation(getDomainClass(), Search.class))).getValue(),
          DataTablesUtils.getPageable(input), joins);
      @SuppressWarnings("unchecked")
      List content = converter == null ? (List) data.getContent() : data.map(converter).getContent();
      output.setData(content);
      output.setRecordsFiltered(data.getTotalElements());

    }
    catch (Exception e) {
      if (logger.isWarnEnabled()) {
        logger.warn(e, e);
      }
      output.setError(e.toString());
    }

    return output;
  }

  @Override
  @SuppressWarnings("unchecked")
  public Revision findLastChangeRevision(ID id) {
    if (this.revisionEntityClass == null) {
      throw new UnsupportedOperationException();
    }
    Class type = getDomainClass();
    org.hibernate.envers.AuditReader reader = org.hibernate.envers.AuditReaderFactory.get(entityManager);
    List revisions = reader.getRevisions(type, id);
    if (revisions.isEmpty()) {
      return null;
    }
    Integer latestRevision = (Integer) revisions.get(revisions.size() - 1);
    Object revisionEntity = reader.findRevision(this.revisionEntityClass, latestRevision);
    RevisionMetadata metadata = (RevisionMetadata) getRevisionMetadata(revisionEntity);
    return new Revision(metadata, reader.find(type, id, latestRevision));
  }

  @SuppressWarnings("unchecked")
  @Override
  public Revision findRevision(ID id, Integer revisionNumber) {
    Assert.notNull(id, "Identifier must not be null!");
    Assert.notNull(revisionNumber, "Revision number must not be null!");
    if (this.revisionEntityClass == null) {
      throw new UnsupportedOperationException();
    }
    org.hibernate.envers.AuditReader reader = org.hibernate.envers.AuditReaderFactory.get(entityManager);
    T revision = (T) reader.findRevision(this.revisionEntityClass, revisionNumber);
    Object entity = reader.find(getDomainClass(), id, revisionNumber);
    return new Revision((RevisionMetadata) getRevisionMetadata(revision), (T) entity);
  }

  @Override
  @SuppressWarnings("unchecked")
  public Revisions findRevisions(ID id) {
    Class type = getDomainClass();
    org.hibernate.envers.AuditReader reader = org.hibernate.envers.AuditReaderFactory.get(entityManager);
    List revisionNumbers = reader.getRevisions(type, id);
    if (revisionNumbers.isEmpty()) {
      return new Revisions(Collections.EMPTY_LIST);
    }
    else {
      if (this.revisionEntityClass == null) {
        throw new UnsupportedOperationException();
      }
      Map revisions = new HashMap(revisionNumbers.size());
      Map revisionEntities = (Map) reader.findRevisions(this.revisionEntityClass, new HashSet(revisionNumbers));
      for (Number number : revisionNumbers) {
        revisions.put((Integer) number, reader.find(type, id, number));
      }
      return new Revisions(toRevisions(revisions, revisionEntities));
    }
  }

  @Override
  @SuppressWarnings("unchecked")
  public Page> findRevisions(ID id, Pageable pageable) {
    Class type = getDomainClass();
    org.hibernate.envers.AuditReader reader = org.hibernate.envers.AuditReaderFactory.get(entityManager);
    List revisionNumbers = reader.getRevisions(type, id);
    boolean isDescending = RevisionSort.getRevisionDirection(pageable.getSort()).isDescending();
    if (isDescending) {
      Collections.reverse(revisionNumbers);
    }
    if (pageable.getOffset() > revisionNumbers.size()) {
      return new PageImpl>(Collections.>emptyList(), pageable, 0);
    }
    int upperBound = pageable.getOffset() + pageable.getPageSize();
    upperBound = upperBound > revisionNumbers.size() ? revisionNumbers.size() : upperBound;
    List subList = revisionNumbers.subList(pageable.getOffset(), upperBound);

    if (this.revisionEntityClass == null) {
      throw new UnsupportedOperationException();
    }

    Map map = new HashMap(revisionNumbers.size());
    Map revisionEntities = (Map) reader.findRevisions(this.revisionEntityClass, new HashSet(revisionNumbers));
    for (Number number : subList) {
      map.put((Integer) number, reader.find(type, id, number));
    }
    Revisions revisions = new Revisions(toRevisions(map, revisionEntities));

    revisions = isDescending ? revisions.reverse() : revisions;
    return new PageImpl>(revisions.getContent(), pageable, revisionNumbers.size());
  }

  @SuppressWarnings("unchecked")
  private List> toRevisions(Map source, Map revisionEntities) {
    List> result = new ArrayList>();
    for (Entry revision : source.entrySet()) {
      Integer revisionNumber = revision.getKey();
      T entity = revision.getValue();
      RevisionMetadata metadata = (RevisionMetadata) getRevisionMetadata(revisionEntities.get(revisionNumber));
      result.add(new Revision(metadata, entity));
    }
    Collections.sort(result);
    return Collections.unmodifiableList(result);
  }

  private RevisionMetadata getRevisionMetadata(Object object) {
    if (object instanceof org.hibernate.envers.DefaultRevisionEntity) {
      return new DefaultRevisionMetadata((org.hibernate.envers.DefaultRevisionEntity) object);
    }
    else {
      return new AnnotationRevisionMetadata(object, org.hibernate.envers.RevisionNumber.class, org.hibernate.envers.RevisionTimestamp.class);
    }
  }
}