• JPA在不写sql的情况下实现模糊查询


    本文已收录于专栏
    《Java》

    背景介绍

      在我们的项目中很多的业务都会设计模糊查询,例如按照姓氏去获取人员的信息,按照手机号的前三位去获取人员的信息等。我们除了正常的手写模糊查询的sql语句去获取信息之外,还可以使用JPA自带的API来实现任意字段的模糊查询。JPA已经给我们封装好了。当我们对模糊查询非常熟悉了之后直接拿来时候即可。

    概念说明

    单字段模糊匹配:

    说明:在一个字段中无论关键字出现在什么位置上,只要有关键词即可。
    场景:获取手机号开头为187的学生
    应用:SELECT*FROM table_name WHERE BINARY column_name LIKE'%keyword%';

    多字段模糊匹配:

    说明:在多个字段中无论关键字出现在什么位置上,只要每个字段有每个字段指定的关键词即可。
    场景:获取手机号开头为187并且姓氏为武的学生
    应用:SELECT*FROM table_name WHERE BINARY column1_name LIKE'%keyword1%'AND column2_name LIKE'%keyword2%';

      注:BINARY函数是开启大小写敏感的函数,底层逻辑是通过Ascii码的方式比较的。因为数据库默认是对大小写不敏感的,也就是我们在查询名称为wzl数据的时候,也会把名称为Wzl的数据也查询出来。

    实现过程

    代码实现

    1.写一个实体类去实现Specification接口,重写toPredicate方法

    import com.fasterxml.jackson.databind.annotation.JsonSerialize;
    import com.tfjybj.dao.UserDao;
    import com.tfjybj.utils.SnowflakeIdWorker;
    import lombok.Data;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.domain.Example;
    import org.springframework.data.jpa.domain.Specification;
    import org.springframework.stereotype.Service;
    import org.springframework.util.ObjectUtils;
    
    import javax.annotation.Resource;
    import javax.persistence.*;
    import javax.persistence.criteria.CriteriaBuilder;
    import javax.persistence.criteria.CriteriaQuery;
    import javax.persistence.criteria.Predicate;
    import javax.persistence.criteria.Root;
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @BelongsProject: incentive
     * @BelongsPackage: com.tfjybj.service
     * @Author: Wuzilong
     * @Description: 描述什么人干什么事儿
     * @CreateTime: 2023-08-28 14:48
     * @Version: 1.0
     */
    @Table
    @Entity
    @Service
    @Data
    public class User implements Specification<User> {
    
        @Id
        @JsonSerialize(using = com.fasterxml.jackson.databind.ser.std.ToStringSerializer.class)
        private Long id;
    
        private String account;
    
        private  String password;
    
        private String phone;
    
        private Date createTime;
    
        private Date updateTime;
    
        private Integer isDelete;
    
        @Resource
        @Transient
        private UserDao userDao;
    
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
            List<String> nonNullFields = new ArrayList<>();
            Field[] declaredFields = this.getClass().getDeclaredFields();
            for (Field field : declaredFields) {
                field.setAccessible(true);
                try {
                    Object value = field.get(this);
                    if (value != null) {
                        nonNullFields.add(field.getName());
                    }
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
    
            Predicate[] predicates = new Predicate[nonNullFields.size()+1];
    
            for (int i = 0; i < nonNullFields.size(); i++) {
                try {
                    predicates[i] = criteriaBuilder.like(root.get(nonNullFields.get(i)), "%" + this.getClass().getDeclaredField(nonNullFields.get(i)).get(this) + "%");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // 添加额外的条件,排除isdelete=1的数据
            predicates[nonNullFields.size()] = criteriaBuilder.notEqual(root.get("isDelete"), 1);
    
            return criteriaBuilder.and(predicates);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86

    本文中实现的是and方式的模糊查询,也可是使用or的方式进行模糊查询,也就是多个字段中都包含一个关键字。

    2.定义一个接口去继承JpaRepository接口,并指定返回的类型和参数类型

    @Entity
    import com.tfjybj.service.User;
    import org.springframework.data.jpa.domain.Specification;
    import org.springframework.data.jpa.repository.JpaRepository;
    import org.springframework.stereotype.Repository;
    
    import java.util.List;
    
    /**
     * @BelongsProject: incentive
     * @BelongsPackage: com.tfjybj.dao
     * @Author: Wuzilong
     * @Description: 描述什么人干什么事儿
     * @CreateTime: 2023-08-28 14:48
     * @Version: 1.0
     */
    @Repository
    public interface UserDao extends JpaRepository<User, Long> {
    
        List<User> findAll(Specification<User> userInfo);
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    3.在业务类中调用声明的接口

    @Entity
        public List<User> selectToFuzzy(User userInfo){
            List<User> userInfoList = userDao.findAll(userInfo);
            return userInfoList;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.在Controller中直接调用业务类中的方法即可

        @RequestMapping(value="selectToFuzzy",method= RequestMethod.POST)
        //模糊查询用户的信息
        public List<User> selectToFuzzy(@RequestBody User userInfo){
            List<User> users = user.selectToFuzzy(userInfo);
            return users;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    执行结果

    在这里插入图片描述

    在这里插入图片描述

      可以看到我们的入参都是对应字段值的一部分内容,phone字段传入的是187它会把phone字段中包含187的所有数据都返回回来。同样两个字段一起模糊查询也是一样。

    其他方式

    1.使用JPQL进行模糊查询:

    使用LIKE关键字结合通配符(%)进行模糊匹配。
    例如:SELECT e FROM Employee e WHERE e.name LIKE '%John%'

    2.使用Spring Data JPA的Query By Example进行模糊查询:

    创建一个实体对象作为查询条件,设置需要模糊匹配的属性值。
    例如:ExampleMatcher matcher = ExampleMatcher.matching().withMatcher("name", match -> match.contains()); Example example = Example.of(employee, matcher);

    3.使用Spring Data JPA的@Query注解进行模糊查询:

    在Repository接口中使用@Query注解定义自定义的查询方法。
    在查询方法中使用%通配符进行模糊匹配。
    例如:@Query("SELECT e FROM Employee e WHERE e.name LIKE %?1%") List findByName(String name);

    总结提升

      根据自己的业务需求去选择使用哪一种模糊查询的方式。底层原理都是一样的。JPA封装了一些公共的内容,我们开发的过程中使用起来就比较容易和简单。但是我们在使用的过程中也要明白底层是如何实现,不能只停留在会使用的阶段中。知其然也要知其所以然。


    🎯 此文章对你有用的话记得留言+点赞+收藏哦🎯
  • 相关阅读:
    面试系列MySql:索引优化
    基于Python flask 的豆瓣电影评分可视化,豆瓣电影评分预测系统
    Vue基础面试题11-19
    【广州华锐互动】VR建筑安全培训体验为建筑行业人才培养提供有力支持
    GIT特殊场景
    fabic.js Stickman应用》》元素关联设置/拓扑图
    国产手机芯片4G方案_紫光展锐安卓核心板虎贲4G智能模块方案定制
    研究人员发现 OpenAI ChatGPT、Google Gemini 的漏洞
    每日一博 - 图解进程(Process)和线程(Thread)区别联系
    Linux ALSA源码分析(基于Linux 5.18)
  • 原文地址:https://blog.csdn.net/weixin_45490198/article/details/132595968