JPA在不写sql的情况下如何实现模糊查询
作者:武梓龙_Wzill
文章介绍了在项目中实现模糊查询的几种方法,包括使用JPA的API、JPQL、QueryByExample和@Query注解,通过实现Specification接口和定义接口继承JpaRepository,可以方便地进行单字段和多字段的模糊查询,文章还提到了BINARY函数的使用以及查询结果的返回
背景介绍
在我们的项目中很多的业务都会设计模糊查询,例如按照姓氏去获取人员的信息,按照手机号的前三位去获取人员的信息等。
我们除了正常的手写模糊查询的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); } }
本文中实现的是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); }
3.在业务类中调用声明的接口
@Entity public List<User> selectToFuzzy(User userInfo){ List<User> userInfoList = userDao.findAll(userInfo); return userInfoList; }
4.在Controller中直接调用业务类中的方法即可
@RequestMapping(value="selectToFuzzy",method= RequestMethod.POST) //模糊查询用户的信息 public List<User> selectToFuzzy(@RequestBody User userInfo){ List<User> users = user.selectToFuzzy(userInfo); return users; }
执行结果
可以看到我们的入参都是对应字段值的一部分内容,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<Employee> example = Example.of(employee, matcher);
3.使用Spring Data JPA的@Query注解进行模糊查询
在Repository接口中使用@Query注解定义自定义的查询方法。
在查询方法中使用%通配符进行模糊匹配。
例如:
@Query("SELECT e FROM Employee e WHERE e.name LIKE %?1%") List<Employee> findByName(String name);
总结提升
根据自己的业务需求去选择使用哪一种模糊查询的方式。底层原理都是一样的。
JPA封装了一些公共的内容,我们开发的过程中使用起来就比较容易和简单。
但是我们在使用的过程中也要明白底层是如何实现,不能只停留在会使用的阶段中。知其然也要知其所以然。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。