JPA 6.JPQL

JPA

@Author:hanguixian

@Email:hn_hanguixian@163.com


六 JPQL

JPQL语言

  • JPQL语言,即 Java Persistence Query Language 的简称。JPQL 是一种和 SQL 非常类似的中间性和对象化查询语言,它最终会被编译成针对不同底层数据库的 SQL 查询,从而屏蔽不同数据库的差异。

  • JPQL语言的语句可以是 select 语句、update 语句或delete语句,它们都通过 Query 接口封装执行

javax.persistence.Query

  • Query接口封装了执行数据库查询的相关方法。调用 EntityManager 的 createQuery、create NamedQuery 及 createNativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。

  • Query接口的主要方法

    • int executeUpdate() 用于执行update或delete语句。

    • List getResultList() 用于执行select语句并返回结果集实体列表。

    • Object getSingleResult() 用于执行只返回单个结果实体的select语句。

    • Query setFirstResult(int startPosition) 用于设置从哪个实体记录开始返回查询结果。

    • Query setMaxResults(int maxResult) 用于设置返回结果实体的最大数。与setFirstResult结合使用可实现分页查询。

    • Query setFlushMode(FlushModeType flushMode) 设置查询对象的Flush模式。参数可以取2个枚举值:FlushModeType.AUTO 为自动更新数据库记录,FlushMode Type.COMMIT 为直到提交事务时才更新数据库记录。

    • setHint(String hintName, Object value) 设置与查询对象相关的特定供应商参数或提示信息。参数名及其取值需要参考特定 JPA 实现库提供商的文档。如果第二个参数无效将抛出IllegalArgumentException异常。

    • setParameter(int position, Object value) 为查询语句的指定位置参数赋值。Position 指定参数序号,value 为赋给参数的值。

    • setParameter(int position, Date d, TemporalType type) 为查询语句的指定位置参数赋 Date 值。Position 指定参数序号,value 为赋给参数的值,temporalType 取 TemporalType 的枚举常量,包括 DATE、TIME 及 TIMESTAMP 三个,,用于将 Java 的 Date 型值临时转换为数据库支持的日期时间类型(java.sql.Date、java.sql.Time及java.sql.Timestamp)。

    • setParameter(int position, Calendar c, TemporalType type) 为查询语句的指定位置参数赋 Calenda r值。position 指定参数序号,value 为赋给参数的值,temporalType 的含义及取舍同前。

    • setParameter(String name, Object value) 为查询语句的指定名称参数赋值。

    • setParameter(String name, Date d, TemporalType type) 为查询语句的指定名称参数赋 Date 值。用法同前。

    • setParameter(String name, Calendar c, TemporalType type) 为查询语句的指定名称参数设置Calendar值。name为参数名,其它同前。该方法调用时如果参数位置或参数名不正确,或者所赋的参数值类型不匹配,将抛出 IllegalArgumentException 异常。

JPQL语句

select语句用于执行查询。其语法可表示为:

  • select_clause
  • form_clause
  • [where_clause]
  • [groupby_clause]
  • [having_clause]
  • [orderby_clause]

select-from 子句

  • from 子句是查询语句的必选子句。

  • Select 用来指定查询返回的结果实体或实体的某些属性

  • From 子句声明查询源实体类,并指定标识符变量(相当于SQL表的别名)。

  • 如果不希望返回重复实体,可使用关键字 distinct 修饰。select、from 都是 JPQL 的关键字,通常全大写或全小写,建议不要大小写混用。

查询所有实体

  • 查询所有实体的 JPQL 查询字串很简单,例如:
    select o from Order o 或 select o from Order as o

  • 关键字 as 可以省去。

  • 标识符变量的命名规范与 Java 标识符相同,且区分大小写。

  • 调用 EntityManager 的 createQuery() 方法可创建查询对象,接着调用 Query 接口的 getResultList() 方法就可获得查询结果集。

where 子句

  • JPQL也支持包含参数的查询,例如:

    • select o from Orders o where o.id = :myId

      • select o from Orders o where o.id = :myId and o.customer = :customerName
    • 注意:参数名前必须冠以冒号(:),执行查询前须使用Query.setParameter(name, value)方法给参数赋值。
  • 也可以不使用参数名而使用参数的序号,例如:

    • select o from Order o where o.id = ?1 and o.customer = ?2

      • 其中 ?1 代表第一个参数,?2 代表第一个参数。在执行查询之前需要使用重载方法
        Query.setParameter(pos, value) 提供参数值。
        uery query = entityManager.createQuery( "select o from   Orders o where o.id = ?1 and o.customer = ?2" );
        query.setParameter( 1, 2 );
        query.setParameter( 2, "John" );
        List orders = query.getResultList();
        … …

查询部分属性

  • 如果只须查询实体的部分属性而不需要返回整个实体。例如:
    select o.id, o.customerName, o.address.streetNumber from Order o order by o.id

  • 执行该查询返回的不再是Orders实体集合,而是一个对象数组的集合(Object[]),集合的每个成员为一个对象数组,可通过数组元素访问各个属性。

使用 Hibernate 的查询缓存

 String jpql = "FROM Customer c WHERE c.age > ?1";
        Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);

        //占位符的索引是从 1 开始
        query.setParameter(1, 1);
        List<Customer> customers = query.getResultList();
        System.out.println(customers.size());

        query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);

order by子句

  • order by子句用于对查询结果集进行排序。和SQL的用法类似,可以用 “asc“ 和 "desc“ 指定升降序。如果不显式注明,默认为升序。
    select o from Orders o order by o.id
    select o from Orders o order by o.address.streetNumber desc
    select o from Orders o order by o.customer asc, o.id desc

group by子句与聚合查询

  • group by 子句用于对查询结果分组统计,通常需要使用聚合函数。常用的聚合函数主要有 AVG、SUM、COUNT、MAX、MIN 等,它们的含义与SQL相同。例如:
    select max(o.id) from Orders o

  • 没有 group by 子句的查询是基于整个实体类的,使用聚合函数将返回单个结果值,可以使用Query.getSingleResult()得到查询结果。例如:
    Query query = entityManager.createQuery(

                "select max(o.id) from Orders o");

    Object result = query.getSingleResult();
    Long max = (Long)result;
    … …

having子句

  • Having 子句用于对 group by 分组设置约束条件,用法与where 子句基本相同,不同是 where 子句作用于基表或视图,以便从中选择满足条件的记录;having 子句则作用于分组,用于选择满足条件的组,其条件表达式中通常会使用聚合函数。

  • 例如,以下语句用于查询订购总数大于100的商家所售商品及数量:
    select o.seller, o.goodId, sum(o.amount) from V_Orders o group by
    o.seller, o.goodId having sum(o.amount) > 100

  • having子句与where子句一样都可以使用参数。

子查询

  • JPQL也支持子查询,在 where 或 having 子句中可以包含另一个查询。当子查询返回多于 1 个结果集时,它常出现在 any、all、exist s表达式中用于集合匹配查询。它们的用法与SQL语句基本相同。

JPQL函数

  • JPQL提供了以下一些内建函数,包括字符串处理函数、算术函数和日期函数。

  • 字符串处理函数主要有:

    • concat(String s1, String s2):字符串合并/连接函数。

      • substring(String s, int start, int length):取字串函数。

      • trim([leading|trailing|both,] [char c,] String s):从字符串中去掉首/尾指定的字符或空格。

      • ower(String s):将字符串转换成小写形式。

      • upper(String s):将字符串转换成大写形式。

      • ength(String s):求字符串的长度。

      • locate(String s1, String s2[, int start]):从第一个字符串中查找第二个字符串(子串)出现的位置。若未找到则返回0。

    • 算术函数主要有 abs、mod、sqrt、size 等。Size 用于求集合的元素个数。

    • 日期函数主要为三个,即 current_date、current_time、current_timestamp,它们不需要参数,返回服务器上的当前日期、时间和时戳。

update语句

  • update语句用于执行数据更新操作。主要用于针对单个实体类的批量更新

delete语句

  • delete语句用于执行数据更新操作。

示例代码:

  • 常用API
package com.ifox.hgx.jpa.test;

import com.ifox.hgx.jpa.entity.Customer;
import com.ifox.hgx.jpa.entity.Order;
import org.hibernate.jpa.QueryHints;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import javax.persistence.*;
import java.util.List;

