JPQL-Query查询实例详解

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS PostgreSQL,高可用系列 2核4GB
简介: JPQL-Query查询实例详解

前面学习了JPQL语言和Query接口。这里学习一下如果通过JPQL和Query接口进行数据的查询、更新和删除。

【1】普通查询

首先说明一下FROM子句和Select…FROM。

  • from 子句是查询语句的必选子句。
  • Select 用来指定查询返回的结果实体或实体的某些属性。
  • From 子句声明查询源实体类,并指定标识符变量(相当于SQL表的别名)。


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

代码实例如下:

@Test
public void testHelloJPQL(){
  String jpql = "select c FROM Customer c WHERE c.age > ?";
  Query query = (Query) entityManager.createQuery(jpql);
  //占位符的索引是从 1 开始
  query.setParameter(1, 1);
  List<Customer> customers = query.getResultList();
  System.out.println(customers.size());
}

控制台输出如下:

可以看到就是一条普通的查询语句。

但是在写JPQL时,需要注意,这一切面向对象。即对象+属性相当于表+列,可以联想一下Hibernate的HQL语言。

如果不写 select 而是直接使用from子句,表明获取对象的全部属性。

String jpql = FROM Customer c WHERE c.age > ?;


需要注意的是查询一个对象的所有属性时,并不能像MySQL那样使用 * 号标志。下面语句是错误的,不符合JPQL的规范。

select * FROM Customer c WHERE c.age > ?


当然,你可以查询部分属性

默认情况下, 若只查询部分属性, 则将返回 Object[] 类型的结果. 或者 Object[] 类型的 List。

.

也可以在实体类中创建对应的构造器, 然后再 JPQL 语句中利用对应的构造器返回实体类的对象.

代码实例如下:

@Test
public void testPartlyProperties(){
  String jpql = "SELECT c.lastName, c.age FROM Customer c WHERE c.id > ?";
  List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();
  System.out.println(result);
  Object[] objects = (Object[]) result.get(0);
  System.out.println(objects[0]);
}

控制台输出如下:

可以看到,获取到的list是一个list<object[]>,解析起来是比较麻烦的。故而,我们更希望使用如下方式:

@Test
public void testPartlyProperties(){
  String jpql = "SELECT new Customer(c.lastName, c.age) FROM Customer c WHERE c.id > ?";
  List result = entityManager.createQuery(jpql).setParameter(1, 1).getResultList();
  System.out.println(result);
}

控制台输出如下:

这样获取到的是list<Customer>,这样解析起来就很友好了。

【2】createNamedQuery和createNativeQuery

① createNamedQuery 适用于在实体类前使用 @NamedQuery 标记的查询语句。

代码示例如下:

@NamedQuery(name="testNamedQuery", query="FROM Customer c WHERE c.id = ?")
@Cacheable(true)
@Table(name="jpa_cutomers")
@Entity
public class Customer {
  private Integer id;
  private String lastName;
  private String email;
  private int age;
  private Date createdTime;
  private Date birth;
  public Customer() {
  }
//...
}

测试代码实例如下:

@Test
public void testNamedQuery(){
  Query query = entityManager.createNamedQuery("testNamedQuery").setParameter(1, 3);
  Customer customer = (Customer) query.getSingleResult();
  System.out.println(customer);
}

控制台输出如下:

② createNativeQuery 适用于本地 SQL

即,你可以像MySQL那样写sql语句进行查询。

代码实例如下:

@Test
public void testNativeQuery(){
  String sql = "SELECT age FROM jpa_cutomers WHERE id = ?";
  Query query = entityManager.createNativeQuery(sql).setParameter(1, 3);
  Object result = query.getSingleResult();
  System.out.println(result);
}

控制台输出如下:


【3】(Hibernate)查询缓存

① 不使用查询缓存

多次查询同条语句,代码实例如下:

@Test
public void testQueryCache(){
String jpql = "FROM Customer c WHERE c.age > ?";
Query query = entityManager.createQuery(jpql);
//占位符的索引是从 1 开始
query.setParameter(1, 1);
List<Customer> customers = query.getResultList();
System.out.println(customers.size());
query = entityManager.createQuery(jpql);
//占位符的索引是从 1 开始
query.setParameter(1, 1);
customers = query.getResultList();
System.out.println(customers.size());
}

控制台输出如下:


② 使用查询缓存

persistence.xml配置如下:

<!-- 二级缓存相关 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.ehcache.EhCacheRegionFactory"/>
<!--  这里表明使用查询缓存 -->
<property name="hibernate.cache.use_query_cache" value="true"/>

代码实例如下:

@Test
public void testQueryCache(){
  String jpql = "FROM Customer c WHERE c.age > ?";
  // QueryHints.HINT_CACHEABLE设置为true
  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());
}

控制台输出如下:

【4】更新和删除

JPQL的更新和删除主要 用的是query.executeUpdate()方法;

代码实例如下:

@Test
public void testExecuteUpdate(){
  String jpql = "UPDATE Customer c SET c.lastName = ? WHERE c.id = ?";
  Query query = entityManager.createQuery(jpql).setParameter(1, "YYY").setParameter(2, 12);
  query.executeUpdate();
}

控制台输出如下:

删除同上。

【5】order by,group by 和having子句

