SpringDataJPA之Specification复杂查询实战

yizhihongxing

下面详细讲解“SpringDataJPA之Specification复杂查询实战”的完整攻略。

一、什么是Specification

Specification(规范)是Spring Data JPA提供的一种查询定义方式,它可以让我们通过编写Java代码构造查询,从而实现类似HQL的灵活嵌入查询的功能。Specification提供了查询复杂条件时的灵活性,可以动态生成查询条件,可读性高,易于维护。

二、Specification的使用场景

Specification常用于以下场景中:

  • 混合查询(多个查询条件);
  • 复杂查询(多表查询、嵌套查询等);
  • 动态查询(查询条件不固定)。

三、Specification的基本用法

Specification主要通过CriteriaQuery来实现,CriteriaQuery表示了一个查询对象,可以通过CriteriaBuilder来创建查询条件,CriteriaBuilder是CriteriaQuery的工厂类。

1、创建Specification

我们可以通过Specification的匿名内部类来创建一个Specification对象,该对象包含了查询条件的组装逻辑。例如:

public static Specification<User> getUserSpecification(String name, Integer age) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            Predicate predicate = cb.conjunction();
            if(StringUtils.isNotBlank(name)){
                predicate.getExpressions().add(cb.like(root.get("name"), "%" + name + "%"));
            }
            if(age != null){
                predicate.getExpressions().add(cb.equal(root.get("age"), age));
            }
            return predicate;
        }
    };
}

上述代码中,我们创建了一个getUserSpecification方法,该方法返回了一个Specification对象。在Specification对象中,我们使用了CriteriaBuilder来组织查询条件。在本例中,我们定义了两个查询条件,一个是模糊匹配名称,一个是精确匹配年龄。

2、使用Specification进行查询

使用Specification对象进行查询的方法主要有两种,分别为:

  • JpaSpecificationExecutor的findAll(Specification var1)方法;
  • JpaSpecificationExecutor的findAll(Specification var1, Pageable var2)方法。

其中,第一种方法可以用于普通查询,第二种方法则用于分页查询。

下面是一个使用Specification进行普通查询的示例代码:

@Autowired
private UserRepository userRepository;

public List<User> findUser(String name, Integer age) {
    Specification<User> specification = getUserSpecification(name, age);
    return userRepository.findAll(specification);
}

以上代码使用了getUserSpecification方法创建Specification对象,并调用了userRepository的findAll方法进行查询。

四、Specification的高级用法

除了上述的简单Query生成,Specification还支持许多其他功能,比如like查询、between查询、in查询、or查询、not查询等。下面将对这些方法进行逐一介绍:

1、like查询

下面是一个模糊查询的示例代码:

public static Specification<User> likeName(String name) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.like(root.get("name"), "%"+name+"%");
        }
    };
}

上述代码中,我们定义了一个方法用于模糊匹配。使用like方法,获取用户名称的关键字作为参数进行查询,并将查询结果以List的形式返回。

2、between查询

下面是一个根据年龄范围进行查询的示例代码:

public static Specification<User> betweenAge(int min, int max) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.between(root.get("age"), min, max);
        }
    };
}

上述代码中,我们定义了一个方法,该方法使用了CriteriaBuilder的between方法,以获取用户年龄在[min,max]之间的数据。

3、in查询

下面是一个根据多个名称进行匹配的示例代码:

public static Specification<User> nameIn(List<String> names) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return root.get("name").in(names);
        }
    };
}

上述代码中,我们定义了一个方法,该方法使用了CriteriaBuilder的in方法,以获取名称在指定集合中的用户数据。

4、or查询

下面是一个根据name或age进行匹配的示例代码:

public static Specification<User> orNameAndAge(String name, Integer age) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            Predicate predicate = cb.disjunction();
            if(StringUtils.isNotBlank(name)){
                predicate.getExpressions().add(cb.like(root.get("name"), "%" + name + "%"));
            }
            if(age != null){
                predicate.getExpressions().add(cb.equal(root.get("age"), age));
            }
            return predicate;
        }
    };
}

上述代码中,我们定义了一个方法,该方法使用了CriteriaBuilder的disjunction方法,以获取name或age匹配的用户数据。

5、not查询

下面是一个否定查询的示例代码:

public static Specification<User> notName(String name) {
    return new Specification<User>() {
        @Override
        public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            return cb.notEqual(root.get("name"), name);
        }
    };
}

上述代码中,我们定义了一个方法,该方法使用了CriteriaBuilder的notEqual方法,以获取除名称等于指定值之外的用户数据。

五、使用Specification实现复杂查询

下面我们以一个多表关联查询的场景为例,介绍如何使用Specification实现复杂查询。

在该场景中,我们需要查询班级信息,同时关联查询学生信息。对于多表查询操作,我们需要以下几步完成:

  • 定义实体类,并使用@OneToMany或@ManyToOne注解进行关联定义;
  • 确认关联表的名称,通过Root来获取表名;
  • 使用join或leftJoin方法或者subQuery方式来关联查询;
  • 在get方法上使用fetch方法或者subQuery方式来声明关联表。

下面是示例代码:

