I have a Spring Data Rest repository which utilises the QueryDSL support outlined here:
https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#spring-data-rest
The default is to query all specified parameters using equals. A mechanism to override the param binding to something other than equals is given in the same article however it requires Java 8.
https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support
Is there any clean way in Java 7 to achieve the same functionality?
I can get the binding customization working as below:
@Override
public void customize(QuerydslBindings bindings, QMember member) {
bindings.bind(member.forename).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String value) {
return path.like(value);
}
});
bindings.bind(member.surname).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String value) {
return path.startsWith(value);
}
});
}
However the examples all use a Java 8 default method on the repository interface to have them applied.
public interface MemberRepository extends JpaRepository<Member, Long>,
QueryDslPredicateExecutor<Member>,QuerydslBinderCustomizer<QMember> {
default void customize(QuerydslBindings bindings, QMember member) {
....
}
}
In Java 7 this is obviously not possible. I have tried with a custom repository however that fails with an error:
org.springframework.data.mapping.PropertyReferenceException: No property customize found for type Member!
public interface MemberRepositoryCustom {
public void customize(QuerydslBindings bindings, QMember member);
}
public class MemberRepositoryCustomImpl implements MemberRepositoryCustom {
@Override
public void customize(QuerydslBindings bindings, QMember member) {
bindings.bind(member.forename).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String value) {
return path.like(value);
}
});
bindings.bind(member.surname).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String value) {
return path.startsWith(value);
}
});
}
}
There are two ways that you can accomplish this using Java 7. The first way is to create a generic base repository that will apply the custom bindings to all of the implemented model repositories. For example:
public class GenericModelRepository<T, ID extends Serializable, S extends EntityPath<T>> extends QueryDslJpaRepository<T, ID> implements QuerydslBinderCustomizer<S> {
public GenericModelRepository(
JpaEntityInformation<T, ID> entityInformation,
EntityManager entityManager) {
super(entityInformation, entityManager);
}
public GenericModelRepository(
JpaEntityInformation<T, ID> entityInformation,
EntityManager entityManager,
EntityPathResolver resolver) {
super(entityInformation, entityManager, resolver);
}
@Override
public void customize(QuerydslBindings bindings, S t) {
bindings.bind(String.class).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String s) {
return path.equalsIgnoreCase(s);
}
});
}
}
To tell Spring Data to use this base repository when implementing all of your custom repository interfaces, simply add it as the repositoryBaseClass in the @EnableJpaRepositories annotation:
@Configuration
@EnableJpaRepositories(basePackages = { "me.woemler.project.repositories" }, repositoryBaseClass = GenericModelRepository.class)
@EnableTransactionManagement
public class RepositoryConfig { ... }
@RepositoryRestResource
public interface PersonRepository extends JpaRepository<Person, Long>,
QueryDslPredicateExecutor<Person>,
QuerydslBinderCustomizer<EntityPath<Person>> {
}
Now all web service StringPath query operations will be case-insensitive equality tests:
GET http://localhost:8080/persons?name=joe%20smith
"_embedded": {
"persons": [
{
"name": "Joe Smith",
"gender": "M",
"age": 35,
"_links": {
"self": {
"href": "http://localhost:8080/persons/1"
},
"person": {
"href": "http://localhost:8080/persons/1"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/persons"
},
"profile": {
"href": "http://localhost:8080/profile/persons"
}
},
"page": {
"size": 20,
"totalElements": 1,
"totalPages": 1,
"number": 0
}
}
The second option, should you want more fine control over how each repository handling its bindings would be to create an Impl version of the repositories you wish to customize:
public class PersonRepositoryImpl implements QuerydslBinderCustomizer<EntityPath<Person>> {
@Override
public void customize(QuerydslBindings bindings, EntityPath<Person> t) {
bindings.bind(String.class).first(new SingleValueBinding<StringPath, String>() {
@Override
public Predicate bind(StringPath path, String s) {
return path.equalsIgnoreCase(s);
}
});
}
}
You can then use the @EnableJpaRepositories annotation normally, but you must create an Impl instance for each of your repository interfaces that you wish to customize.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With