Skip to content

Commit 36c9b4d

Browse files
DiegoKrupitzaschauder
authored andcommitted
Added exists method with specification to JpaSpecificationExecutor.
It is now possible to make existence checks based on a `Specification`. This is an addition to checking existence with using an `Example`. Closes #2388 Original pull request #2449
1 parent 8fb25c6 commit 36c9b4d

File tree

4 files changed

+58
-28
lines changed

4 files changed

+58
-28
lines changed

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

+10
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
*
3030
* @author Oliver Gierke
3131
* @author Christoph Strobl
32+
* @author Diego Krupitza
3233
*/
3334
public interface JpaSpecificationExecutor<T> {
3435

@@ -74,4 +75,13 @@ public interface JpaSpecificationExecutor<T> {
7475
* @return the number of instances.
7576
*/
7677
long count(@Nullable Specification<T> spec);
78+
79+
/**
80+
* Checks whether the data store contains elements that match the given {@link Specification}.
81+
*
82+
* @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}.
83+
* @return <code>true</code> if the data store contains elements that match the given {@link Specification} otherwise
84+
* <code>false</code>.
85+
*/
86+
boolean exists(Specification<T> spec);
7787
}

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

+24-28
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,16 @@
1515
*/
1616
package org.springframework.data.jpa.repository.support;
1717

18+
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
19+
20+
import java.util.*;
21+
import java.util.function.Function;
22+
23+
import javax.persistence.*;
24+
import javax.persistence.criteria.*;
25+
1826
import org.springframework.dao.EmptyResultDataAccessException;
19-
import org.springframework.data.domain.Example;
20-
import org.springframework.data.domain.Page;
21-
import org.springframework.data.domain.PageImpl;
22-
import org.springframework.data.domain.Pageable;
23-
import org.springframework.data.domain.Sort;
27+
import org.springframework.data.domain.*;
2428
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
2529
import org.springframework.data.jpa.domain.Specification;
2630
import org.springframework.data.jpa.provider.PersistenceProvider;
@@ -37,29 +41,6 @@
3741
import org.springframework.transaction.annotation.Transactional;
3842
import org.springframework.util.Assert;
3943

40-
import javax.persistence.EntityManager;
41-
import javax.persistence.LockModeType;
42-
import javax.persistence.NoResultException;
43-
import javax.persistence.Parameter;
44-
import javax.persistence.Query;
45-
import javax.persistence.TypedQuery;
46-
import javax.persistence.criteria.CriteriaBuilder;
47-
import javax.persistence.criteria.CriteriaQuery;
48-
import javax.persistence.criteria.ParameterExpression;
49-
import javax.persistence.criteria.Path;
50-
import javax.persistence.criteria.Predicate;
51-
import javax.persistence.criteria.Root;
52-
import java.util.ArrayList;
53-
import java.util.Collection;
54-
import java.util.Collections;
55-
import java.util.HashMap;
56-
import java.util.List;
57-
import java.util.Map;
58-
import java.util.Optional;
59-
import java.util.function.Function;
60-
61-
import static org.springframework.data.jpa.repository.query.QueryUtils.*;
62-
6344
/**
6445
* Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
6546
* you a more sophisticated interface than the plain {@link EntityManager} .
@@ -80,6 +61,7 @@
8061
* @author Greg Turnquist
8162
* @author Yanming Zhou
8263
* @author Ernst-Jan van der Laan
64+
* @author Diego Krupitza
8365
*/
8466
@Repository
8567
@Transactional(readOnly = true)
@@ -555,6 +537,20 @@ public <S extends T> boolean exists(Example<S> example) {
555537
return query.setMaxResults(1).getResultList().size() == 1;
556538
}
557539

540+
/*
541+
* (non-Javadoc)
542+
* @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#exists(org.springframework.data.jpa.domain.Specification)
543+
*/
544+
@Override
545+
public boolean exists(Specification<T> spec) {
546+
547+
CriteriaQuery<Integer> cq = this.em.getCriteriaBuilder().createQuery(Integer.class);
548+
cq.select(this.em.getCriteriaBuilder().literal(1));
549+
applySpecificationToCriteria(spec, getDomainClass(), cq);
550+
TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.em.createQuery(cq));
551+
return query.setMaxResults(1).getResultList().size() == 1;
552+
}
553+
558554
/*
559555
* (non-Javadoc)
560556
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)

src/test/java/org/springframework/data/jpa/domain/sample/UserSpecifications.java

+12
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
* Collection of {@link Specification}s for a {@link User}.
2727
*
2828
* @author Oliver Gierke
29+
* @author Diego Krupitza
2930
*/
3031
public class UserSpecifications {
3132

@@ -69,6 +70,17 @@ public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBu
6970
};
7071
}
7172

73+
/**
74+
* A {@link Specification} to do an age check.
75+
*
76+
* @param age upper (exclusive) bound of the age
77+
* @return
78+
*/
79+
public static Specification<User> userHasAgeLess(final Integer age) {
80+
81+
return (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), age);
82+
}
83+
7284
/**
7385
* A {@link Specification} to do a like-match on a {@link User}'s lastname but also adding a sort order on the
7486
* firstname.

src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java

+12
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
* @author Sander Krabbenborg
9797
* @author Jesse Wouters
9898
* @author Greg Turnquist
99+
* @author Diego Krupitza
99100
*/
100101
@ExtendWith(SpringExtension.class)
101102
@ContextConfiguration("classpath:application-context.xml")
@@ -2640,6 +2641,17 @@ void readsDerivedInterfaceProjections() {
26402641
assertThat(repository.findAllInterfaceProjectedBy()).hasSize(4);
26412642
}
26422643

2644+
@Test // GH-2388
2645+
void existsWithSpec() {
2646+
flushTestUsers();
2647+
2648+
Specification<User> minorSpec = userHasAgeLess(18);
2649+
Specification<User> hundredYearsOld = userHasAgeLess(100);
2650+
2651+
assertThat(repository.exists(minorSpec)).isFalse();
2652+
assertThat(repository.exists(hundredYearsOld)).isTrue();
2653+
}
2654+
26432655
private Page<User> executeSpecWithSort(Sort sort) {
26442656

26452657
flushTestUsers();

0 commit comments

Comments
 (0)