public static Specification<ClassInfo> queryByNameAndStuName(String className, String stuName) {
    return new Specification<ClassInfo>() {
        @Override
        public Predicate toPredicate(Root<ClassInfo> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
            List<Predicate> predicates = new ArrayList<>();
            if(StringUtils.isNotBlank(className)){
                predicates.add(cb.like(root.get("className"), "%" + className + "%"));
            }
            Join<ClassInfo, Student> stuJoin = root.join("students", JoinType.LEFT);
            if(StringUtils.isNotBlank(stuName)){
                predicates.add(cb.like(stuJoin.get("stuName"), "%" + stuName + "%"));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        }
    };
}

以上代码中,我们定义了一个以班级名称和学生名称为查询条件的Specification对象。在该对象中,我们希望能获取包含关键词的班级和学生信息。

首先,我们在查询中通过Root获取到了主表的表名。然后,我们使用了join方法,声明了主表和从表之间的关系,并指定了从表名称。在最后的查询条件中,我们通过多个and和like方式,获取到了符合条件的数据。

六、Specification的注意点

使用Specification时,需要注意以下点:

  1. 多个条件的组合查询时,使用and或or操作符进行组合;
  2. 获取关联表数据时,需要使用fetch方法或者subQuery方式来获取数据,避免n+1问题;
  3. 所有查询条件都需要由Predicate类型构成;
  4. Specification中可以嵌套Specification,并使用combine进行组合;
  5. 避免在Specification中写复杂的查询逻辑,尽量把条件简化,尤其是多表关联查询。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringDataJPA之Specification复杂查询实战 - Python技术站

(1)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Java的Struts框架报错“ActionForwardNotFoundException”的原因与解决办法

    当使用Java的Struts框架时,可能会遇到“ActionForwardNotFoundException”错误。这个错误通常由以下原因之一起: 配置错误:如果ActionForward配置不正确可能会出现。在这种情况下,需要检查ActionForward配置以解决此问题。 URL路径问题:如果URL路径不正确,则可能会出现此。在种情况下,需要检查URL路…

    Java 2023年5月5日
    00
  • SpringBoot整合mybatis-generator插件流程详细讲解

    下面是SpringBoot整合mybatis-generator插件的详细攻略,我们将分为以下几个步骤进行操作: 添加mybatis-generator插件依赖 配置mybatis-generator插件 配置生成代码的输出路径和文件名 自动生成代码 示例展示 1. 添加mybatis-generator插件依赖 首先,我们需要在项目中添加mybatis-g…

    Java 2023年5月20日
    00
  • springboot数据库密码加密的配置方法

    当我们在使用SpringBoot开发项目中,经常需要对数据库的密码进行加密,以保障密码信息的安全。下面是一份完整的攻略,讲解了使用SpringBoot 加密数据库密码的配置方法。 第一步:依赖 在pom.xml中添加如下模块依赖: <dependency> <groupId>com.ulisesbocchio</groupId&…

    Java 2023年5月19日
    00
  • Java MyBatis之Mapper代理详解

    Java MyBatis之Mapper代理详解 在MyBatis中,Mapper代理是一种方便且易于使用的方式来执行数据库操作。Mapper代理充当了DAO层与MyBatis框架之间的接口,从而将SQL语句执行的逻辑与业务逻辑分开。 1. Mapper代理的创建 Mapper代理是通过MyBatis框架自动生成的。MyBatis通过读取我们配置的Mapper…

    Java 2023年5月20日
    00
  • servlet实现文件上传、预览、下载、删除功能

    准备工作 首先,在编写servlet实现文件上传、预览、下载、删除功能之前,需要先进行准备工作,包括: 建立一个Web项目 引入相关的jar包,如commons-fileupload.jar、commons-io.jar等 编写一个用于上传文档的html页面 定义文件上传后保存的路径 上传文件 上传文件是整个功能的核心部分,下面是实现文件上传的步骤: 2.1…

    Java 2023年6月15日
    00
  • Java SimpleDateFormat中英文时间格式化转换详解

    下面是关于“Java SimpleDateFormat中英文时间格式化转换详解”的完整攻略: 1. 概述 在Java中,我们经常需要把日期或时间格式化成指定格式的字符串,或者将字符串转换为日期或时间。SimpleDateFormat类就是一个非常常用的类,它可以根据给定的日期时间格式模板将一个Date对象格式化为字符串,或将一个字符串解析为Date对象。 S…

    Java 2023年5月20日
    00
  • Java Fluent Mybatis 项目工程化与常规操作详解流程篇 下

    Java Fluent Mybatis 项目工程化与常规操作详解流程篇 Java Fluent Mybatis 是一个基于 Mybatis 的 fluent 动态 SQL 构建器,可以帮助我们快速生成复杂的 SQL 语句。下面我们将详细讲解 Java Fluent Mybatis 项目工程化与常规操作的流程。 一、创建项目 首先,我们需要创建一个 Maven…

    Java 2023年5月20日
    00
  • JSP登录中Session的用法实例详解

    JSP登录中Session的用法实例详解 什么是Session Session 是在服务器端存储用户信息的最常用的方式之一。它能够跨越不同的请求并在整个会话期间保持这些信息。Session 变量存储在服务器上,当用户浏览网站时,它们的信息会被传输到服务器进行处理并返回响应页面。在 Java 中可以使用 HttpSession 对象来操作 Session。 S…

    Java 2023年6月15日
    00
合作推广
合作推广
分享本页
返回顶部