Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.2k views
in Technique[技术] by (71.8m points)

spring data - Customizing Param Binding for QueryDSL Support

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?

Update

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);
            }
        });
    }
}
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to WuJiGu Developer Q&A Community for programmer and developer-Open, Learning and Share
...