public class TestJPQL {

    private EntityManagerFactory entityManagerFactory;
    private EntityManager entityManager;
    private EntityTransaction transaction;

    @Before
    public void init() {
        entityManagerFactory = Persistence.createEntityManagerFactory("jpa_m1");
        entityManager = entityManagerFactory.createEntityManager();
        transaction = entityManager.getTransaction();
        transaction.begin();
    }

    @After
    public void destroy() {
        transaction.commit();
        entityManager.close();
        entityManagerFactory.close();
    }


    //需要注意的是位置参数的是位置前加符号”?”,命名参数是名称前是加符号”:”。
    @Test
    public void testHelloJPQL() {
//        String jpql = "FROM Customer c WHERE c.age > ?1";
//        Query query = entityManager.createQuery(jpql);
//        query.setParameter(1, 1);

        String jpql = "FROM Customer c where c.age > :age";

        Query query = entityManager.createQuery(jpql);

        query.setParameter("age", 1);
        List<Customer> customers = query.getResultList();
        System.out.println(customers.size());

        /**
         * 注意:如果是双向关联的 可能会发生:StackOverflowError
         */
//        for (Customer customer:customers) {
//            System.out.println(customer);
//        }
    }

    //默认情况下, 若只查询部分属性, 则将返回 Object[] 类型的结果. 或者 Object[] 类型的 List.
    //也可以在实体类中创建对应的构造器, 然后再 JPQL 语句中利用对应的构造器返回实体类的对象.
    @Test
    public void testPartlyProperties() {
        String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?1";
        List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();

        System.out.println(result);
        //输出结果:[Customer{id=null, lastName='CCC', email='null', age=34, createTime=null, birth=null, orders=[]}]
    }

    //createNamedQuery 适用于在实体类前使用 @NamedQuery 标记的查询语句
    //在实体类上:@NamedQuery(name = "testNameQuery" ,query = "select c from Customer c where c.age = ?1")
    @Test
    public void testNameQuery() {
        Customer customer = (Customer) entityManager.createNamedQuery("testNameQuery").setParameter(1, 34).getSingleResult();
        System.out.println(customer);
    }

    //createNativeQuery 适用于本地 SQL
    @Test
    public void test() {
        String sql = "select age from JPA_CUSTOMTERS where id = ?";
        Query query = entityManager.createNativeQuery(sql).setParameter(1, 1);
        int age = (int) query.getSingleResult();
        System.out.println("age:" + age);
    }


    //使用 hibernate 的查询缓存.
    @Test
    public void testQueryCache(){
        String jpql = "FROM Customer c WHERE c.age > ?1";
        Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);

        //占位符的索引是从 1 开始
        query.setParameter(1, 1);
        List<Customer> customers = query.getResultList();
        System.out.println(customers.size());

        query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);

        //占位符的索引是从 1 开始
        query.setParameter(1, 1);
        customers = query.getResultList();
        System.out.println(customers.size());
    }

    //查询 order 数量大于 2 的那些 Customer
    @Test
    public void testGroupBy(){
        String jpql = "SELECT o.customer FROM Order o "
                + "GROUP BY o.customer "
                + "HAVING count(o.id) >= 2";
        List<Customer> customers = entityManager.createQuery(jpql).getResultList();

        System.out.println(customers);
    }

    @Test
    public void testOrderBy(){
        String jpql = "FROM Customer c WHERE c.age > ?1 ORDER BY c.age DESC";
        Query query = entityManager.createQuery(jpql).setHint(QueryHints.HINT_CACHEABLE, true);

        //占位符的索引是从 1 开始
        query.setParameter(1, 1);
        List<Customer> customers = query.getResultList();
        System.out.println(customers.size());
    }

    /**
     * JPQL 的关联查询同 HQL 的关联查询.
     *
     * LEFT OUTER JOIN FETCH c.orders
     *
     * FETCH 很关键 会自动封装类
     */
    @Test
    public void testLeftOuterJoinFetch(){
        String jpql = "select c FROM Customer  c LEFT OUTER JOIN FETCH c.orders  WHERE c.id = ?1";

        Customer customer =
                (Customer) entityManager.createQuery(jpql).setParameter(1, 1).getSingleResult();
        System.out.println(customer);
        System.out.println(customer.getLastName());
        System.out.println(customer.getOrders().size());

//        List<Object[]> result = entityManager.createQuery(jpql).setParameter(1, 12).getResultList();
//        System.out.println(result);
    }

    @Test
    public void testSubQuery(){
        //查询所有 Customer 的 lastName 为 YY 的 Order
        String jpql = "SELECT o FROM Order o "
                + "WHERE o.customer = (SELECT c FROM Customer c WHERE c.lastName = ?1)";

        Query query = entityManager.createQuery(jpql).setParameter(1, "KKK");
        List<Order> orders = query.getResultList();
        System.out.println(orders.size());
    }

    //使用 jpql 内建的函数
    @Test
    public void testJpqlFunction(){
        String jpql = "SELECT lower(c.email) FROM Customer c";

        List<String> emails = entityManager.createQuery(jpql).getResultList();
        System.out.println(emails);
    }

    //可以使用 JPQL 完成 UPDATE 和 DELETE 操作.
    @Test
    public void testExecuteUpdate(){
        String jpql = "UPDATE Customer c SET c.lastName = ?1 WHERE c.id = ?2";
        Query query = entityManager.createQuery(jpql).setParameter(1, "YYY").setParameter(2, 1);

        query.executeUpdate();
    }



}
  • 实现分页排序查询
