使用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

public interface CustomerQBERepository extends PagingAndSortingRepository<Customer,Long>, QueryByExampleExecutor<Customer> {  //这个泛型表示的是你要操作的Java实体类
    
}

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

public interface QueryByExampleExecutor<T> {
    <S extends T> Optional<S> findOne(Example<S> example);

    <S extends T> Iterable<S> findAll(Example<S> example);

    <S extends T> Iterable<S> findAll(Example<S> example, Sort sort);

    <S extends T> Page<S> findAll(Example<S> example, Pageable pageable);

    <S extends T> long count(Example<S> example);

    <S extends T> boolean exists(Example<S> example);

    <S extends T, R> R findBy(Example<S> example, Function<FetchableFluentQuery<S>, R> queryFunction);
}

方法演示:

    @Autowired
    CustomerQBERepository repository;  //把刚刚自己定义的接口注入到容器

 
   //根据客户名称  客户地址动态查询

    @Test
    public  void test01(){

        //构建实体类需要动态查询的条件  比如需要通过名字和地址进行动态查询
        Customer customer=new Customer();
        customer.setCustName("徐庶");  //在实际开发中这个查询的条件都是从前端传过来的,比如传了name和address过来,你需要使用对应的数据类型进行接收就行,这里是演示所以直接写死了查询条件
        customer.setCustAddress("BEIJING");

        // 通过Example构建查询条件  特别注意这个Example对象是spring data中的依赖,别引成了hibernate的
        Example<Customer> example = Example.of(customer);  

        List<Customer> list = (List<Customer>) repository.findAll(example);
        System.out.println(list);
    }



    /**
     * 通过匹配器进行条件的限制    客户名称  客户地址动态查询
     */
    @Test
    public  void test02(){

        // 查询条件
        Customer customer=new Customer();
        customer.setCustName("庶");
        customer.setCustAddress("JING");

        // 通过匹配器 对条件行为进行设置
        ExampleMatcher matcher = ExampleMatcher.matching()
                //.withIgnorePaths("custName")       // 设置忽略的属性
                //.withIgnoreCase("custAddress")      // 设置忽略大小写
                //.withStringMatcher(ExampleMatcher.StringMatcher.ENDING);    // 对所有条件字符串进行了结尾匹配
                .withMatcher("custAddress",m -> m.endsWith().ignoreCase());      // 针对单个条件进行限制, 会使withIgnoreCase失效,需要单独设置,第一个参数是需要对那个属性进行匹配,第二个参数表示的是需要对属性使用什么样的匹配规则
                //.withMatcher("custAddress",ExampleMatcher.GenericPropertyMatchers.endsWith().ignoreCase());

        // 通过Example构建查询条件  加了匹配器的查询条件
        Example<Customer> example = Example.of(customer,matcher);

        List<Customer> list = (List<Customer>) repository.findAll(example);
        System.out.println(list);
    }

匹配器提供的一些方法:

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

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

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
</dependencies>

1、创建实体类

package com.ljm.entity;
import javax.persistence.*;
import java.util.Objects;

/**
 * @author LJM
 * @create 2022/8/1
 */
@Entity
@Table(name = "student")
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;


    @Column(name = "name")
    private String name;


    @Column(name = "age")
    private Long age;

    @Column(name = "loves")
    private String loves;


    @Column(name = "card_num")
    private String cardNum;


    public void setId(Long id) {
        this.id = id;
    }

    public Long getId() {
        return this.id;
    }


    public void setName(String name) {
        this.name = name;
    }


    public String getName() {
        return this.name;
    }

    public void setAge(Long age) {
        this.age = age;
    }

    public Long getAge() {
        return this.age;
    }

    public void setLoves(String loves) {
        this.loves = loves;
    }


    public String getLoves() {
        return this.loves;
    }

    public String getCardNum() {
        return cardNum;
    }

    public void setCardNum(String cardNum) {
        this.cardNum = cardNum;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        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);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name, age, loves, cardNum);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", loves='" + loves + '\'' +
                ", cardNum='" + cardNum + '\'' +
                '}';
    }
}

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

或者是使用sql:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for student
-- ----------------------------
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `age` bigint NULL DEFAULT NULL,
  `card_num` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `loves` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of student
-- ----------------------------
INSERT INTO `student` VALUES (1, 16, '12345', '打篮球', '小李');
INSERT INTO `student` VALUES (2, 16, '123456', '测试', '小王');
INSERT INTO `student` VALUES (3, 17, '123456', '测试', '小王');

SET FOREIGN_KEY_CHECKS = 1;

3、创建dao层

package com.ljm.dao;

import com.ljm.entity.Student;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;

public interface StudentRepository extends JpaRepository<Student, String>, JpaSpecificationExecutor<Student> {

}

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

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

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

@Service
public class StudentService {

    @Autowired
    private StudentRepository studentRepository;

    public Page<Student> queryStudent(Student queryStudent, Pageable pageable) {

        //创建匹配条件
        ExampleMatcher matcher = ExampleMatcher.matching().withIgnoreNullValues();

        Example<Student> example = Example.of(queryStudent, matcher);
        Page<Student> pageStudent = studentRepository.findAll(example, pageable);
        return pageStudent ;
    }
}

5、编写controller

@RestController
@RequestMapping("/student")
public class StudentController {

    @Autowired
    StudentService studentService;

    @GetMapping("/list")
    public Page<Student> list(int page, int size, @RequestBody Student student) {

        PageRequest pageRequest = PageRequest.of(page, size);
        Page<Student> pageStudent =
                studentService.queryStudent(student, pageRequest);
        return pageStudent;

    }
}

Specification分页查询

Specifications分页查询基础知识

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

1、继承接口JpaSpecificationExecutor。

public interface CustomerSpecificationsRepository
        extends PagingAndSortingRepository<Customer,Long>,
        JpaSpecificationExecutor<Customer> {  //这个泛型表示的是你要操作的Java实体类

}

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

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

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

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

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

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

    @Autowired
    CustomerSpecificationsRepository repository; //把刚刚自定义的接口注入到容器中


	//进行精确匹配,单一条件查询
    @Test
    public  void testR2(){

        List<Customer> customer = repository.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                // root对象可以让我们从表中获取我们想要的列
                // CriteriaBuilder where 设置各种条件  (> < in ..)
                // query  组合(order by , where)
                Path<Object> custId = root.get("custId");  //获取对应的字段
                Path<Object> custName = root.get("custName");
                Path<Object> custAddress = root.get("custAddress");

                // 对字段进行精确匹配 参数1 :为哪个字段设置条件   参数2:值
                Predicate predicate = cb.equal(custAddress, "BEIJING");

                return predicate;
            }
        });

        System.out.println(customer);
    }



	//进行多条件查询
    @Test
    public  void testR3(){

        List<Customer> customer = repository.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                //root对象可以让我们从表中获取我们想要的列
                // CriteriaBuilder where 设置各种条件  (> < in ..)
                // query  组合(order by , where)
                Path<Long> custId = root.get("custId");
                Path<String> custName = root.get("custName");
                Path<String> custAddress = root.get("custAddress");

                // 参数1 :为哪个字段设置条件   参数2:值 
                Predicate custAddressP = cb.equal(custAddress, "BEIJING"); //等于
                Predicate custIdP = cb.greaterThan(custId, 0L);   //大于
                CriteriaBuilder.In<String> in = cb.in(custName);  //in
                in.value("徐庶").value("王五");  //通过点value来进行in范围的拼接

				//对多个条件进行拼接
                Predicate and = cb.and(custAddressP, custIdP,in);

                return and;
            }
        });

        System.out.println(customer);
    }

//---------------------------------------------------------------------------------------------------------
//注意上面我们进行查询条件的构建的时候,是把查询条件给写死了,在实际开发中这个条件应该是动态的,我们需要根据前端传来的数据进行判断,然后通过判断的结果来进行查询条件的构建---主要是进行非空判断


	//模拟动态条件查询
    @Test
    public  void testR4(){

        //模拟前端传过来的数据
        Customer params=new Customer();
        //params.setCustAddress("BEIJING");
        params.setCustId(0L);
        params.setCustName("徐庶,王五");

        List<Customer> customer = repository.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {


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

                // 1. 通过root拿到需要设置条件的字段
                Path<Long> custId = root.get("custId");
                Path<String> custName = root.get("custName");
                Path<String> custAddress = root.get("custAddress");

                // 2. 通过CriteriaBuilder设置不同类型条件
                //2.1 因为动态查询的时候条件是变化的,不确定的,所以需要使用集合来进行条件的保存
                List<Predicate> list=new ArrayList<>();
                if(!StringUtils.isEmpty(params.getCustAddress())) {
                    // 参数1 :为哪个字段设置条件   参数2:值
                    list.add(cb.equal(custAddress, "BEIJING")) ;
                }
                if(params.getCustId()>-1){
                    list.add(cb.greaterThan(custId, 0L));
                }

                if(!StringUtils.isEmpty(params.getCustName())) {
                    CriteriaBuilder.In<String> in = cb.in(custName);
                    in.value("徐庶").value("王五");
                    list.add(in);
                }


                // 组合条件  因为涉及动态查询,这个拼接的查询条件个数在上面的判断中已经确定了,这里需要我们传一个数组过来,通过集合转数组的方法进行转换,不过需要的是Prediccate类型的定长数组
                Predicate and = cb.and(list.toArray(new Predicate[list.size()]));

                return and;
            }
        });

        System.out.println(customer);
    }


	//进行排序等多条件的操作
    @Test
    public  void testR5(){

        Customer params=new Customer();
        //params.setCustAddress("BEIJING");
        params.setCustId(0L);
        params.setCustName("徐庶,王五");

        List<Customer> customer = repository.findAll(new Specification<Customer>() {
            @Override
            public Predicate toPredicate(Root<Customer> root, CriteriaQuery<?> query, CriteriaBuilder cb) {

                //root对象可以让我们从表中获取我们想要的列
                // CriteriaBuilder where 设置各种条件  (> < in ..)
                // query  组合(order by , where)
                Path<Long> custId = root.get("custId");
                Path<String> custName = root.get("custName");
                Path<String> custAddress = root.get("custAddress");

                // 参数1 :为哪个字段设置条件   参数2:值
                List<Predicate> list=new ArrayList<>();
                if(!StringUtils.isEmpty(params.getCustAddress())) {
                    list.add(cb.equal(custAddress, "BEIJING")) ;
                }
                if(params.getCustId()>-1){
                    list.add(cb.greaterThan(custId, 0L));
                }

                if(!StringUtils.isEmpty(params.getCustName())) {
                    CriteriaBuilder.In<String> in = cb.in(custName);
                    in.value("徐庶").value("王五");
                    list.add(in);
                }


                Predicate and = cb.and(list.toArray(new Predicate[list.size()]));

                //对id字段进行降序排序
                Order desc = cb.desc(custId);

                //使用呢query对象对条件和组合进行拼接
                return query.where(and).orderBy(desc).getRestriction();
            }
        });

        System.out.println(customer);
    }

缺点:不支持分组等相关聚合函数的操作,但是支持排序 如果你还是想要通过jpa实现分组等聚合函数的操作,那就需要通过原生的jpa,通过自己获取root对象,CriteriaQuery和CriteriaBuilder对象,然后再使用他们继续操作。因为重写toPredicate方法提供的数据库操作是定死的字段,你想要修改就只能提供原生的jpa操作来自己实现。

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

