简体   繁体   English

Java Spring REST API处理许多可选参数

[英]Java Spring REST API Handling Many Optional Parameters

I'm currently messing around with a Spring Boot REST API project for instructional purposes. 我目前正忙着使用Spring Boot REST API项目进行教学。 I have a rather large table with 22 columns loaded into a MySQL database and am trying to give the user the ability to filter the results by multiple columns (let's say 6 for the purposes of this example). 我有一个相当大的表,其中22列被加载到MySQL数据库中,我试图让用户能够按多列过滤结果(假设这个例子的目的是6)。

I am currently extending a Repository and have initialized methods such as findByParam1 and findByParam2 and findByParam1OrderByParam2Desc and etc. and have verified that they are working as intended. 我目前正在扩展一个Repository并初始化了一些方法,例如findByParam1和findByParam2以及findByParam1OrderByParam2Desc等,并且已经验证它们是按预期工作的。 My question to you guys is the best way to approach allowing the user the ability to leverage all 6 optional RequestParams without writing a ridiculous amount of conditionals/repository method variants. 我向你们提出的问题是最好的方法,允许用户能够利用所有6个可选的RequestParams,而无需编写大量的条件/存储库方法变体。 For example, I want to give the user the ability to hit url home/get-data/ to get all results, home/get-data?param1=xx to filter based on param1, and potentially, home/get-data?param1=xx&param2=yy...&param6=zz to filter on all the optional parameters. 例如,我想让用户能够点击url home / get-data /获取所有结果,home / get-data?param1 = xx根据param1进行过滤,可能还有home / get-data?param1 = xx&param2 = yy ...&param6 = zz来过滤所有可选参数。

For reference, here is what the relevant chunk of my controller looks like (roughly). 作为参考,这是我的控制器的相关块(粗略地)。

@RequestMapping(value = "/get-data", method = RequestMethod.GET)
public List<SomeEntity> getData(@RequestParam Map<String, String> params) {
    String p1 = params.get("param1");
    if(p1 != null) {
        return this.someRepository.findByParam1(p1);
    }
    return this.someRepository.findAll();
}

My issue so far is that the way I am proceeding about this means that I will basically need n! 到目前为止我的问题是我正在进行的这种方式意味着我基本上需要n! amount of methods in my repository to support this functionality with n equalling the amount of fields/columns I want to filter on. 我的存储库中用于支持此功能的方法数量,n等于我要过滤的字段/列数量。 Is there a better way to approach handling this, perhaps where I am filtering the repository 'in-place' so I can simply filter 'in-place' as I check the Map to see what filters the user did indeed populate? 有没有更好的方法来处理这个问题,也许我正在过滤存储库'就地',所以我可以简单地过滤'就地',因为我检查地图以查看用户确实填充了哪些过滤器?

EDIT: So I'm currently implementing a 'hacky' solution that might be related to J. West's comment below. 编辑:所以我正在实施一个'hacky'解决方案,可能与J. West的评论有关。 I assume that the user will be specifying all n parameters in the request URL and if they do not (for example, they specify p1-p4 but not p5 and p6) I generate SQL that just matches the statement to LIKE '%' for the non-included params. 我假设用户将在请求URL中指定所有n个参数,如果它们没有(例如,它们指定p1-p4但不指定p5和p6),我生成的SQL恰好匹配LIKE'%'的语句非包含的参数。 It would look something like... 它看起来像......

@Query("select u from User u where u.p1 = :p1 and u.p2 = :p2 ... and u.p6 = :p6") 
List<User> findWithComplicatedQueryAndSuch;

and in the Controller, I would detect if p5 and p6 were null in the Map and if so, simply change them to the String '%'. 在Controller中,我会检测地图中p5和p6是否为空,如果是,则只需将它们更改为字符串'%'即可。 I'm sure there is a more precise and intuitive way to do this, although I haven't been able to find anything of the sort yet. 我确信有更精确和直观的方法来做到这一点,虽然我还没有找到任何类型的东西。

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/ 您可以使用JpaSpecificationExecutor和自定义Specification轻松完成此操作: httpsJpaSpecificationExecutor

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. 我会用包含所有可选get参数的DTO替换HashMap,然后根据该DTO构建规范,显然你也可以保留HashMap并基于它构建规范。

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! 只是一个根据公司代码改编的简单示例,您就会明白!

Another solution with less coding would be to use QueryDsl integration with Spring MVC. 编码较少的另一种解决方案是使用QueryDsl与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 有关参考,请参阅文档https://spring.io/blog/2015/09/04/what-s-new-in-spring-data-release-gosling#querydsl-web-support和示例项目https:// github.com/spring-projects/spring-data-examples/tree/master/web/querydsl

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. 如果您的存储库类实现JpaRepository接口,则可以使用按示例查询(QBE)技术更轻松地执行操作,因为该接口实现了QueryByExampleExecutor接口,该接口提供了将Example <T>的对象作为参数的findAll方法。

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. 假设该实体是User (就像您的示例中一样),并且您希望创建端点以获取其属性值等于指定值的用户。 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> {
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM