• 使用Specification与Example方式实现动态条件查询案例


    目录

    Query by Example

    Query by Example分页查询基础讲解

    使用example进行的动态条件分页查询案例演示

    Specification分页查询

    Specifications分页查询基础知识

    使用Specification来进行动态条件分页查询案例演示

    Querydsl


    下面将介绍三种可以实现动态查询的方式,实际上它们各自都有自己的应用场景,每一种方法都有自己的局限

    Query by Example

    Query by Example分页查询基础讲解

    只支持查询:

    • 不支持嵌套或分组的属性约束,如 firstname = ?0 or (firstname = ?1 and lastname = ?2).

    • 只支持字符串】 start/contains/ends/regex 匹配和其他属性类型的精确匹配

    使用步骤:

    1、将Repository继承QueryByExampleExecutor

    1. public interface CustomerQBERepository extends PagingAndSortingRepository, QueryByExampleExecutor { //这个泛型表示的是你要操作的Java实体类
    2. }

    我们看一下这个querybyexampleexecutor的源码定义了哪些方法:

    1. public interface QueryByExampleExecutor {
    2. extends T> Optional findOne(Example example);
    3. extends T> Iterable findAll(Example example);
    4. extends T> Iterable findAll(Example example, Sort sort);
    5. extends T> Page findAll(Example example, Pageable pageable);
    6. extends T> long count(Example example);
    7. extends T> boolean exists(Example example);
    8. extends T, R> R findBy(Example example, Function, R> queryFunction);
    9. }

    方法演示:

    1. @Autowired
    2. CustomerQBERepository repository; //把刚刚自己定义的接口注入到容器
    3. //根据客户名称 客户地址动态查询
    4. @Test
    5. public void test01(){
    6. //构建实体类需要动态查询的条件 比如需要通过名字和地址进行动态查询
    7. Customer customer=new Customer();
    8. customer.setCustName("徐庶"); //在实际开发中这个查询的条件都是从前端传过来的,比如传了name和address过来,你需要使用对应的数据类型进行接收就行,这里是演示所以直接写死了查询条件
    9. customer.setCustAddress("BEIJING");
    10. // 通过Example构建查询条件 特别注意这个Example对象是spring data中的依赖,别引成了hibernate的
    11. Example example = Example.of(customer);
    12. List list = (List) repository.findAll(example);
    13. System.out.println(list);
    14. }
    15. /**
    16. * 通过匹配器进行条件的限制 客户名称 客户地址动态查询
    17. */
    18. @Test
    19. public void test02(){
    20. // 查询条件
    21. Customer customer=new Customer();
    22. customer.setCustName("庶");
    23. customer.setCustAddress("JING");
    24. // 通过匹配器 对条件行为进行设置
    25. ExampleMatcher matcher = ExampleMatcher.matching()
    26. //.withIgnorePaths("custName") // 设置忽略的属性
    27. //.withIgnoreCase("custAddress") // 设置忽略大小写
    28. //.withStringMatcher(ExampleMatcher.StringMatcher.ENDING); // 对所有条件字符串进行了结尾匹配
    29. .withMatcher("custAddress",m -> m.endsWith().ignoreCase()); // 针对单个条件进行限制, 会使withIgnoreCase失效,需要单独设置,第一个参数是需要对那个属性进行匹配,第二个参数表示的是需要对属性使用什么样的匹配规则
    30. //.withMatcher("custAddress",ExampleMatcher.GenericPropertyMatchers.endsWith().ignoreCase());
    31. // 通过Example构建查询条件 加了匹配器的查询条件
    32. Example example = Example.of(customer,matcher);
    33. List list = (List) repository.findAll(example);
    34. System.out.println(list);
    35. }

    匹配器提供的一些方法:

    注意演示案例使用的持久层框架是spring data jpa中的hibernate。

    pom文件依赖:我的演示环境的spring boot是 2.6.10

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.springframework.bootgroupId>
    4. <artifactId>spring-boot-starter-data-jpaartifactId>
    5. dependency>
    6. <dependency>
    7. <groupId>org.springframework.bootgroupId>
    8. <artifactId>spring-boot-starter-webartifactId>
    9. dependency>
    10. <dependency>
    11. <groupId>mysqlgroupId>
    12. <artifactId>mysql-connector-javaartifactId>
    13. <scope>runtimescope>
    14. dependency>
    15. <dependency>
    16. <groupId>org.springframework.bootgroupId>
    17. <artifactId>spring-boot-starter-testartifactId>
    18. <scope>testscope>
    19. dependency>
    20. dependencies>

    1、创建实体类

    1. package com.ljm.entity;
    2. import javax.persistence.*;
    3. import java.util.Objects;
    4. /**
    5. * @author LJM
    6. * @create 2022/8/1
    7. */
    8. @Entity
    9. @Table(name = "student")
    10. public class Student {
    11. @Id
    12. @GeneratedValue(strategy = GenerationType.IDENTITY)
    13. @Column(name = "id")
    14. private Long id;
    15. @Column(name = "name")
    16. private String name;
    17. @Column(name = "age")
    18. private Long age;
    19. @Column(name = "loves")
    20. private String loves;
    21. @Column(name = "card_num")
    22. private String cardNum;
    23. public void setId(Long id) {
    24. this.id = id;
    25. }
    26. public Long getId() {
    27. return this.id;
    28. }
    29. public void setName(String name) {
    30. this.name = name;
    31. }
    32. public String getName() {
    33. return this.name;
    34. }
    35. public void setAge(Long age) {
    36. this.age = age;
    37. }
    38. public Long getAge() {
    39. return this.age;
    40. }
    41. public void setLoves(String loves) {
    42. this.loves = loves;
    43. }
    44. public String getLoves() {
    45. return this.loves;
    46. }
    47. public String getCardNum() {
    48. return cardNum;
    49. }
    50. public void setCardNum(String cardNum) {
    51. this.cardNum = cardNum;
    52. }
    53. @Override
    54. public boolean equals(Object o) {
    55. if (this == o) return true;
    56. if (o == null || getClass() != o.getClass()) return false;
    57. Student student = (Student) o;
    58. return Objects.equals(id, student.id) && Objects.equals(name, student.name) && Objects.equals(age, student.age) && Objects.equals(loves, student.loves) && Objects.equals(cardNum, student.cardNum);
    59. }
    60. @Override
    61. public int hashCode() {
    62. return Objects.hash(id, name, age, loves, cardNum);
    63. }
    64. @Override
    65. public String toString() {
    66. return "Student{" +
    67. "id='" + id + '\'' +
    68. ", name='" + name + '\'' +
    69. ", age=" + age +
    70. ", loves='" + loves + '\'' +
    71. ", cardNum='" + cardNum + '\'' +
    72. '}';
    73. }
    74. }

    2、数据库创建表(实际上hibernate会帮我们自动创建,只要我们在配置文件中提供库名就行),

    或者是使用sql:

    1. SET NAMES utf8mb4;
    2. SET FOREIGN_KEY_CHECKS = 0;
    3. -- ----------------------------
    4. -- Table structure for student
    5. -- ----------------------------
    6. DROP TABLE IF EXISTS `student`;
    7. CREATE TABLE `student` (
    8. `id` bigint NOT NULL AUTO_INCREMENT,
    9. `age` bigint NULL DEFAULT NULL,
    10. `card_num` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
    11. `loves` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
    12. `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
    13. PRIMARY KEY (`id`) USING BTREE
    14. ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
    15. -- ----------------------------
    16. -- Records of student
    17. -- ----------------------------
    18. INSERT INTO `student` VALUES (1, 16, '12345', '打篮球', '小李');
    19. INSERT INTO `student` VALUES (2, 16, '123456', '测试', '小王');
    20. INSERT INTO `student` VALUES (3, 17, '123456', '测试', '小王');
    21. SET FOREIGN_KEY_CHECKS = 1;

    3、创建dao层

    1. package com.ljm.dao;
    2. import com.ljm.entity.Student;
    3. import org.springframework.data.jpa.repository.JpaRepository;
    4. import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
    5. public interface StudentRepository extends JpaRepository, JpaSpecificationExecutor {
    6. }

    使用example进行的动态条件分页查询案例演示

    创建实体类和数据库表相关步骤在上面;

    4、直接编写service层具体的实现,这里就不编写service接口了。

    1. @Service
    2. public class StudentService {
    3. @Autowired
    4. private StudentRepository studentRepository;
    5. public Page queryStudent(Student queryStudent, Pageable pageable) {
    6. //创建匹配条件
    7. ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreNullValues();
    8. Example example = Example.of(queryStudent, matcher);
    9. Page pageStudent = studentRepository.findAll(example, pageable);
    10. return pageStudent ;
    11. }
    12. }

    5、编写controller

    1. @RestController
    2. @RequestMapping("/student")
    3. public class StudentController {
    4. @Autowired
    5. StudentService studentService;
    6. @GetMapping("/list")
    7. public Page list(int page, int size, @RequestBody Student student) {
    8. PageRequest pageRequest = PageRequest.of(page, size);
    9. Page pageStudent =
    10. studentService.queryStudent(student, pageRequest);
    11. return pageStudent;
    12. }
    13. }

    Specification分页查询

    Specifications分页查询基础知识

    之前使用Query by Example只能针对字符串进行条件设置,那如果希望对所有类型支持,可以使用Specifications。

    1、继承接口JpaSpecificationExecutor。

    1. public interface CustomerSpecificationsRepository
    2. extends PagingAndSortingRepository,
    3. JpaSpecificationExecutor { //这个泛型表示的是你要操作的Java实体类
    4. }

    2、传入Specification的实现: 结合lambda表达式

    • Root:查询哪个表(关联查询) = from

    • CriteriaQuery:查询哪些字段,排序是什么 =组合(order by . where )

    • CriteriaBuilder:条件之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么类型(> between in...) = where

    • Predicate(Expression): 每一条查询条件的详细描述

                root对象可以让我们从表中获取我们想要的列
                CriteriaBuilder where 设置各种条件  (> < in between......)
                query  组合(order by , where)

    1. @Autowired
    2. CustomerSpecificationsRepository repository; //把刚刚自定义的接口注入到容器中
    3. //进行精确匹配,单一条件查询
    4. @Test
    5. public void testR2(){
    6. List customer = repository.findAll(new Specification() {
    7. @Override
    8. public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
    9. // root对象可以让我们从表中获取我们想要的列
    10. // CriteriaBuilder where 设置各种条件 (> < in ..)
    11. // query 组合(order by , where)
    12. Path custId = root.get("custId"); //获取对应的字段
    13. Path custName = root.get("custName");
    14. Path custAddress = root.get("custAddress");
    15. // 对字段进行精确匹配 参数1 :为哪个字段设置条件 参数2:值
    16. Predicate predicate = cb.equal(custAddress, "BEIJING");
    17. return predicate;
    18. }
    19. });
    20. System.out.println(customer);
    21. }
    22. //进行多条件查询
    23. @Test
    24. public void testR3(){
    25. List customer = repository.findAll(new Specification() {
    26. @Override
    27. public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
    28. //root对象可以让我们从表中获取我们想要的列
    29. // CriteriaBuilder where 设置各种条件 (> < in ..)
    30. // query 组合(order by , where)
    31. Path custId = root.get("custId");
    32. Path custName = root.get("custName");
    33. Path custAddress = root.get("custAddress");
    34. // 参数1 :为哪个字段设置条件 参数2:值
    35. Predicate custAddressP = cb.equal(custAddress, "BEIJING"); //等于
    36. Predicate custIdP = cb.greaterThan(custId, 0L); //大于
    37. CriteriaBuilder.In in = cb.in(custName); //in
    38. in.value("徐庶").value("王五"); //通过点value来进行in范围的拼接
    39. //对多个条件进行拼接
    40. Predicate and = cb.and(custAddressP, custIdP,in);
    41. return and;
    42. }
    43. });
    44. System.out.println(customer);
    45. }
    46. //---------------------------------------------------------------------------------------------------------
    47. //注意上面我们进行查询条件的构建的时候,是把查询条件给写死了,在实际开发中这个条件应该是动态的,我们需要根据前端传来的数据进行判断,然后通过判断的结果来进行查询条件的构建---主要是进行非空判断
    48. //模拟动态条件查询
    49. @Test
    50. public void testR4(){
    51. //模拟前端传过来的数据
    52. Customer params=new Customer();
    53. //params.setCustAddress("BEIJING");
    54. params.setCustId(0L);
    55. params.setCustName("徐庶,王五");
    56. List customer = repository.findAll(new Specification() {
    57. @Override
    58. public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
    59. //root对象可以让我们从表中获取我们想要的列
    60. // CriteriaBuilder where 设置各种条件 (> < in ..)
    61. // query 组合(order by , where)
    62. // 1. 通过root拿到需要设置条件的字段
    63. Path custId = root.get("custId");
    64. Path custName = root.get("custName");
    65. Path custAddress = root.get("custAddress");
    66. // 2. 通过CriteriaBuilder设置不同类型条件
    67. //2.1 因为动态查询的时候条件是变化的,不确定的,所以需要使用集合来进行条件的保存
    68. List list=new ArrayList<>();
    69. if(!StringUtils.isEmpty(params.getCustAddress())) {
    70. // 参数1 :为哪个字段设置条件 参数2:值
    71. list.add(cb.equal(custAddress, "BEIJING")) ;
    72. }
    73. if(params.getCustId()>-1){
    74. list.add(cb.greaterThan(custId, 0L));
    75. }
    76. if(!StringUtils.isEmpty(params.getCustName())) {
    77. CriteriaBuilder.In in = cb.in(custName);
    78. in.value("徐庶").value("王五");
    79. list.add(in);
    80. }
    81. // 组合条件 因为涉及动态查询,这个拼接的查询条件个数在上面的判断中已经确定了,这里需要我们传一个数组过来,通过集合转数组的方法进行转换,不过需要的是Prediccate类型的定长数组
    82. Predicate and = cb.and(list.toArray(new Predicate[list.size()]));
    83. return and;
    84. }
    85. });
    86. System.out.println(customer);
    87. }
    88. //进行排序等多条件的操作
    89. @Test
    90. public void testR5(){
    91. Customer params=new Customer();
    92. //params.setCustAddress("BEIJING");
    93. params.setCustId(0L);
    94. params.setCustName("徐庶,王五");
    95. List customer = repository.findAll(new Specification() {
    96. @Override
    97. public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
    98. //root对象可以让我们从表中获取我们想要的列
    99. // CriteriaBuilder where 设置各种条件 (> < in ..)
    100. // query 组合(order by , where)
    101. Path custId = root.get("custId");
    102. Path custName = root.get("custName");
    103. Path custAddress = root.get("custAddress");
    104. // 参数1 :为哪个字段设置条件 参数2:值
    105. List list=new ArrayList<>();
    106. if(!StringUtils.isEmpty(params.getCustAddress())) {
    107. list.add(cb.equal(custAddress, "BEIJING")) ;
    108. }
    109. if(params.getCustId()>-1){
    110. list.add(cb.greaterThan(custId, 0L));
    111. }
    112. if(!StringUtils.isEmpty(params.getCustName())) {
    113. CriteriaBuilder.In in = cb.in(custName);
    114. in.value("徐庶").value("王五");
    115. list.add(in);
    116. }
    117. Predicate and = cb.and(list.toArray(new Predicate[list.size()]));
    118. //对id字段进行降序排序
    119. Order desc = cb.desc(custId);
    120. //使用呢query对象对条件和组合进行拼接
    121. return query.where(and).orderBy(desc).getRestriction();
    122. }
    123. });
    124. System.out.println(customer);
    125. }
    126. 缺点:不支持分组等相关聚合函数的操作,但是支持排序 如果你还是想要通过jpa实现分组等聚合函数的操作,那就需要通过原生的jpa,通过自己获取root对象,CriteriaQuery和CriteriaBuilder对象,然后再使用他们继续操作。因为重写toPredicate方法提供的数据库操作是定死的字段,你想要修改就只能提供原生的jpa操作来自己实现。

      使用Specification来进行动态条件分页查询案例演示

      1. @Autowired
      2. private StudentRepository studentRepository;
      3. /**
      4. * 通过Specification构建动态查询条件,动态的条件为通过name,age,学号进行动态查询
      5. * @param student 前端传过来的查询dto
      6. * @return specification 返回构建好的动态查询条件
      7. */
      8. public Specification createSpecification(Student student) {
      9. Specification specification = new Specification() {
      10. @Override
      11. public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
      12. //构建查询条件
      13. Path name = root.get("name");
      14. Path age = root.get("age");
      15. Path cardNum = root.get("cardNum");
      16. ArrayList list = new ArrayList<>();
      17. if (!StringUtils.isEmpty(student.getName())) {
      18. list.add(criteriaBuilder.equal(name, student.getName()));
      19. }
      20. //这个age这里会抛空指针异常(前端可能会传null过来),暂时就想到了使用这种方法进行了处理。。。。
      21. Long aLong = student.getAge();
      22. String sLong = String.valueOf(aLong);
      23. if (!"null".equals(sLong)){
      24. long ageLong = Long.parseLong(sLong);
      25. if ( ageLong > 0 || ageLong < 150) {
      26. list.add(criteriaBuilder.equal(age, ageLong));
      27. }
      28. }
      29. if (!StringUtils.isEmpty(student.getCardNum())) {
      30. list.add(criteriaBuilder.equal(cardNum, student.getCardNum()));
      31. }
      32. Predicate and = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));
      33. return and;
      34. }
      35. };
      36. return specification;
      37. }
      38. /**
      39. * 通过Specification进行条件分页查询,查询条件包含姓名、年龄、学号三个字段
      40. * @param currentPage 当前页面
      41. * @param pageSize 每页存放的数据条数
      42. * @param specification Specification对象
      43. * @return studentPage 返回分页查询结果
      44. */
      45. public Page page (Integer currentPage, Integer pageSize, Specification specification){
      46. Pageable pageable = PageRequest.of((currentPage - 1) > 0 ? (currentPage - 1) : 0, pageSize);
      47. Page studentPage = studentRepository.findAll(specification, pageable);
      48. return studentPage;
      49. }

      编写controller:

      1. @Autowire
      2. StudentService studentService
      3. /**
      4. * 通过Specification进行动态条件的分页查询,
      5. * 动态分页查询条件为:name,age,学号
      6. * @param page 当前页
      7. * @param size 每页存放的数据条数
      8. * @param student 分页查询结果
      9. * @return
      10. */
      11. @GetMapping("/page")
      12. public Page page(
      13. @RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
      14. @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
      15. @RequestBody Student student){
      16. Specification specification = studentService.createSpecification(student);
      17. Page studentPage = studentService.page(page, size, specification);
      18. return studentPage;
      19. }

      Querydsl

      QueryDSL是基于ORM框架或SQL平台上的一个通用查询框架。借助QueryDSL可以在任何支持的ORM框架或SQL平台上以通用API方式构建查询。 JPA是QueryDSL的主要集成技术,是JPQL和Criteria查询的代替方法。目前QueryDSL支持的平台包括JPA,JDO,SQL,Mongodb 等等。

      Querydsl扩展能让我们以链式方式代码编写查询方法。该扩展需要一个接口QueryDslPredicateExecutor,它定义了很多查询方法。

      我们自己定义的接口继承QueryDslPredicateExecutor该接口,那么我们就可以使用该接口提供的各种方法了。

      1. public interface CustomerQueryDSLRepository extends
      2. PagingAndSortingRepository
      3. , QuerydslPredicateExecutor { //这个泛型是我们需要操作的Java的实体类
      4. }

      需要引入单独的依赖:

      1. <querydsl.version>4.4.0querydsl.version>
      2. <dependency>
      3. <groupId>com.querydslgroupId>
      4. <artifactId>querydsl‐jpaartifactId>
      5. <version>${querydsl.version}version>
      6. dependency>

      引入组件:用于构建q类,但是加入这个组件后它不会自动编译,所以需要我们在maven的组件那里点击进行编译。

      1. <apt.version>1.1.3apt.version>
      2. <plugins>
      3. <plugin>
      4. <groupId>com.mysema.mavengroupId>
      5. <artifactId>apt-maven-pluginartifactId>
      6. <version>${apt.version}version>
      7. <dependencies>
      8. <dependency>
      9. <groupId>com.querydslgroupId>
      10. <artifactId>querydsl-aptartifactId>
      11. <version>${querydsl.version}version>
      12. dependency>
      13. dependencies>
      14. <executions>
      15. <execution>
      16. <phase>generate-sourcesphase>
      17. <goals>
      18. <goal>processgoal>
      19. goals>
      20. <configuration>
      21. <outputDirectory>target/generated-sources/queriesoutputDirectory>
      22. <processor>com.querydsl.apt.jpa.JPAAnnotationProcessorprocessor>
      23. <logOnlyOnError>truelogOnlyOnError>
      24. configuration>
      25. execution>
      26. executions>
      27. plugin>
      28. plugins>

      编译完成后会在我们的target目录下生成对应的class文件,这样我们就可以自己去构建q类去了?实际上还是不行,因为这个编译后的只是一个class文件,这个时候我们书写代码去用这个Q类还是会报错,应该是压根点不出来。这是因为我们编写代码是用source文件夹进行编写的,而字节码文件是被排除在source(可以进行编码的文件夹)目录下的,所以这个需要我们手动的把这个Q类所在的文件夹变成source文件夹。  

      然后就可以进行编码测试了:

      1. @Autowired
      2. CustomerQueryDSLRepository repository; //把刚刚自定义的接口注入容器中
      3. @Test
      4. public void test01() {
      5. //拿到Q类对象
      6. QCustomer customer = QCustomer.customer;
      7. // 通过Id查找 这里的id我们写死为1了,但是实际开发中是会用一个变量来接收前端传输过来的id的
      8. BooleanExpression eq = customer.custId.eq(1L);
      9. System.out.println(repository.findOne(eq));
      10. }
      11. /**
      12. * 查询客户名称范围 (in)
      13. 等于 EQ : equal .eq
      14. 不等于 NE : not equal .ne
      15. 小于 LT : less than .lt
      16. 大于 GT : greater than .gt
      17. 小于等于 LE : less than or equal .loe
      18. 大于等于 GE : greater than or equal .goe
      19. * id >大于
      20. * 地址 精确
      21. */
      22. @Test
      23. public void test02() {
      24. QCustomer customer = QCustomer.customer;
      25. // 通过Id查找
      26. BooleanExpression and = customer.custName.in("徐庶", "王五")
      27. .and(customer.custId.gt(0L)) //id大于0
      28. .and(customer.custAddress.eq("BEIJING")); //地址为BEIJING
      29. System.out.println(repository.findOne(and));
      30. }
      31. //模拟动态查询
      32. @Test
      33. public void test03() {
      34. Customer params=new Customer();
      35. params.setCustAddress("BEIJING");
      36. params.setCustId(0L);
      37. params.setCustName("徐庶,王五");
      38. QCustomer customer = QCustomer.customer;
      39. // 初始条件 类似于1=1 永远都成立的条件
      40. BooleanExpression expression = customer.isNotNull().or(customer.isNull());
      41. //这里是使用三目运算来进行判断了,当然也可以使用if来进行判断
      42. //注意:记得要使用and来进行拼接这个条件
      43. expression=params.getCustId()>-1?
      44. expression.and(customer.custId.gt(params.getCustId())):expression;
      45. expression=!StringUtils.isEmpty( params.getCustName())?
      46. expression.and(customer.custName.in(params.getCustName().split(","))):expression;
      47. expression=!StringUtils.isEmpty( params.getCustAddress())?
      48. expression.and(customer.custAddress.eq(params.getCustAddress())):expression;
      49. System.out.println(repository.findAll(expression));
      50. }
      51. // 解决线程安全问题 如果使用@autowire来对EntityManager进行注入,那可能会出现线程安全问题,使用注解PersistenceContext可以为每一个线程单独绑定一个EntityManager,这样就可以解决线程不安全的问题了
      52. @PersistenceContext
      53. EntityManager em;
      54. /**
      55. * 自定义列查询、分组
      56. * 需要使用原生态的方式(Specification)
      57. * 通过Repository进行查询, 列、表都是固定
      58. */
      59. @Test
      60. public void test04() {
      61. JPAQueryFactory factory = new JPAQueryFactory(em);
      62. QCustomer customer = QCustomer.customer;
      63. // 构建基于QueryDSL的查询 像写SQL一样来拼接条件 这个Tuple是一个自定义对象,主要是负责来接收你要查询信息,比如你只需要查一张表中的两个字段,实际上是没有对象来接收这个查询结果的两个字段的,而提供的Tuple这个对象就可以用来接收这种类型的数据
      64. JPAQuery tupleJPAQuery = factory.select(customer.custId, customer.custName)
      65. .from(customer)
      66. .where(customer.custId.eq(1L))
      67. .orderBy(customer.custId.desc());
      68. // 执行查询
      69. List fetch = tupleJPAQuery.fetch();
      70. // 处理返回数据 对结果集进行遍历
      71. for (Tuple tuple : fetch) {
      72. System.out.println(tuple.get(customer.custId));
      73. System.out.println(tuple.get(customer.custName));
      74. }
      75. }
      76. //结合聚合函数进行查询,注意结果集的返回值
      77. @Test
      78. public void test05() {
      79. JPAQueryFactory factory = new JPAQueryFactory(em);
      80. QCustomer customer = QCustomer.customer;
      81. // 构建基于QueryDSL的查询
      82. JPAQuery longJPAQuery = factory.select(
      83. customer.custId.sum())
      84. .from(customer)
      85. //.where(customer.custId.eq(1L))
      86. .orderBy(customer.custId.desc());
      87. // 执行查询
      88. List fetch = longJPAQuery.fetch();
      89. // 处理返回数据
      90. for (Long sum : fetch) {
      91. System.out.println(sum);
      92. }
      93. }

    127. 相关阅读:
      最长回文子串
      瑞_23种设计模式_组合模式
      本周大新闻|苹果再强调禁用“元宇宙”一词,TDK推出全球最小RGB激光模块
      C++获取商店应用(msix应用)桌面快捷方式的安装目录
      telnet无效指令,telnet找不到命令
      pom管理规范
      Qt QtWidgets相关问题汇总
      AI助力智能安检,基于目标检测模型实现X光安检图像智能检测分析
      【毕业设计】stm32单片机智能扫地机器人 - 嵌入式 物联网
      无线WiFi安全渗透与攻防(五) aircrack-ng(亲测有效)、mdk3联合攻击
    128. 原文地址:https://blog.csdn.net/weixin_53142722/article/details/126097851