Skip to content

Customized data query with DataTables support, based on spring-data

Notifications You must be signed in to change notification settings

EaphoneTech/eaphone-spring-data-query

Repository files navigation

eaphone-spring-data-query

Customized query binding for spring-data.

This is a sample project showing how it works.

This project is inspired from darrachequesne/spring-data-jpa-datatables, which works with spring-data-jpa.

Quick view

screen cast from sample project

Usage

A short but working example.

Introduce into project

TODO Not uploaded to any public Maven Repository yet.

<dependency>
    <groupId>com.eaphonetech</groupId>
    <artifactId>eaphone-spring-data-query-jpa</artifactId>
    <version>2.0.1</version>
</dependency>

OR for MongoDB:

<dependency>
    <groupId>com.eaphonetech</groupId>
    <artifactId>eaphone-spring-data-query-mongodb</artifactId>
    <version>2.0.1</version>
</dependency>

Initialization

In any @Configuration class, add:

@EnableMongoRepositories(repositoryFactoryBeanClass = EaphoneQueryRepositoryFactoryBean.class)

Write new Repository

Just as spring-data does:

@Repository
public interface UserRepository extends JpaQueryRepository<Order, Long> {
}

OR for MongoDB:

@Repository
public interface UserRepository extends MongoDBQueryRepository<Order, String> {
}

Note that EaphoneQueryRepository extends PagingAndSortingRepository so it already contains functionalities like findAll(Pageable) and save().

Expose fields on view

In many cases the data grid shows only part of all fields. In this scenario you can add @JsonView(QueryOutput.View.class) to your controller and entity, so only fields marked with @JsonView(QueryOutput.View.class) will be displayed in query output.

@Data
@Document(collection = "order")
public class Order {

    @Id
    @JsonView(QueryOutput.View.class)
    private String id;

    @JsonFormat(pattern = "yyyy-MM-dd")
    @JsonView(QueryOutput.View.class)
    private Date date;

    @JsonView(QueryOutput.View.class)
    private String orderNumber;

    @JsonView(QueryOutput.View.class)
    private boolean isValid;

    @JsonView(QueryOutput.View.class)
    private double price;
    
    /** the detail will not show in query result */
    private EmbeddedOrderDetail detail;
}

On the browser side

A special designed data grid is designed for this protocol, will be introduced later.

On the Server Side

A repository has the following methods:

  • Using Query
    • QueryOutput<T> findAll(QueryInput input); for basic simple query
    • <R> QueryOutput<R> findAll(QueryInput input, Function<T, R> converter); for query with customized data convert
  • Using Specification (JPA only)
    • QueryOutput<T> findAll(QueryInput input, Specification<T> additionalSpecification);
    • QueryOutput<T> findAll(QueryInput input, Specification<T> additionalSpecification, Specification<T> preFilteringSpecification);
    • <R> QueryOutput<R> findAll(QueryInput input, Specification<T> additionalSpecification, Specification<T> preFilteringSpecification, Function<T, R> converter);
  • Using Criteria (MongoDB only)
    • QueryOutput<T> findAll(QueryInput input, Criteria additionalCriteria);
    • QueryOutput<T> findAll(QueryInput input, Criteria additionalCriteria, Criteria preFilteringCriteria);
  • Using Aggregation (MongoDB only)
    • <View> QueryOutput<View> findAll(Class<View> classOfView, QueryInput input, AggregationOperation... operations);
    • <View> QueryOutput<View> findAll(Class<View> classOfView, QueryInput input, Collection<? extends AggregationOperation> operations);

Examples

@PostMapping("/data/orders/search")
public QueryOutput<Order> getOrdersByPost(@Valid @RequestBody QueryInput input) {
    return repo.findAll(input);
}

@PostMapping("/data/orders/count")
public CountOutput count(@Valid @RequestBody CountInput input) {
    return repo.count(input);
}

MongoDB Aggregation

import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;

@GetMapping("/")
public QueryOutput<DataView> getAll(@Valid QueryInput input) {
    return repo.findAll(DataView.class,
        input,
        // just provide your aggregation pipeline here
        lookup("user", "userId", "id", "user"),
        unwind("user"),
        project()
            .and("user.sex").as("user.gender")
            .andInclude(
                "timestamp", "createTime", "sensorType",
                "batchId", "source", "beginTime",
                "endTime", "text", "url", "value")
            );
}

More details on QueryInput

A more detailed document is here, and with 中文版.

Future Plans

In the near future:

  • More complicated tests (and verifications)

Known Issues

  • $match, $sum: 1, $limit and $skip are attached to given aggregation pipeline so in some cases the logic may be broken.
  • Text search is simply converted to Regular Expressions with Literal flag and may contain some logical flaws.
  • Querydsl support may be missing or error-prone, as my project does not use it.