美文网首页
Spring + MyBatis构建REST简单查询语言(一)

Spring + MyBatis构建REST简单查询语言(一)

作者: 鱼头三 | 来源:发表于2019-12-03 23:28 被阅读0次

    构建REST简单查询语言(一)

    本系列文章起源于我在实际项目中遇到的问题,思路来源于网上的博客,可视为对该系列博客的思想继承。如有兴趣可访问原博客。

    1. 什么是REST查询语言

    如今REST接口大行其道,我们所构建的http接口也都是REST。看过RESTful接口介绍的,会觉得这种接口十分简单,语义也比较清楚。然而在实际的工作中,会发现网上的介绍类文章说的还是太浅了,我们所遇到的需求,要比教科书上复杂的多。

    查询是其中具有代表性的一种。

    按照RESTful的定义来说,GET请求代表查询资源,比如我们要查询一个名为张三的用户,那么RESTful风格的表述可以是GET /users?name=张三 。是不是清晰简单?

    然而实际的情况要复杂的多。

    在我们的系统中,可能有100个名叫张三的用户,而实际我们只需要年龄在20至30之间,家住上海的那些。在这种情况下,简单的用多个field进行拼接难以满足需求。因此,我们需要一个查询表达式来面对复杂的查询过滤需求。

    2. 构建一个简单的查询表达式

    所以这个系列的目标是构建一个简单的查询表达式,便于映射到SQL查询条件。

    定义一个简单的表达式,其要素为三个

    • 查询字段 key
    • 操作 operation
    • 查询参数 value

    如下表达式,name:张三,age>20,age<30,city:上海代表查询系统中名叫张三,年龄在20至30之间,家住上海的用户。可以发现,这种方式更加灵活,并且有很好的扩展性。

    3. 定义实体类

    首先,我们来定义一个简单的User实体。

    @Table(name = "t_user")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long   id;
        private String firstName;
        private String lastName;
        private String email;
        private int    age;
            // getter,setter及构造方法
    }
    

    4. 使用ExampleBuilder映射REST查询到数据库查询

    接下来我们将面对最核心的问题:在数据持久化层该怎么构建查询。

    其实也可以将问题转换为:如何将自定义的查询表达式表示为SQL语句。

    下面的代码就是在DAO层将查询表达式构建为MyBatis的Example,有赖于这种面向对象构建查询条件的方式,能让我们更方便的实现功能。

    在我看的博客中,作者使用的是Spring JPA,并且使用CriteriaBuilder构建查询语句。而我的项目基本都是使用Mybatis,考虑到ORM层的切换成本,在本系列中我将使用MybatisExample构建SQL查询语句。

    public class UserDAO implements IUserDAO {
    
        private UserMapper userMapper;
    
        @Override
        public List<User> searchUser(List<SearchCriteria> params) {
    
            Example example = new Example(User.class);
            UserSearchQueryCriteriaConsumer searchConsumer = new UserSearchQueryCriteriaConsumer(example);
            params.forEach(searchConsumer);
    
            return userMapper.selectByExample(example);
        }
    

    SearchQueryCriteriaConsumer类中,我们实际就是将表达式构建为一个一个的Criteria,然后用and()方法将他们连接起来。

    大家应该发现了我们现在构建的REST查询语言只能表示AND关系。关于如何实现OR关系查询我将在接下来的文章中介绍。

    public class SearchQueryCriteriaConsumer implements Consumer<SearchCriteria> {
    
        @NonNull
        private Example example;
    
        @Override
        public void accept(SearchCriteria param) {
            Example.Criteria criteria = example.createCriteria();
            if (param.getOperation().equalsIgnoreCase(">")) {
                criteria.andGreaterThan(param.getKey(), param.getValue());
            } else if (param.getOperation().equalsIgnoreCase("<")) {
                criteria.andLessThan(param.getKey(), param.getValue());
            } else if (param.getOperation().equalsIgnoreCase(":")) {
                criteria.andEqualTo(param.getKey(), param.getValue());
            }
            example.and(criteria);
        }
    }
    

    使用Example构建查询语句的操作很简单,主要依赖于SearchCriteria这个类。我们在其中将查询条件转换为三个属性:

    public class SearchCriteria {
        /**
         * 代表field name
         */
        private String key;
        /**
         * 代表执行操作
         */
        private String operation;
        /**
         * 代表field value
         */
        private Object value;
    

    Controller层,通过正则表达式来提取表达式并创建SearchCriteria类。

    public class UserController {
    
        @Autowired
        private IUserDAO userDAO;
    
        @GetMapping
        public @ResponseBody List<User> searchUser(@RequestParam("q") String query){
    
            List<SearchCriteria> params = new ArrayList<>();
            if (query != null) {
                Pattern pattern = Pattern.compile("(\\w+?)(:|<|>)(\\w+?),");
                Matcher matcher = pattern.matcher(query + ",");
                while (matcher.find()) {
                    params.add(new SearchCriteria(matcher.group(1), matcher.group(2), matcher.group(3)));
                }
            }
    
            userDAO.searchUser(params);
    
            return userDAO.searchUser(params);
        }
    
    }
    

    To Be Continue...

    【参考资料】

    REST Query Language with Spring and JPA Criteria

    相关文章

      网友评论

          本文标题:Spring + MyBatis构建REST简单查询语言(一)

          本文链接:https://www.haomeiwen.com/subject/jetkgctx.html