Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package th.co.geniustree.springdata.jpa.repository;

import java.io.Serializable;
import org.springframework.data.domain.Page;
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.EntityGraph;
import org.springframework.data.jpa.repository.query.JpaEntityGraph;
import org.springframework.data.repository.NoRepositoryBean;

import java.util.List;
import java.util.Optional;

/**
Expand All @@ -17,13 +18,18 @@
public interface JpaSpecificationExecutorWithProjection<T, ID> {

<R> Optional<R> findById(ID id, Class<R> projectionClass);

<R> Optional<R> findOne(Specification<T> spec, Class<R> projectionClass);

<R> List<R> findAll(Specification<T> spec, Class<R> projectionClass);

<R> List<R> findAll(Specification<T> spec, Class<R> projectionClass, Sort sort);

<R> Page<R> findAll(Specification<T> spec, Class<R> projectionClass, Pageable pageable);

/**
* Use Spring Data Annotation instead of manually provide EntityGraph.
*
* @param spec
* @param projectionType
* @param namedEntityGraph
Expand All @@ -37,6 +43,7 @@ public interface JpaSpecificationExecutorWithProjection<T, ID> {

/**
* Use Spring Data Annotation instead of manually provide EntityGraph.
*
* @param spec
* @param projectionClass
* @param dynamicEntityGraph
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,25 @@
import org.springframework.util.Assert;
import th.co.geniustree.springdata.jpa.repository.JpaSpecificationExecutorWithProjection;

import javax.persistence.*;
import javax.persistence.criteria.*;
import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.ManyToOne;
import javax.persistence.NoResultException;
import javax.persistence.OneToOne;
import javax.persistence.Query;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Expression;
import javax.persistence.criteria.Fetch;
import javax.persistence.criteria.From;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.criteria.Selection;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.Bindable;
import javax.persistence.metamodel.ManagedType;
Expand All @@ -34,9 +51,19 @@
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Member;
import java.util.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static javax.persistence.metamodel.Attribute.PersistentAttributeType.*;
import static javax.persistence.metamodel.Attribute.PersistentAttributeType.ELEMENT_COLLECTION;
import static javax.persistence.metamodel.Attribute.PersistentAttributeType.MANY_TO_MANY;
import static javax.persistence.metamodel.Attribute.PersistentAttributeType.MANY_TO_ONE;
import static javax.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_MANY;
import static javax.persistence.metamodel.Attribute.PersistentAttributeType.ONE_TO_ONE;


/**
Expand All @@ -46,7 +73,8 @@ public class JpaSpecificationExecutorWithProjectionImpl<T, ID extends Serializab

private static final Logger log = LoggerFactory.getLogger(JpaSpecificationExecutorWithProjectionImpl.class);
private static final Map<Attribute.PersistentAttributeType, Class<? extends Annotation>> ASSOCIATION_TYPES;
static{

static {
Map<Attribute.PersistentAttributeType, Class<? extends Annotation>> persistentAttributeTypes = new HashMap<Attribute.PersistentAttributeType, Class<? extends Annotation>>();
persistentAttributeTypes.put(ONE_TO_ONE, OneToOne.class);
persistentAttributeTypes.put(ONE_TO_MANY, null);
Expand All @@ -56,6 +84,7 @@ public class JpaSpecificationExecutorWithProjectionImpl<T, ID extends Serializab

ASSOCIATION_TYPES = Collections.unmodifiableMap(persistentAttributeTypes);
}

private final EntityManager entityManager;

private final ProjectionFactory projectionFactory = new SpelAwareProxyProjectionFactory();
Expand All @@ -67,11 +96,11 @@ public JpaSpecificationExecutorWithProjectionImpl(JpaEntityInformation entityInf
this.entityManager = entityManager;
this.entityInformation = entityInformation;
}

@Override
public <R> Optional<R> findById(ID id, Class<R> projectionType) {
final ReturnedType returnedType = ReturnTypeWarpper.of(projectionType, getDomainClass(), projectionFactory);

CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> q = builder.createQuery(Tuple.class);
Root<T> root = q.from(getDomainClass());
Expand All @@ -89,11 +118,11 @@ public <R> Optional<R> findById(ID id, Class<R> projectionType) {
} else {
throw new IllegalArgumentException("only except projection");
}

final TypedQuery<Tuple> query = this.applyRepositoryMethodMetadata(this.entityManager.createQuery(q));

try {
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory,returnedType);
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory, returnedType);
final R singleResult = resultProcessor.processResult(query.getSingleResult(), new TupleConverter(returnedType));
return Optional.ofNullable(singleResult);
} catch (NoResultException e) {
Expand All @@ -107,21 +136,36 @@ public <R> Optional<R> findOne(Specification<T> spec, Class<R> projectionType) {
final ReturnedType returnedType = ReturnTypeWarpper.of(projectionType, getDomainClass(), projectionFactory);
final TypedQuery<Tuple> query = getTupleQuery(spec, Sort.unsorted(), returnedType);
try {
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory,returnedType);
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory, returnedType);
final R singleResult = resultProcessor.processResult(query.getSingleResult(), new TupleConverter(returnedType));
return Optional.ofNullable(singleResult);
} catch (NoResultException e) {
return Optional.empty();
}
}

@Override
public <R> List<R> findAll(Specification<T> spec, Class<R> projectionType) {
return findAll(spec, projectionType, Sort.unsorted());
}

@Override
public <R> List<R> findAll(Specification<T> spec, Class<R> projectionType, Sort sort) {
return findAll(spec, projectionType, Pageable.unpaged(), sort).getContent();
}

@Override
public <R> Page<R> findAll(Specification<T> spec, Class<R> projectionType, Pageable pageable) {
Sort sort = pageable.getSort() != null && pageable.getSort().isSorted() ? pageable.getSort() : Sort.unsorted();
return findAll(spec, projectionType, pageable, sort);
}

private <R> Page<R> findAll(Specification<T> spec, Class<R> projectionType, Pageable pageable, Sort sort) {
final ReturnedType returnedType = ReturnTypeWarpper.of(projectionType, getDomainClass(), projectionFactory);
final TypedQuery<Tuple> query = getTupleQuery(spec, pageable.getSort() != null && pageable.getSort().isSorted() ? pageable.getSort() : Sort.unsorted(), returnedType);
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory,returnedType);
final TypedQuery<Tuple> query = getTupleQuery(spec, sort, returnedType);
final MyResultProcessor resultProcessor = new MyResultProcessor(projectionFactory, returnedType);
if (pageable.isPaged()) {
query.setFirstResult((int)pageable.getOffset());
query.setFirstResult((int) pageable.getOffset());
query.setMaxResults(pageable.getPageSize());
}
final List<R> resultList = resultProcessor.processResult(query.getResultList(), new TupleConverter(returnedType));
Expand All @@ -135,26 +179,26 @@ static Long executeCountQuery(TypedQuery<Long> query) {
Long total = 0L;

Long element;
for(Iterator var3 = totals.iterator(); var3.hasNext(); total = total + (element == null ? 0L : element)) {
element = (Long)var3.next();
for (Iterator var3 = totals.iterator(); var3.hasNext(); total = total + (element == null ? 0L : element)) {
element = (Long) var3.next();
}

return total;
}

@Override
public <R> Page<R> findAll(Specification<T> spec, Class<R> projectionType, String namedEntityGraph, org.springframework.data.jpa.repository.EntityGraph.EntityGraphType type, Pageable pageable) {
return findAll(spec,projectionType,pageable);
return findAll(spec, projectionType, pageable);
}

@Override
public <R> Page<R> findAll(Specification<T> spec, Class<R> projectionType, JpaEntityGraph dynamicEntityGraph, Pageable pageable) {
return findAll(spec,projectionType,pageable);
return findAll(spec, projectionType, pageable);
}

protected TypedQuery<Tuple> getTupleQuery(@Nullable Specification spec, Sort sort, ReturnedType returnedType) {
if (!returnedType.needsCustomConstruction()){
return getQuery(spec,sort);
if (!returnedType.needsCustomConstruction()) {
return getQuery(spec, sort);
}
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaQuery<Tuple> query = builder.createQuery(Tuple.class);
Expand Down Expand Up @@ -213,9 +257,10 @@ private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {

return toReturn;
}

private void applyQueryHints(Query query) {
QueryHints queryHints = DefaultQueryHints.of(this.entityInformation, getRepositoryMethodMetadata());
if(queryHints==null){
if (queryHints == null) {
queryHints = QueryHints.NoHints.INSTANCE;
}
for (Map.Entry<String, Object> hint : queryHints.withFetchGraphs(this.entityManager)) {
Expand Down Expand Up @@ -311,6 +356,7 @@ private static boolean requiresJoin(@Nullable Bindable<?> propertyPathModel, boo

return from.join(attribute, JoinType.LEFT);
}

private static boolean isAlreadyFetched(From<?, ?> from, String attribute) {

for (Fetch<?, ?> fetch : from.getFetches()) {
Expand Down
Loading