Java Spring REST API Handling Many Optional Parameters

前端 未结 3 654
执念已碎
执念已碎 2021-01-05 10:18

I\'m currently messing around with a Spring Boot REST API project for instructional purposes. I have a rather large table with 22 columns loaded into a MySQL database and am

相关标签:
3条回答
  • 2021-01-05 11:09

    You can do this easily with a JpaSpecificationExecutor and a custom Specification: https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/

    I would replace the HashMap with a DTO containing all optional get params, then build the specifications based on that DTO, obviously you can also keep the HashMap and build the specification based on it.

    Basically:

    public class VehicleFilter implements Specification<Vehicle>
    {
        private String art;
        private String userId;
        private String vehicle;
        private String identifier;
    
        @Override
        public Predicate toPredicate(Root<Vehicle> root, CriteriaQuery<?> query, CriteriaBuilder cb)
        {
            ArrayList<Predicate> predicates = new ArrayList<>();
    
            if (StringUtils.isNotBlank(art))
            {
                predicates.add(cb.equal(root.get("art"), art));
            }
            if (StringUtils.isNotBlank(userId))
            {
                predicates.add(cb.equal(root.get("userId"), userId));
            }
            if (StringUtils.isNotBlank(vehicle))
            {
                predicates.add(cb.equal(root.get("vehicle"), vehicle));
            }
            if (StringUtils.isNotBlank(identifier))
            {
                predicates.add(cb.equal(root.get("identifier"), fab));
            }
    
            return predicates.size() <= 0 ? null : cb.and(predicates.toArray(new Predicate[predicates.size()]));
        }
    
    // getter & setter
    }
    

    And the controller:

    @RequestMapping(value = "/{ticket}/count", method = RequestMethod.GET)
    public long getItemsCount(
        @PathVariable String ticket,
        VehicleFilter filter,
        HttpServletRequest request
    ) throws Exception
    {
        return vehicleService.getCount(filter);
    }
    

    Service:

    @Override
    public long getCount(VehicleFilter filter)
    {
        return vehicleRepository.count(filter);
    }
    

    Repository:

    @Repository
    public interface VehicleRepository extends JpaRepository<Vehicle, Integer>, JpaSpecificationExecutor<Vehicle>
    {
    }
    

    Just a quick example adapted from company code, you get the idea!

    0 讨论(0)
  • 2021-01-05 11:10

    You can do it even more easily using Query By Example (QBE) technique if your repository class implements JpaRepository interface as that interface implements QueryByExampleExecutor interface which provides findAll method that takes object of Example<T> as an argument.

    Using this approach is really applicable for your scenario as your entity has a lot of fields and you want user to be able to get those which are matching filter represented as subset of entity's fields with their corresponding values that have to be matched.

    Let's say the entity is User (like in your example) and you want to create endpoint for fetching users whose attribute values are equal to the ones which are specified. That could be accomplished with the following code:

    Entity class:

    @Entity
    public class User implements Serializable {
        private Long id;
        private String firstName;
        private String lastName;
        private Integer age;
        private String city;
        private String state;
        private String zipCode;
    }
    

    Controller class:

    @Controller
    public class UserController {
        private UserRepository repository;
        private UserController(UserRepository repository) {
            this.repository = repository;
        }
    
        @GetMapping
        public List<User> getMatchingUsers(@RequestBody User userFilter) {
            return repository.findAll(Example.of(userFilter));
        }
    }
    

    Repository class:

    @Repository
    public class UserRepository implements JpaRepository<User, Integer> {
    }
    
    0 讨论(0)
  • 2021-01-05 11:23

    Another solution with less coding would be to use QueryDsl integration with Spring MVC.

    By using this approach all your request parameters will be automatically resolved to one of your domain properties and appended to your query.

    For reference check the documentation https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support and the example project https://github.com/spring-projects/spring-data-examples/tree/master/web/querydsl

    0 讨论(0)
提交回复
热议问题