@Autowired
private StudentRepository studentRepository;

    /**
     * 通过Specification构建动态查询条件,动态的条件为通过name,age,学号进行动态查询
     * @param student 前端传过来的查询dto
     * @return specification 返回构建好的动态查询条件
     */
    public Specification<Student> createSpecification(Student student) {
        Specification<Student> specification = new Specification<Student>() {
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //构建查询条件
                Path<String> name = root.get("name");
                Path<Long> age = root.get("age");
                Path<String> cardNum = root.get("cardNum");

                ArrayList<Predicate> list = new ArrayList<>();
                if (!StringUtils.isEmpty(student.getName())) {
                    list.add(criteriaBuilder.equal(name, student.getName()));
                }
                //这个age这里会抛空指针异常(前端可能会传null过来),暂时就想到了使用这种方法进行了处理。。。。
                Long aLong = student.getAge();
                String sLong = String.valueOf(aLong);
                if (!"null".equals(sLong)){
                    long ageLong = Long.parseLong(sLong);
                    if ( ageLong > 0 || ageLong < 150) {
                        list.add(criteriaBuilder.equal(age, ageLong));
                    }
                }

                if (!StringUtils.isEmpty(student.getCardNum())) {
                    list.add(criteriaBuilder.equal(cardNum, student.getCardNum()));
                }

                Predicate and = criteriaBuilder.and(list.toArray(new Predicate[list.size()]));

                return and;
            }

        };

        return specification;
    }



    /**
     * 通过Specification进行条件分页查询,查询条件包含姓名、年龄、学号三个字段
     * @param currentPage  当前页面
     * @param pageSize  每页存放的数据条数
     * @param specification Specification对象
     * @return studentPage 返回分页查询结果
     */
    public Page<Student> page (Integer currentPage, Integer pageSize, Specification<Student> specification){

        Pageable pageable = PageRequest.of((currentPage - 1) > 0 ? (currentPage - 1) : 0, pageSize);
        Page<Student> studentPage = studentRepository.findAll(specification, pageable);
        return studentPage;
    }

编写controller:

@Autowire
StudentService studentService

    /**
     * 通过Specification进行动态条件的分页查询,
     * 动态分页查询条件为:name,age,学号
     * @param page 当前页
     * @param size 每页存放的数据条数
     * @param student 分页查询结果
     * @return
     */
    @GetMapping("/page")
    public Page<Student> page(
            @RequestParam(value = "page", required = false, defaultValue = "1") Integer page,
            @RequestParam(value = "size", required = false, defaultValue = "10") Integer size,
            @RequestBody Student student){

        Specification<Student> specification = studentService.createSpecification(student);

        Page<Student> studentPage = studentService.page(page, size, specification);

        return studentPage;

    }

Querydsl

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

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

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

public interface CustomerQueryDSLRepository extends
        PagingAndSortingRepository<Customer,Long>
          , QuerydslPredicateExecutor<Customer> {  //这个泛型是我们需要操作的Java的实体类

}

需要引入单独的依赖:

<querydsl.version>4.4.0</querydsl.version>

<!‐‐ querydsl ‐‐>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl‐jpa</artifactId>
<version>${querydsl.version}</version>
</dependency>

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

<apt.version>1.1.3</apt.version>


<plugins>
    <plugin>
        <groupId>com.mysema.maven</groupId>
        <artifactId>apt-maven-plugin</artifactId>
        <version>${apt.version}</version>
        <dependencies>
            <dependency>
                <groupId>com.querydsl</groupId>
                <artifactId>querydsl-apt</artifactId>
                <version>${querydsl.version}</version>
            </dependency>
        </dependencies>
        <executions>
            <execution>
                <phase>generate-sources</phase>
                <goals>
                    <goal>process</goal>
                </goals>
                <configuration>
                    <outputDirectory>target/generated-sources/queries</outputDirectory>
                    <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
                    <logOnlyOnError>true</logOnlyOnError>
                </configuration>
            </execution>
        </executions>
    </plugin>
