Last active
September 5, 2024 13:26
-
-
Save tcollins/0ebd1dfa78028ecdef0b to your computer and use it in GitHub Desktop.
Spring Data JPA - Limit results when using Specifications without an unnecessary count query being executed
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
If you use the findAll(Specification, Pageable) method, a count query is first executed and then the | |
data query is executed if the count returns a value greater than the offset. | |
For what I was doing I did not need pageable, but simply wanted to limit my results. This is easy | |
to do with static named queries and methodNameMagicGoodness queries, but from my research (googling | |
for a few hours) I couldn't find a way to do it with dynamic criteria queries using Specifications. | |
During my search I found two things that helped me to figure out how to just do it myself. | |
1.) A stackoverflow question. | |
How to disable count when Specification and Pageable are used together? | |
http://stackoverflow.com/questions/26738199/how-to-disable-count-when-specification-and-pageable-are-used-together | |
(where I will add a link to this gist) | |
2.) Spring documentation - Adding custom behavior to all repositories | |
http://docs.spring.io/spring-data/data-jpa/docs/current/reference/html/#repositories.custom-behaviour-for-all-repositories | |
I followed the Spring documentation pretty closely and got this all working pretty quickly without | |
any real problems. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@NoRepositoryBean | |
public interface BaseRepository<T, ID extends Serializable> extends JpaRepository<T, ID> { | |
List<T> findAll(Specification<T> spec, int offset, int maxResults, Sort sort); | |
List<T> findAll(Specification<T> spec, int offset, int maxResults); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BaseRepositoryFactoryBean<R extends JpaRepository<T, I>, T, I extends Serializable> extends JpaRepositoryFactoryBean<R, T, I> { | |
@SuppressWarnings("rawtypes") | |
@Override | |
protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) { | |
return new BaseRepositoryFactory(entityManager); | |
} | |
private static class BaseRepositoryFactory<T, I extends Serializable> extends JpaRepositoryFactory { | |
private final EntityManager em; | |
public BaseRepositoryFactory(EntityManager em) { | |
super(em); | |
this.em = em; | |
} | |
@SuppressWarnings({ "unchecked", "rawtypes", "hiding" }) | |
protected <T, ID extends Serializable> SimpleJpaRepository<?, ?> getTargetRepository(RepositoryMetadata metadata, EntityManager entityManager) { | |
SimpleJpaRepository<?, ?> repo = new BaseRepositoryImpl(metadata.getDomainType(), entityManager); | |
return repo; | |
} | |
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) { | |
return BaseRepositoryImpl.class; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class BaseRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseRepository<T, ID> { | |
private final EntityManager entityManager; | |
public BaseRepositoryImpl(Class<T> domainClass, EntityManager entityManager) { | |
super(domainClass, entityManager); | |
this.entityManager = entityManager; | |
} | |
public List<T> findAll(Specification<T> spec, int offset, int maxResults) { | |
return findAll(spec, offset, maxResults, null); | |
} | |
public List<T> findAll(Specification<T> spec, int offset, int maxResults, Sort sort) { | |
TypedQuery<T> query = getQuery(spec, sort); | |
if (offset < 0) { | |
throw new IllegalArgumentException("Offset must not be less than zero!"); | |
} | |
if (maxResults < 1) { | |
throw new IllegalArgumentException("Max results must not be less than one!"); | |
} | |
query.setFirstResult(offset); | |
query.setMaxResults(maxResults); | |
return query.getResultList(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@SpringBootApplication | |
@EnableJpaRepositories(repositoryFactoryBeanClass = BaseRepositoryFactoryBean.class) | |
public class MySpringBootApplication { | |
public static void main(String[] args) { | |
SpringApplication app = new SpringApplication(MySpringBootApplication.class); | |
app.run(args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is just to show an example of a repo | |
public interface UserRepository extends BaseRepository<User, Long>, JpaSpecificationExecutor<User> { | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Slice is simply a specialization of Window, the same goal can be achieved using the
<S extends T, R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction);
method of
JpaSpecificationExecutor
https://gist.github.com/josergdev/06c82891a719eca4834410339885ad23