public CarManagementPageDTOS page(CarManagementPageRequest pageRequest) {

    String jpql = "select new com.enduser.dto.CarManagementPage(ee.userName, ec.id, ec.endUserId, ec.parkedStatus,ec.plateNumber, ec.status, ecc.vin,ecc.imgUrl)" + "from com.entity.enduer.EndUserCarInfoEO ec left join EndUserCarCeEO ecc on ec.id = ecc.endUserCarInfoId left join com.entity.enduer.EndUserEO ee on ec.endUserId = ee.id where 1=1 ";

    //拼装条件
    if (pageRequest.getUserName() != null) {
        jpql = jpql + " and ec.endUserId in ( SELECT id FROM com.entity.enduer.EndUserEO er WHERE er.userName LIKE :userName) ";
    }
    if (pageRequest.getPlateNumber() != null) {
        jpql = jpql + " and ec.plateNumber  like :plateNumber ";
    }

    //排序
    jpql = jpql + " order by ec.modifyDate DESC  ";

    //创建jpql查询
    Query query = entityManager.createQuery(jpql);

    //设置参数
    if (pageRequest.getUserName() != null) {
        query.setParameter("userName", "%" + pageRequest.getUserName() + "%");
    }
    if (pageRequest.getPlateNumber() != null) {
        query.setParameter("plateNumber", "%" + pageRequest.getPlateNumber() + "%");
    }

    //得到总的记录数
    int totalCount = query.getResultList().size();


    /**
    public class PageDetail {
        //总记录数
        private int totalCount;
        //当页数量
        private int pageSize;
        //页码
        private int pageNo;
        //....
    */

    //设置分页信息
    PageResponseDetail pageResponseDetail = new PageResponseDetail();
    pageResponseDetail.setPageNo(pageRequest.getPageNo() - 1);
    pageResponseDetail.setPageSize(pageRequest.getPageSize());
    pageResponseDetail.setTotalCount(totalCount);

    //得到查询内容的实体
    List<CarManagementPageDTO> carManagementPageDTOList = query.setFirstResult((pageRequest.getPageNo() - 1) * pageRequest.getPageSize()).setMaxResults(pageRequest.getPageSize()).getResultList();

    /**
        public class CarManagementPageDTOS {
            //查询信息封装的实体
            private List<CarManagementPageDTO> carManagementPageDTOList ;
            //分页实体
            private PageResponseDetail pageResponseDetail ;
            //....
    */
    CarManagementPageDTOS carManagementPageDTOS = new CarManagementPageDTOS(carManagementPageDTOList, pageResponseDetail);
    //封装了查询的信息,分页信息
    return carManagementPageDTOS;
}
全部评论

相关推荐

投递美团等公司10个岗位
点赞 评论 收藏
转发
点赞 收藏 评论
分享
牛客网
牛客企业服务