Skip to content

Commit 084abb2

Browse files
committed
DATAJPA-218 - Extend Query by Example API to typed and untyped specs.
1 parent 473a0af commit 084abb2

File tree

4 files changed

+190
-51
lines changed

4 files changed

+190
-51
lines changed

src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
import org.springframework.dao.InvalidDataAccessApiUsageException;
3636
import org.springframework.data.domain.Example;
3737
import org.springframework.data.domain.ExampleSpec;
38-
import org.springframework.data.domain.ExampleSpecAccessor;
38+
import org.springframework.data.repository.core.support.ExampleSpecAccessor;
3939
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
4040
import org.springframework.orm.jpa.JpaSystemException;
4141
import org.springframework.util.Assert;

src/main/java/org/springframework/data/jpa/repository/JpaRepository.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,12 +104,12 @@ public interface JpaRepository<T, ID extends Serializable>
104104
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
105105
*/
106106
@Override
107-
<S extends T> List<T> findAll(Example<S> example);
107+
<S extends T> List<S> findAll(Example<S> example);
108108

109109
/* (non-Javadoc)
110110
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
111111
*/
112112
@Override
113-
<S extends T> List<T> findAll(Example<S> example, Sort sort);
113+
<S extends T> List<S> findAll(Example<S> example, Sort sort);
114114

115115
}

src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java

