Spring Data JPA之动态查询

Spring Data JPA之动态查询在日常工作中,SpringDataJPA的使用给我们带来了极大的方便,但是实际业务中很多场景需要支持动态查询。比如前端查询功能提供了很多查询条件,用户可以根据一部分条件进行查询,那么后端就需要支持可配置的查询服务。在使用mybatis等时,可以用动态查询的方式轻松搞定,但是对于初次使用SpringDataJPA的小白来说,着实有些困难。本人也是在实际工作中参考资料,一步步摸索,掌握了一定的…

大家好,欢迎来到IT知识分享网。

在日常工作中,Spring Data JPA的使用给我们带来了极大的方便,但是实际业务中很多场景需要支持动态查询。比如前端查询功能提供了很多查询条件,用户可以根据一部分条件进行查询,那么后端就需要支持可配置的查询服务。在使用mybatis等时,可以用动态查询的方式轻松搞定,但是对于初次使用Spring Data JPA的小白来说,着实有些困难。本人也是在实际工作中参考资料,一步步摸索,掌握了一定的方法,现跟大家分享。

通过了解发现,Spring Data JPA是支持动态查询的,需要我们的repo继承JpaSpecificationExecutor接口,使用的时候传入相应参数即可。先看一下JpaSpecificationExecutor接口:

public interface JpaSpecificationExecutor<T> {
	
	T findOne(Specification<T> spec);
	List<T> findAll(Specification<T> spec);
	Page<T> findAll(Specification<T> spec, Pageable pageable);
	List<T> findAll(Specification<T> spec, Sort sort);
	long count(Specification<T> spec);
}

 其中,Pageable是分页查询用的,Sort是排序用的,比较简单,先不做介绍,本次主要介绍Specification。先看看它的定义:

public interface Specification<T> {

	Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb);
}

Root是查询的根对象,CriteriaBuilder可以用来构建查询关系。下面以一个实例来介绍具体使用:

下面是一个保存学生信息的表t_student:

Spring Data JPA之动态查询

先构建一下查询条件的参数体:

public class StudentParam {
    private String name;   //姓名
    private String address;   //住址
    private String school;   //学校
    private String birthday;   //出生日期

    //省略get、set方法。
}

查询条件包含姓名、住址、学校、出生日期等。

看一下repo接口:

public interface StudentRepo extends CrudRepository<StudentEntity, String>, JpaSpecificationExecutor,
        PagingAndSortingRepository<StudentEntity, String> {
}

业务实现代码:

