Skip to content
On this page

JPQL

除了方法命名查询,Spring Data Jpa还提供了一种类似于SQL语句面向对象的查询语句JPQL

相对于命名查询,JPQL涵盖更多的数据库操作,它可以做到命名查询无法做到的复杂的SQL操作,比如连接查询、批量更新和删除、分组聚合等

  • JPQL语法的基本概念是语句中出现的不再是表名、列名,而是java实体类名和属性名,并且区分大小写。JPQL的关键字和SQL的一样,不区分大小写。当查询全属性时,不能使用"select *"、而要通过"select 别名"的形式查询

对比:

// SQL,查询所有
select * from employee e
// JPLQ语句,查询全属性
select e from Employee e
// JPQL语句,查询全属性的一种简写形式
from Employee e

// SQL语句,查询name列
select e.name from employee e
// JPQL语句,查询name列
select e.name from Employee e

通过@Query注解在持久层的方法上书写JPQL语句,这样当调用该方法时就会通过JPQL与定义的逻辑操作数据库

语法

运算符

  • jpql提供算术运算符,其算术运算符和标准的sql一致
sql
select emp from Employee emp where (emp.age - 5) = 20
  • jpql提供的关系运算,其关系运算符与标准的sql一致
sql
select emp from Employee whhere emp.age > 20
  • JPQL的逻辑运算符符合java面向对象的特性,有between and、like、in、is null、is empty、not、and、or,其中is null用来判断普通属性值是否为null,is empty用来判断属性值是否为null
sql
select emp from Employee emp where emp emp.age between 20 and 30

函数

  • 字符串有concat、substring、trim、upper、lower、locate等,其中concat用来拼接字符串,trom用来去掉字符串的前后空格,length用来得到字符串的长度,locate是定位函数

  • 时间函数有CURRENT_DATE(日期)、CURRENT_TIME(时间)和CURRENT_TIMESTAMP(日期时间)

  • 算数函数有ABS(绝对值)、SQRT(平方根)、MOD(取模)、SIZE(集合数量)

分组查询

  • 和sql一样,jpql也使用group by ...having

  • 聚合函数有avg(distint)、sum(distint)、count(distint)、min(distint)、max(distint)

连接查询

  • jPQL的连接查询和SQL的查询显著不同,根本原因是其类与类的关系是包含关系,连接时可以通过自身属性直接关联
sql
select emp.name, emp.userNumber, dept.deptCode from Employee left join emp.dept dept
  • 使用fetch可以及时查询相关联的数据,但该数据只包含在使用对象中,不会出现在结果集中
sql
select emp from Employee left join fetch emp.dept

参数查询

  • 如果语句需要获取动态的参数,则可以使用将" :参数名 "作为占位符的形式指定动态参数,参数名只需要和方法的形式参数名保持一致即可
java
@Query(value = "select emp from Employee emp where emp.name = :name")
    List<Employee> findByName(String name);
  • 如果使用实体类型的参数,则需要使用" #{} "的形式,并且实体参数前也要加" # "号。
  • 注意:必须增加@Param("employee"),否则传入的实体类对象属性无法注入,会报gender为null
java
@Query(value = "select emp from Employee emp where emp.gender like concat('%', :#{#employee.gender}, '%') ")
    List<Employee> findByGender(@Param("employee") Employee employee);
java
@Test
    public void test1() {
        Employee emp = new Employee();
        emp.setGender("");
        List<Employee> byGender = employeeRepository.findByGender(emp);
        for (Employee employee : byGender) {
            logger.info(employee.getName());
        }
    }

本地SQL

  • Spring data Jpa处理提供JPQL查询,也给开发者提供了使用原生sql的权利,即使用@Query注解的权利。当value属性中添加sql语句时,可以使用nativeQuery=true来标记这是一条原生sql
  • 但一般不推荐使用这种方法,因为使用它相当于使用mybatis框架,将sql与java分离,并将结果集整理成集合返回,这不溯河spring data jpa的ORM思想
java
@Query(value = "select emp.name from employee emp where age between :startAge and :endAge ;", nativeQuery = true)
    List<String> findByAgeBetween(@Param("startAge") Integer startAge, @Param("endAge") Integer endAge);
java
@Test
    public void test2() {
        List<String> list = employeeRepository.findByAgeBetween(9, 11);
        for (String name : list) {
            System.out.println(name);
        }
    }
  • 注意返回值类型必须是:List<String> 不可以是 List<Employee> 否则会报nested exception is org.hibernate.exception.SQLGrammarException: could not execute query 异常。需要根据查询数据,匹配返回值类型只能使用Map<String, Object>进行接收。 如下示例:
@Query(value = "select emp.name, emp.user_number from employee emp where age between :startAge and :endAge ;", nativeQuery = true)
    List<Map<String, Object>> findByAgeBetween(@Param("startAge") Integer startAge, @Param("endAge") Integer endAge);
java
@Test
    public void test2() {
        List<Map<String, Object>> list = employeeRepository.findByAgeBetween(9, 11);
        for (Map employee : list) {
            logger.info((String) employee.get("name"));
        }
    }
  • 不能自定义DTO类进行接收,JPA 原生 sql 的返回不能直接转换成对象,原生 sql 的返回形式是 Map 、 List<Map> 或 Object,因此可以使用 List<Map<String,Object>> 接收

增删改

  • jpql没有提供inset方法,这是因为insert操作不涉及条件筛选,通过JpaRepository提供的save方法即可
  • 如果update操作通过save方法进行修改,则只能通过id的方式筛选对象。如果想通过其他属性找到对象并进行修改,则save方法无法满足需求,同理delete操作也有通过非id属性筛选的情况。即如果想要通过id属性进行修改和删除操作,则save方法和deleteById方法就可以满足需求,但是如果西昂要通过其他属性进行修改和删除,则需要通过JPQL方式实现
  • 如果要进行修改和删除的JQPL操作,则要在该方法活该类上添加@Tansactional事务注解,另外还需要在方法上加@Modifying注解,该注解标记此方法是一个修改和删除的方法,其返回类型是void或者Integer。整数结果的值是数据库受到影响的行数
java
@Transactional
    @Modifying
    @Query(value = "update Employee emp set emp.name = :name where emp.userNumber = :userNumber")
    Integer updateNameByUserNumber(@Param("name") String name, @Param("userNumber") Integer userNumber);