Skip to content

Added exists method with specification to JpaSpecificationExecutor. #2449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
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
Expand Up @@ -29,6 +29,7 @@
*
* @author Oliver Gierke
* @author Christoph Strobl
* @author Diego Krupitza
*/
public interface JpaSpecificationExecutor<T> {

Expand Down Expand Up @@ -74,4 +75,13 @@ public interface JpaSpecificationExecutor<T> {
* @return the number of instances.
*/
long count(@Nullable Specification<T> spec);

/**
* Checks whether the data store contains elements that match the given {@link Specification}.
*
* @param spec the {@link Specification} to use for the existence check. Must not be {@literal null}.
* @return <code>true</code> if the data store contains elements that match the given {@link Specification} otherwise
* <code>false</code>.
*/
boolean exists(Specification<T> spec);
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@
*/
package org.springframework.data.jpa.repository.support;

import static org.springframework.data.jpa.repository.query.QueryUtils.*;

import java.util.*;
import java.util.function.Function;

import javax.persistence.*;
import javax.persistence.criteria.*;

import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.data.domain.Example;
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.domain.*;
import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.provider.PersistenceProvider;
Expand All @@ -37,29 +41,6 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.Parameter;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import static org.springframework.data.jpa.repository.query.QueryUtils.*;

/**
* Default implementation of the {@link org.springframework.data.repository.CrudRepository} interface. This will offer
* you a more sophisticated interface than the plain {@link EntityManager} .
Expand All @@ -80,6 +61,7 @@
* @author Greg Turnquist
* @author Yanming Zhou
* @author Ernst-Jan van der Laan
* @author Diego Krupitza
*/
@Repository
@Transactional(readOnly = true)
Expand Down Expand Up @@ -555,6 +537,20 @@ public <S extends T> boolean exists(Example<S> example) {
return query.setMaxResults(1).getResultList().size() == 1;
}

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

CriteriaQuery<Integer> cq = this.em.getCriteriaBuilder().createQuery(Integer.class);
cq.select(this.em.getCriteriaBuilder().literal(1));
applySpecificationToCriteria(spec, getDomainClass(), cq);
TypedQuery<Integer> query = applyRepositoryMethodMetadata(this.em.createQuery(cq));
return query.setMaxResults(1).getResultList().size() == 1;
}

/*
* (non-Javadoc)
* @see org.springframework.data.repository.query.QueryByExampleExecutor#findAll(org.springframework.data.domain.Example)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
* Collection of {@link Specification}s for a {@link User}.
*
* @author Oliver Gierke
* @author Diego Krupitza
*/
public class UserSpecifications {

Expand Down Expand Up @@ -69,6 +70,17 @@ public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBu
};
}

/**
* A {@link Specification} to do an age check.
*
* @param age upper (exclusive) bound of the age
* @return
*/
public static Specification<User> userHasAgeLess(final Integer age) {

return (root, query, cb) -> cb.lessThan(root.get("age").as(Integer.class), age);
}

/**
* A {@link Specification} to do a like-match on a {@link User}'s lastname but also adding a sort order on the
* firstname.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
* @author Sander Krabbenborg
* @author Jesse Wouters
* @author Greg Turnquist
* @author Diego Krupitza
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:application-context.xml")
Expand Down Expand Up @@ -2640,6 +2641,17 @@ void readsDerivedInterfaceProjections() {
assertThat(repository.findAllInterfaceProjectedBy()).hasSize(4);
}

@Test // GH-2388
void existsWithSpec() {
flushTestUsers();

Specification<User> minorSpec = userHasAgeLess(18);
Specification<User> hundredYearsOld = userHasAgeLess(100);

assertThat(repository.exists(minorSpec)).isFalse();
assertThat(repository.exists(hundredYearsOld)).isTrue();
}

private Page<User> executeSpecWithSort(Sort sort) {

flushTestUsers();
Expand Down