Lines changed: 98 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -433,52 +433,63 @@ public List<T> findAll(Specification<T> spec, Sort sort) {
433433
/* (non-Javadoc)
434434
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findOne(org.springframework.data.domain.Example)
435435
*/
436+
@SuppressWarnings("unchecked")
436437
@Override
437-
public <S extends T> T findOne(Example<S> example) {
438-
return findOne(new ExampleSpecification<T>((Example) example));
438+
public <S extends T> S findOne(Example<S> example) {
439+
try {
440+
return getQuery(new ExampleSpecification<S>(example), example.getResultType(), (Sort) null).getSingleResult();
441+
} catch (NoResultException e) {
442+
return null;
443+
}
439444
}
440445

441446
/* (non-Javadoc)
442447
* @see org.springframework.data.repository.query.QueryByExampleExecutor#count(org.springframework.data.domain.Example)
443448
*/
449+
@SuppressWarnings("unchecked")
444450
@Override
445451
public <S extends T> long count(Example<S> example) {
446-
return count(new ExampleSpecification<T>((Example) example));
452+
return executeCountQuery(getCountQuery(new ExampleSpecification<S>(example), example.getResultType()));
447453
}
448454

449455
/* (non-Javadoc)
450456
* @see org.springframework.data.repository.query.QueryByExampleExecutor#exists(org.springframework.data.domain.Example)
451457
*/
452458
@Override
453459
public <S extends T> boolean exists(Example<S> example) {
454-
return !getQuery(new ExampleSpecification<T>((Example) example), (Sort) null).getResultList().isEmpty();
460+
return !getQuery(new ExampleSpecification<S>(example), example.getResultType(), (Sort) null).getResultList()
461+
.isEmpty();
455462
}
456463

457464
/*
458465
* (non-Javadoc)
459466
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
460467
*/
461468
@Override
462-
public <S extends T> List<T> findAll(Example<S> example) {
463-
return findAll(new ExampleSpecification<T>((Example) example));
469+
public <S extends T> List<S> findAll(Example<S> example) {
470+
return getQuery(new ExampleSpecification<S>(example), example.getResultType(), (Sort) null).getResultList();
464471
}
465472

466473
/*
467474
* (non-Javadoc)
468475
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Sort)
469476
*/
470477
@Override
471-
public <S extends T> List<T> findAll(Example<S> example, Sort sort) {
472-
return findAll(new ExampleSpecification<T>((Example) example), sort);
478+
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
479+
return getQuery(new ExampleSpecification<S>(example), example.getResultType(), sort).getResultList();
473480
}
474481

475482
/*
476483
* (non-Javadoc)
477484
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example, org.springframework.data.domain.Pageable)
478485
*/
479486
@Override
480-
public <S extends T> Page<T> findAll(Example<S> example, Pageable pageable) {
481-
return findAll(new ExampleSpecification<T>((Example) example), pageable);
487+
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
488+
489+
ExampleSpecification<S> spec = new ExampleSpecification<S>(example);
490+
TypedQuery<S> query = getQuery(new ExampleSpecification<S>(example), example.getResultType(), pageable);
491+
return pageable == null ? new PageImpl<S>(query.getResultList())
492+
: readPage(query, example.getResultType(), pageable, spec);
482493
}
483494

484495
/*
@@ -496,7 +507,6 @@ public long count() {
496507
*/
497508
@Override
498509
public long count(Specification<T> spec) {
499-
500510
return executeCountQuery(getCountQuery(spec));
501511
}
502512

@@ -572,14 +582,29 @@ public void flush() {
572582
* @return
573583
*/
574584
protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) {
585+
return readPage(query, getDomainClass(), pageable, spec);
586+
}
587+
588+
/**
589+
* Reads the given {@link TypedQuery} into a {@link Page} applying the given {@link Pageable} and
590+
* {@link Specification}.
591+
*
592+
* @param query must not be {@literal null}.
593+
* @param domainClass must not be {@literal null}.
594+
* @param spec can be {@literal null}.
595+
* @param pageable can be {@literal null}.
596+
* @return
597+
*/
598+
protected <S extends T> Page<S> readPage(TypedQuery<S> query, Class<S> domainClass, Pageable pageable,
599+
Specification<S> spec) {
575600

576601
query.setFirstResult(pageable.getOffset());
577602
query.setMaxResults(pageable.getPageSize());
578603

579-
Long total = executeCountQuery(getCountQuery(spec));
580-
List<T> content = total > pageable.getOffset() ? query.getResultList() : Collections.<T> emptyList();
604+
Long total = executeCountQuery(getCountQuery(spec, domainClass));
605+
List<S> content = total > pageable.getOffset() ? query.getResultList() : Collections.<S> emptyList();
581606

582-
return new PageImpl<T>(content, pageable, total);
607+
return new PageImpl<S>(content, pageable, total);
583608
}
584609

585610
/**
@@ -592,7 +617,21 @@ protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification
592617
protected TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) {
593618

594619
Sort sort = pageable == null ? null : pageable.getSort();
595-
return getQuery(spec, sort);
620+
return getQuery(spec, getDomainClass(), sort);
621+
}
622+
623+
/**
624+
* Creates a new {@link TypedQuery} from the given {@link Specification}.
625+
*
626+
* @param spec can be {@literal null}.
627+
* @param domainClass must not be {@literal null}.
628+
* @param pageable can be {@literal null}.
629+
* @return
630+
*/
631+
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Pageable pageable) {
632+
633+
Sort sort = pageable == null ? null : pageable.getSort();
634+
return getQuery(spec, domainClass, sort);
596635
}
597636

598637
/**
@@ -603,11 +642,23 @@ protected TypedQuery<T> getQuery(Specification<T> spec, Pageable pageable) {
603642
* @return
604643
*/
605644
protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {
645+
return getQuery(spec, getDomainClass(), sort);
646+
}
647+
648+
/**
649+
* Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}.
650+
*
651+
* @param spec can be {@literal null}.
652+
* @param domainClass must not be {@literal null}.
653+
* @param sort can be {@literal null}.
654+
* @return
655+
*/
656+
protected <S extends T> TypedQuery<S> getQuery(Specification<S> spec, Class<S> domainClass, Sort sort) {
606657

607658
CriteriaBuilder builder = em.getCriteriaBuilder();
608-
CriteriaQuery<T> query = builder.createQuery(getDomainClass());
659+
CriteriaQuery<S> query = builder.createQuery(domainClass);
609660

610-
Root<T> root = applySpecificationToCriteria(spec, query);
661+
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
611662
query.select(root);
612663

613664
if (sort != null) {
@@ -624,11 +675,22 @@ protected TypedQuery<T> getQuery(Specification<T> spec, Sort sort) {
624675
* @return
625676
*/
626677
protected TypedQuery<Long> getCountQuery(Specification<T> spec) {
678+
return getCountQuery(spec, getDomainClass());
679+
}
680+
681+
/**
682+
* Creates a new count query for the given {@link Specification}.
683+
*
684+
* @param spec can be {@literal null}.
685+
* @param domainClass must not be {@literal null}.
686+
* @return
687+
*/
688+
protected <S extends T> TypedQuery<Long> getCountQuery(Specification<S> spec, Class<S> domainClass) {
627689

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

631-
Root<T> root = applySpecificationToCriteria(spec, query);
693+
Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
632694

633695
if (query.isDistinct()) {
634696
query.select(builder.countDistinct(root));
@@ -647,9 +709,24 @@ protected TypedQuery<Long> getCountQuery(Specification<T> spec) {
647709
* @return
648710
*/
649711
private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, CriteriaQuery<S> query) {
712+
return applySpecificationToCriteria(spec, getDomainClass(), query);
713+
714+
}
715+
716+
/**
717+
* Applies the given {@link Specification} to the given {@link CriteriaQuery}.
718+
*
719+
* @param spec can be {@literal null}.
720+
* @param domainClass must not be {@literal null}.
721+
* @param query must not be {@literal null}.
722+
* @return
723+
*/
724+
private <S, U extends T> Root<U> applySpecificationToCriteria(Specification<U> spec, Class<U> domainClass,
725+
CriteriaQuery<S> query) {
650726

651727
Assert.notNull(query);
652-
Root<T> root = query.from(getDomainClass());
728+
Assert.notNull(domainClass);
729+
Root<U> root = query.from(domainClass);
653730

654731
if (spec == null) {
655732
return root;
@@ -665,14 +742,14 @@ private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, Criteria
665742
return root;
666743
}
667744

668-
private TypedQuery<T> applyRepositoryMethodMetadata(TypedQuery<T> query) {
745+
private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {
669746

670747
if (metadata == null) {
671748
return query;
672749
}
673750

674751
LockModeType type = metadata.getLockModeType();
675-
TypedQuery<T> toReturn = type == null ? query : query.setLockMode(type);
752+
TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
676753

677754
applyQueryHints(toReturn);
678755

0 commit comments

Comments
 (0)