Как фильтровать данные, используя атрибуты класса с MongoTemplate?

Я использую MongoTemplate впервые. Используя Postman, я запускаю API и передаю JSON как @RequestBody, чтобы получить отфильтрованный список Student.

Модельный класс

Student.java

package bd.ac.seu.erp.criteriademo.model;

import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;

import javax.validation.constraints.NotNull;

@Data
@NoArgsConstructor
@EqualsAndHashCode
public class Student {
    @Id
    String studentId;

    @NotNull
    Name name;

    String nationality;

}

Name.java

package bd.ac.seu.erp.criteriademo.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Name {
    private String firstName;
    private String lastName;
}

контроллер

StudentController.java

package bd.ac.seu.erp.criteriademo.Controller;

import bd.ac.seu.erp.criteriademo.Service.StudentService;
import bd.ac.seu.erp.criteriademo.model.Student;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.List;

@CrossOrigin()
@RestController
public class StudentController {
    StudentService studentService;

    public StudentController(StudentService studentService) {
        this.studentService = studentService;
    }

    @PostMapping("student/filter")
    public ResponseEntity<?> getFilteredList(@RequestBody HashMap<String, String> fieldValueMap) {
        System.out.println("Something");

        MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("requestedBy", "Shamin Asfaq");

        List<Student> studentList = studentService.applyFilter(fieldValueMap);
        return new ResponseEntity<>(studentList, multiValueMap, HttpStatus.OK);
    }
}

обслуживание

StudentService.java

package bd.ac.seu.erp.criteriademo.Service;

import bd.ac.seu.erp.criteriademo.model.Student;
import com.mongodb.MongoClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

@Service
public class StudentService {

    @Value("${spring.data.mongodb.database}")
    String databaseName;

    public List<Student> applyFilter(HashMap<String,String> fieldValueMap) {

        MongoTemplate mongoTemplate = new MongoTemplate(new MongoClient("127.0.0.1"), databaseName);

        Query query = new Query();

        for(Map.Entry<String,String> keyValuePair: fieldValueMap.entrySet()) {
            query.addCriteria(Criteria.where(keyValuePair.getKey()).is(keyValuePair.getValue()));
        }

        return mongoTemplate.find(query, Student.class);
    }
}

Когда я запускаю API через Postman, обычно это то, что я посылаю как @RequestBody:

{
    "nationality":"Bangladeshi"
}

... и это работает просто отлично. Проблема начинается, когда я пытаюсь фильтровать данные, используя атрибут "firstName" или "lastName".

Это то, что я пытался отправить:

{
    "name": {
        "firstName": "Shamin"
    }
}

Возвращение, которое я получаю от Почтальона:

{
    "timestamp": "2018-08-09T12:01:58.620+0000",
    "status": 400,
    "error": "Bad Request",
    "message": "JSON parse error: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.lang.String` out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 2, column: 10] (through reference chain: java.util.HashMap[\"name\"])",
    "path": "/student/filter"
}

Ясно, что я понял, что бэкэнд рассматривает "имя" как ключ, а остальная часть - как значение.

Как я могу отфильтровать данные, используя атрибут "firstName" или "lastName"? Чтобы быть более общим, как я могу фильтровать данные, используя атрибуты класса или атрибуты класса в другом классе?

1 ответ

Решение

Ваша карта не десериализована должным образом в контроллере остальных. вместо использования HashMap.

Попробуйте использовать класс Student в качестве тела запроса или используйте что-то вроде com.fasterxml.jackson.databind.node.ObjectNode и прочитайте значения.

public ResponseEntity<?> getFilteredList(@RequestBody ObjectNode req)

или измените Json на:

{
    "name.firstname":"aaa"
}

Для ваших нужд, я думаю, вы должны посмотреть на QueryDsl, это очень хорошо, и вы можете выполнять запросы с GET запросом student/filter? Name.firstname=xxx, у вас есть хорошая ссылка с примерами

Вам нужно только расширить QuerydslPredicateExecutor в StudentRepository:

import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface StudentRepository extends PagingAndSortingRepository<Student, String>, QuerydslPredicateExecutor<Student> {
}

а затем просто запрос с использованием предиката:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.function.Predicate;

@CrossOrigin()
@RestController
public class StudentController {
    StudentRepository studentService;

    public StudentController(StudentRepository studentService) {
        this.studentService = studentService;
    }

    @GetMapping("student/filter")
    public ResponseEntity<Page<Student>> getFilteredList(@QuerydslPredicate(root = Student.class) Predicate predicate, Pageable pageable) {
        System.out.println("Something");

        MultiValueMap<String, String> multiValueMap = new LinkedMultiValueMap<>();
        multiValueMap.add("requestedBy", "Shamin Asfaq");

        Page<Student> studentList = studentService.findAll(predicate, pageable);
        return new ResponseEntity<>(studentList, multiValueMap, HttpStatus.OK);
    }
}

вам также нужно добавить этот плагин сборки в ваш pom.xml:

        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
            <version>${apt.version}</version>
            <dependencies>
                <dependency>
                    <groupId>com.querydsl</groupId>
                    <artifactId>querydsl-apt</artifactId>
                    <version>${querydsl.version}</version>
                </dependency>
            </dependencies>
            <executions>
                <execution>
                    <phase>generate-sources</phase>
                    <goals>
                        <goal>process</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>target/generated-sources/annotations</outputDirectory>
                        <processor>org.springframework.data.mongodb.repository.support.MongoAnnotationProcessor</processor>
                        <logOnlyOnError>true</logOnlyOnError>
                    </configuration>
                </execution>
            </executions>
        </plugin>
Другие вопросы по тегам