1

I'm new to JPA. I have a class like this

@Table(name="student")
@Entity
public class Student{
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int studentId;
    
    @Column
    @Size(max = 20)
    private String name;
    
    @Column
    @Min(value = 2014)
    @Max(value = 2020)
    private int yearOfBirth;
    
    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "classroomId")
    Classroom classroom;

    //Getters and setters
    }

My repository:

        @Repository
        public interface HocSinhRepository extends JpaRepository<Student, Integer> {}

My controller:

public class StudentController {
        @Autowired
        StudentRepository studentRepository;
        
        @GetMapping(value = "/get")
        public Page<Student> get(@RequestParam Optional<Integer> page, @RequestParam Optional<String> sortBy) {
            return studentRepository.findAll(
                    PageRequest.of(page.orElse(0), 3, Sort.Direction.ASC, sortBy.orElse("name"))
                    );
        }
    }

By using Optional.orElse, I can assign a default value to the sortBy parameter if it's null. How can I do the same thing if the parameter is not null, but just a non-sensible string (like "asdasd")?

Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
Anh Đỗ
  • 19
  • 3

4 Answers4

2

You can use the map operator of optionals.

So it would be

sortBy.map(o -> {
           if (o.equalsIgnoreCase("asdasd")){
               return "name";
           } else {
               return o;
           }
       }).orElse("name"));

This will make the optional return the value "name" when it is empty or when the containing value exists and is with ignore case "asdasd". In every other case it will return the original value it contained.

So your code will be adapted to be

return studentRepository.findAll(
                    PageRequest.of(page.orElse(0), 3, Sort.Direction.ASC, 
                                   sortBy.map(o -> {
                                              if (o.equalsIgnoreCase("asdasd")){
                                                   return "name";
                                              } else {
                                                   return o;
                                              }}).orElse("name"));
                   ));
Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
1

variation on @xerx593's VALID_SORT_FIELDS and making spring's @RequestParam optional

    public static final Set<String> VALID_SORT_FIELDS = Set.of("name", "studentId");
    
    @GetMapping(value = "/get")
    public Page<Student> get(
            @RequestParam(required = false, defaultValue = "0") Integer page,
            @RequestParam(required = false, defaultValue = "name") String sortBy) {
        
        String sortByOrDefault = VALID_SORT_FIELDS.contains(sortBy) ? sortBy : "name";
        return studentRepository.findAll(PageRequest.of(page, 3, Sort.Direction.ASC, sortByOrDefault));
    
    }
indybee
  • 1,507
  • 13
  • 17
  • I notice that making @RequestParam optional is the same as making the parameter type Optional. Is there a distinguishable advantage to either? – Anh Đỗ Dec 24 '21 at 03:35
  • @AnhĐỗ i think Optional is usually [used with return types rather than method arguments](https://stackoverflow.com/questions/31922866/why-should-java-8s-optional-not-be-used-in-arguments) – indybee Dec 24 '21 at 13:26
1

another variation:

    @GetMapping(value = "/get")
    public Page<Student> get(
            @RequestParam(required = false, defaultValue = "0") Integer page,
            @RequestParam(required = false, defaultValue = "name") String sortBy) {

        String sortByOrDefault = SortConfig.STUDENTS.getSortableFields().contains(sortBy) 
                ? sortBy
                : SortConfig.STUDENTS.getDefaultSortField();
        return studentRepository.findAll(PageRequest.of(page, 3, Sort.Direction.ASC, sortByOrDefault));

    }

    public enum SortConfig {
        STUDENTS(List.of("name", "studentId")), 
        ANOTHER_ENTITY(List.of("field1", "field2"));

        private String defaultSortField;
        private List<String> sortableFields;

        private SortConfig(List<String> sortableFields) {
            this.sortableFields = sortableFields;
        }

        public String getDefaultSortField() {
            return defaultSortField;
        }

        public List<String> getSortableFields() {
            return sortableFields;
        }

    }
indybee
  • 1,507
  • 13
  • 17
0

A common scenario would be:

To have the "valid field names" stored in a "data structure", e.g., like:

public static final Set<String> VALID_SORT_FIELDS = Set.of("name", "studentId"/*, all we "consider valid"...*/);

Then we can use it in our controller like:

@GetMapping(value = "/get")
public Page<Student> get(@RequestParam Optional<Integer> page, @RequestParam Optional<String> sortBy) {
  final String sort = sortBy
  .map( s -> 
      s != null && VALID_SORT_FIELDS.contains(s)?
          s : "name" // or a consatant com.domain.my...DEFAULT_SORT
  )
  .get();
  return studentRepository.findAll(
     PageRequest.of(page.orElse(0), 3, Sort.Direction.ASC, sort)
  );
}

When we want to ignore the case, we would adjust to:

...&&  VALID_SORT_FIELDS.contains(s.toLowerCase(/*some (default) Locale*/))

Another/additional possible approach: validaion-api... but the wrapping Optional is a "challange"(??)...

xerx593
  • 12,237
  • 5
  • 33
  • 64