public List<Student> queryStudent(final StudentParam studentParam) {
        Specification<StudentEntity> specification = new Specification<StudentEntity>() {
            public Predicate toPredicate(Root<StudentEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (studentParam.getName() != null && !"".equals(studentParam.getName())) {
                    predicates.add(cb.equal(root.<String>get("name"), studentParam.getName()));
                }
                if (studentParam.getStartDate() != null && !"".equals(studentParam.getStartDate())) {
                    predicates.add(cb.greaterThan(root.<String>get("birthday"), studentParam.getStartDate()));
                }
                if (studentParam.getEndDate() != null && !"".equals(studentParam.getEndDate())) {
                    predicates.add(cb.lessThan(root.<String>get("birthday"), studentParam.getEndDate()));
                }
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        List<StudentEntity> studentEntities = studentRepo.findAll(specification);
        List<Student> students = new ArrayList<Student>();
        for (StudentEntity studentEntity : studentEntities) {
            students.add(studentCopier.copy(studentEntity));
        }
        return students;
    }

说明:以其中一行代码:predicates.add(cb.equal(root.<String>get(“name”), studentParam.getName()));为例。”name”指定了实体的其中一个属性,studentParam.getName()是传入的姓名参数值,equal指的是等于。此段代码的含义是where条件,如:name=’张三’。注意指定的属性名必须与查询实体的属性名一致,否则会报错。

测试代码:

@Test
    public void queryStudentTest() {
        StudentParam studentParam = new StudentParam();
        studentParam.setName("张三");
        studentParam.setStartDate("2001-03-01");
        studentParam.setEndDate("2002-03-01");
        List<Student> students = studentService.queryStudent(studentParam);
        for (Student student : students) {
            info(student.toString());
        }
    }

执行结果: 

Hibernate: 
    select
        studentent0_.id as id1_0_,
        studentent0_.address as address2_0_,
        studentent0_.birthday as birthday3_0_,
        studentent0_.name as name4_0_,
        studentent0_.school as school5_0_,
        studentent0_.tel as tel6_0_ 
    from
        t_student studentent0_ 
    where
        studentent0_.name=? 
        and studentent0_.birthday>? 
        and studentent0_.birthday<?
Student{id='b6ef1f42e8a64367abc251af22246870', name='张三', address='菜市场', tel='12314512312311', school='西城中学', birthday='2001-03-11'}
Student{id='bc56bad3fd4e4604abe8a1c4ee2c1f66', name='张三', address='菜市场', tel='12314512312311', school='明湖中学', birthday='2002-02-11'}
Student{id='ffe6c2942c574d118fcfc4836580791d', name='张三', address='菜市场', tel='12314512312311', school='实验中学', birthday='2002-01-11'}

在执行中,我们发现Spring Data JPA根据配置的查询条件自动生成了条件语句:where
        studentent0_.name=?
        and studentent0_.birthday>?
        and studentent0_.birthday<?

上例中,将查询条件通过and关联起来,进一步,如果我们需要用的or,该如何实现?看下面:

public List<Student> queryStudent(final StudentParam studentParam) {
        Specification<StudentEntity> specification = new Specification<StudentEntity>() {
            public Predicate toPredicate(Root<StudentEntity> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<Predicate>();
                if (studentParam.getName() != null && !"".equals(studentParam.getName())) {
                    predicates.add(cb.equal(root.<String>get("name"), studentParam.getName()));
                }
                if (studentParam.getStartDate() != null && !"".equals(studentParam.getStartDate())) {
                    predicates.add(cb.greaterThan(root.<String>get("birthday"), studentParam.getStartDate()));
                }
                if (studentParam.getEndDate() != null && !"".equals(studentParam.getEndDate())) {
                    predicates.add(cb.lessThan(root.<String>get("birthday"), studentParam.getEndDate()));
                }
                //定义or的条件数组
                List<Predicate> orPredicates = new ArrayList<Predicate>();
                if (studentParam.getSchool() != null && !"".equals(studentParam.getSchool())) {
                    orPredicates.add(cb.equal(root.<String>get("school"),  studentParam.getSchool()));
                }
                if (studentParam.getAddress() != null && !"".equals(studentParam.getAddress())) {
                    orPredicates.add(cb.equal(root.<String>get("address"), studentParam.getAddress()));
                }
                //生成or的查询表达式
                Predicate orPredicate = cb.or(orPredicates.toArray(new Predicate[orPredicates.size()]));
                //与and结合起来
                predicates.add(orPredicate);
                return cb.and(predicates.toArray(new Predicate[predicates.size()]));
            }
        };
        List<StudentEntity> studentEntities = studentRepo.findAll(specification);
        List<Student> students = new ArrayList<Student>();
        for (StudentEntity studentEntity : studentEntities) {
            students.add(studentCopier.copy(studentEntity));
        }
        return students;
    }

测试代码:

@Test
    public void queryStudentTest() {
        StudentParam studentParam = new StudentParam();
        studentParam.setName("张三");
        studentParam.setStartDate("2001-03-01");
        studentParam.setEndDate("2002-03-01");
        studentParam.setAddress("清江小区");
        studentParam.setSchool("西城中学");
        List<Student> students = studentService.queryStudent(studentParam);
        for (Student student : students) {
            info(student.toString());
        }
    }

执行结果:

Hibernate: 
    select
        studentent0_.id as id1_0_,
        studentent0_.address as address2_0_,
        studentent0_.birthday as birthday3_0_,
        studentent0_.name as name4_0_,
        studentent0_.school as school5_0_,
        studentent0_.tel as tel6_0_ 
    from
        t_student studentent0_ 
    where
        studentent0_.name=? 
        and studentent0_.birthday>? 
        and studentent0_.birthday<? 
        and (
            studentent0_.school=? 
            or studentent0_.address=?
        )
Student{id='b6ef1f42e8a64367abc251af22246870', name='张三', address='菜市场', tel='12314512312311', school='西城中学', birthday='2001-03-11'}

Spring Data JPA 支持的动态查询也非常强大,实际工作中可以根据情况灵活配置。希望我的分享能给需要的朋友一点帮助,不足之处,敬请斧正。

 

 

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/10190.html

(0)
上一篇 2024-03-31 18:15
下一篇 2024-04-06 08:33

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

关注微信