</plugins>

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

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

    @Autowired
    CustomerQueryDSLRepository repository;  //把刚刚自定义的接口注入容器中

    @Test
    public  void test01() {
        //拿到Q类对象
        QCustomer customer = QCustomer.customer;
        // 通过Id查找  这里的id我们写死为1了,但是实际开发中是会用一个变量来接收前端传输过来的id的
        BooleanExpression eq = customer.custId.eq(1L);
        System.out.println(repository.findOne(eq));
    }


    /**
     * 查询客户名称范围 (in)
     等于 EQ : equal .eq
	 不等于 NE : not equal .ne
	 小于 LT : less than .lt
	 大于 GT : greater than .gt
	 小于等于 LE : less than or equal .loe
	 大于等于 GE : greater than or equal .goe
     * id  >大于
     * 地址  精确
     */
    @Test
    public  void test02() {
        QCustomer customer = QCustomer.customer;

        // 通过Id查找
        BooleanExpression and = customer.custName.in("徐庶", "王五")
                .and(customer.custId.gt(0L))  //id大于0
                .and(customer.custAddress.eq("BEIJING")); //地址为BEIJING

        System.out.println(repository.findOne(and));

    }



	//模拟动态查询
    @Test
    public  void test03() {

        Customer params=new Customer();
        params.setCustAddress("BEIJING");
        params.setCustId(0L);
        params.setCustName("徐庶,王五");


        QCustomer customer = QCustomer.customer;

        // 初始条件 类似于1=1 永远都成立的条件
        BooleanExpression expression = customer.isNotNull().or(customer.isNull());

        //这里是使用三目运算来进行判断了,当然也可以使用if来进行判断
        //注意:记得要使用and来进行拼接这个条件
        expression=params.getCustId()>-1?
                expression.and(customer.custId.gt(params.getCustId())):expression;
        expression=!StringUtils.isEmpty( params.getCustName())?
                expression.and(customer.custName.in(params.getCustName().split(","))):expression;
        expression=!StringUtils.isEmpty( params.getCustAddress())?
                expression.and(customer.custAddress.eq(params.getCustAddress())):expression;


        System.out.println(repository.findAll(expression));

    }


    // 解决线程安全问题  如果使用@autowire来对EntityManager进行注入,那可能会出现线程安全问题,使用注解PersistenceContext可以为每一个线程单独绑定一个EntityManager,这样就可以解决线程不安全的问题了
    @PersistenceContext
    EntityManager em;

    /**
     * 自定义列查询、分组
     * 需要使用原生态的方式(Specification)
     * 通过Repository进行查询, 列、表都是固定
     */
    @Test
    public  void test04() {
        JPAQueryFactory factory = new JPAQueryFactory(em);

        QCustomer customer = QCustomer.customer;

        // 构建基于QueryDSL的查询  像写SQL一样来拼接条件    这个Tuple是一个自定义对象,主要是负责来接收你要查询信息,比如你只需要查一张表中的两个字段,实际上是没有对象来接收这个查询结果的两个字段的,而提供的Tuple这个对象就可以用来接收这种类型的数据
        JPAQuery<Tuple> tupleJPAQuery = factory.select(customer.custId, customer.custName)
                .from(customer)
                .where(customer.custId.eq(1L))
                .orderBy(customer.custId.desc());

        // 执行查询
        List<Tuple> fetch = tupleJPAQuery.fetch();

        // 处理返回数据  对结果集进行遍历
        for (Tuple tuple : fetch) {
            System.out.println(tuple.get(customer.custId));
            System.out.println(tuple.get(customer.custName));
        }

    }


	//结合聚合函数进行查询,注意结果集的返回值
    @Test
    public  void test05() {
        JPAQueryFactory factory = new JPAQueryFactory(em);
        QCustomer customer = QCustomer.customer;

        // 构建基于QueryDSL的查询
        JPAQuery<Long> longJPAQuery = factory.select(
                        customer.custId.sum())
                .from(customer)
                //.where(customer.custId.eq(1L))
                .orderBy(customer.custId.desc());

        // 执行查询
        List<Long> fetch = longJPAQuery.fetch();

        // 处理返回数据
        for (Long sum : fetch) {
            System.out.println(sum);
        }

    }

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值