JPQL是面向对象的,和hibernate一致。此外,像排序、分组等和普通MySQL并无差异。

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;

group by实例:

//查询 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);
}

Having 子句用于对 group by 分组设置约束条件,用法与where 子句基本相同。

不同是 where 子句作用于基表或视图,以便从中选择满足条件的记录;having 子句则作用于分组,用于选择满足条件的组,其条件表达式中通常会使用聚合函数。


order by 实例:

@Test
public void testOrderBy(){
  String jpql = "FROM Customer c WHERE c.age > ? 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());
}

order by子句用于对查询结果集进行排序。和SQL的用法类似,可以用 “asc“ 和 "desc“ 指定升降序。

如果不显式注明,默认为升序。

【6】关联查询与Fetch

在JPQL中,很多时候都是通过在实体类中配置实体关联的类属性来实现隐含的关联(join)查询。

例如:

select o from Orders o where o.address.streetNumber=2000 

上述JPQL语句编译成以下SQL时就会自动包含关联,默认为左关联。

在某些情况下可能仍然需要对关联做精确的控制。为此,JPQL 也支持和 SQL 中类似的关联语法。

如:

left out join / left join 
inner join 
left join / inner join fetch 

其中,left join和left out join等义,都是允许符合条件的右边表达式中的实体为空。

左外连接实例如下:

@Test
public void testLeftOuterJoinFetch(){
  String jpql = "FROM Customer c LEFT OUTER JOIN FETCH c.orders WHERE c.id = ?";
  Customer customer = 
      (Customer) entityManager.createQuery(jpql).setParameter(1, 7).getSingleResult();
  System.out.println(customer.getLastName());
  System.out.println(customer.getOrders().size());
}

控制台输出如下:


需要注意的是,这里JPQL用到了FETCH。如果不加FETCH呢?

代码实例如下:

@Test
public void testLeftOuterJoinFetch(){
  String jpql = "FROM Customer c LEFT OUTER JOIN  c.orders WHERE c.id = ?";
  List<Object[]> result = entityManager.createQuery(jpql).setParameter(1, 7).getResultList();
  System.out.println(result);
}

控制台输出如下:

“fetch”连接允许仅仅使用一个选择语句就将相关联的对象或一组值的集合随着他们的父对象的初始化而被初始化。

在默认的查询中,Entity中的集合属性默认不会被关联,集合属性默认是延迟加载( lazy-load )。那么,left fetch/left out fetch/inner join fetch提供了一种灵活的查询加载方式来提高查询的性能。

综上,在使用JPQL语言时 ,需要时刻记得和hibernate一致–面向对象。如果你使用的Query为createNativeQuery,才可以像使用普通MySQL一样进行数据操作。

【7】子查询

JPQL也支持子查询,在 where 或 having 子句中可以包含另一个查询。

当子查询返回多于 1 个结果集时,它常出现在 any、all、exist s表达式中用于集合匹配查询。

它们的用法与SQL语句基本相同。

子查询实例如下:

@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 = ?)";
  Query query = entityManager.createQuery(jpql).setParameter(1, "YY");
  List<Order> orders = query.getResultList();
  System.out.println(orders.size());
}

控制台输出如下:



相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
12月前
|
Java Spring 容器
如何解决spring EL注解@Value获取值为null的问题
本文探讨了在使用Spring框架时,如何避免`@Value(&quot;${xxx.xxx}&quot;)`注解导致值为null的问题。通过具体示例分析了几种常见错误场景,包括类未交给Spring管理、字段被`static`或`final`修饰以及通过`new`而非依赖注入创建对象等,提出了相应的解决方案,并强调了理解框架原理的重要性。
656 4
SpringBoot之文件上传(单文件与多文件上传的使用)
SpringBoot之文件上传(单文件与多文件上传的使用)
239 1
|
Docker Ruby 容器
docker安装gitlab-ee并破解
本文介绍docker安装和gitlab-ee的破解方法
docker安装gitlab-ee并破解
|
机器学习/深度学习 Linux Python
服务器上后台运行python程序
服务器上后台运行python程序
1194 0
|
9月前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
10412 46
|
存储 Java 数据库连接
JPA 之 Hibernate EntityManager 使用指南
JPA 之 Hibernate EntityManager 使用指南
1386 0
|
Linux Windows
IDEA如何查看每一行代码的提交记录(人员,时间)
【9月更文挑战第24天】在IntelliJ IDEA中,可通过安装GitToolBox插件并利用其功能来便捷地查看每行代码的提交记录,包括提交者、时间和提交信息。具体操作为:首先安装GitToolBox插件,然后在代码编辑区域将鼠标悬停于目标代码行以查看简要信息,或使用快捷键打开“Version Control”窗口查看详细提交历史。
5501 2
|
SQL 安全 Java
Spring Boot 学习研究笔记(十七) -Spring boot JPA的复杂查询
Spring Boot 学习研究笔记(十七) -Spring boot JPA的复杂查询
720 0
|
资源调度 算法
深入理解网络中的死锁和活锁现象
【8月更文挑战第24天】
591 0
SpringMVC拦截器的介绍,拦截器的基本实现,拦截器链配置
SpringMVC拦截器的介绍,拦截器的基本实现,拦截器链配置
240 2