superflux
superflux

Reputation: 137

Spring Boot Dynamic Querying With Spring Data Rest

I am trying to do something a little interesting where I want to be able to dynamically build sql query filters using Spring Boot 1.5.9 and Spring Data Rest without needing to write a controller. I feel like I might be on the right path, but I am kinda stuck.

The idea is to intercept an HTTP Request GET method by using HandlerInterceptorAdapter and store the Request query parameters into an object that can be used by the PagingAndSortingRepository. The plan is to override findAll(Pageable p) that Spring Data Rest uses by default, to call findAll(Specification s, Pageable p) to allow dynamic filtering.

I have used Request Scope provided by Spring to save the query parameters to allow me to use them throughout the life cycle of the Request.

The problem is that I cannot access the QueryParam object from the Repository interface file. I tried to use dependency injection into the interface file, by following this stackoverflow solution, but it does not work: Spring Dependency injection for interfaces

Spring Data Rest tries to create an endpoint by trying to build a SQL query based on the function name. Even when I tried to get around this issue by trying to trick Spring Data by creating a function that it can query with then overriding it with SampleRepositoryComponents that I implemented below, I cannot get the data stored in QueryParam.

Someone else also made a pull request to allow Spring Data Rest to ignore certain methods: https://github.com/spring-projects/spring-data-rest/pull/286

QueryParam:

public class QueryParam {
  String query_string;
  Map<String, String[]> parameter_map;

  public String getQuery_string() {
    return query_string;
  }
  public void setQuery_string(String query_string) {
    this.query_string = query_string;
  }
  public Map<String, String[]> getParameter_map() {
    return parameter_map;
  }
  public void setParameter_map(Map<String, String[]> parameter_map) {
    this.parameter_map = parameter_map;
  }
}

SampleRepository:

@RepositoryRestResource(collectionResourceRel = "Sample", path = "Sample")
public interface SampleRepository extends PagingAndSortingRepository<Sample, UUID>,
    JpaSpecificationExecutor<Sample> {
  // Cannot just use any custom name
  @RestResource(exported = false)
  public QueryParam getQuery_param();

  default public Page<Sample> findAll(Pageable p) {
    QuerySpecification<Sample> qs = new QuerySpecification<Sample>(getQuery_param());

    return findAll(qs, p);
  }
}

QuerySpecification:

public class QuerySpecification<T> implements Specification<T> {
  private QueryParam query_param;

  public QuerySpecification(QueryParam query_param) {
    this.query_param = query_param;
  };

  @Override
  public Predicate toPredicate(Root<T> rt, CriteriaQuery<?> cq, CriteriaBuilder cb) {
    // Build your custom predicate here
    return null;
  }
}

SampleRepositoryComponents:

@Component
public abstract class SampleRepositoryComponents implements SampleRepository {
  @Autowired
  private QueryParam query_param;

  @Override
  public QueryParam getQuery_param() {
    return this.query_param;
  }

  public void setQuery_param(QueryParam query_param) {
    this.query_param = query_param;
  }
}

Upvotes: 4

Views: 4336

Answers (1)

Cepr0
Cepr0

Reputation: 30309

The same is already implemented 'from the box' in Spring Data REST with help of Querydsl and Web support Spring Data extensions.

Just extend you repos from QuerydslPredicateExecutor<MyEntity> (and optionally QuerydslBinderCustomizer<QMyEntity> with default implementation of the customize method like in the example here), then you can query your repo data with filtering (more other - with paging and sorting support):

/myEntities?field1=value1&field2=value2&sort=field2,desc&size=10&page=2

Don't forget to add QueryDSL dependency to you project (like this for example):

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
</dependency>

<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <scope>provided</scope>
</dependency>

<build>
    <plugins>
        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <version>1.1.3</version>
            <executions>
                <execution>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/annotations</outputDirectory>
                        <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>                                                       
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Then compile your project (for example mvn compile) to let it make 'Q' classes.

Upvotes: 3

Related Questions