当前位置: 首页 > news >正文

JPA 学习笔记 4:JPQL

JPA 学习笔记 4:JPQL

二级缓存

示例:

Order order1 = entityManager.find(Order.class, 1L);
Order order2 = entityManager.find(Order.class, 1L);
System.out.println(order1.getOrderName());

实际执行时 Hibernate 只会执行一次 SELECT 语句进行查询,因为这两次查询是在一个 Session 内,Hibernate 默认启用一级缓存,所以只会有一次查询。

这里人为将两次查询放在两个 Session 内:

Order order1 = entityManager.find(Order.class, 1L);
// 提交事务,重启一个事务
entityTransaction.commit();
entityManager.close();
entityManager = entityManagerFactory.createEntityManager();
entityTransaction = entityManager.getTransaction();
entityTransaction.begin();
Order order2 = entityManager.find(Order.class, 1L);
System.out.println(order1.getOrderName());

此时会有两条 SELECT 语句被执行。可以使用 Hibernate 的二级缓存来避免重复查询,因为二级缓存是跨事务的,在一个 EntityManagerFactory 内都有效。

要开启 Hibernate 的二级缓存需要添加以下依赖:

<!-- hibernate jcache -->
<dependency><groupId>org.hibernate.orm</groupId><artifactId>hibernate-jcache</artifactId><version>${hibernate.version}</version>
</dependency>
<!-- jcache 实现,这里使用 ehcache -->
<dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId><version>3.11.1</version><exclusions><exclusion><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId></exclusion><exclusion><artifactId>slf4j-api</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions>
</dependency>
<!-- JCache API -->
<dependency><groupId>javax.cache</groupId><artifactId>cache-api</artifactId><version>1.1.1</version>
</dependency>
<!-- JAXB运行时 -->
<dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>4.0.5</version>
</dependency>

简单解释一下依赖:

  • jcache,一种 sun 制订的缓存标准,这里的 cache-api 是具体的 API
  • hibernate-jcache,Hibernate 使用 jcache 连接缓存的 jar 包
  • ehcache,一种实现了 jcache 接口的缓存,还有其他实现,具体可以看这里

需要说明的是,这里的依赖适用于 JDK21 + Hibernate7 + Ehcache3 的组合,如果是其他版本,可能引入的 jar 包略有不同,这里牵扯高版本的 JDK 剥离某些 jar 包的问题,具体问题具体分析即可。

修改 JPA 配置文件,开启二级缓存:

<!-- 二级缓存 -->
<property name="hibernate.cache.use_second_level_cache" value="true"/>
<property name="hibernate.cache.region.factory_class" value="org.hibernate.cache.jcache.internal.JCacheRegionFactory"/>
<property name="hibernate.cache.use_query_cache" value="true"/>
<property name="hibernate.javax.cache.provider" value="org.ehcache.jsr107.EhcacheCachingProvider"/>

设置二级缓存策略:

<!-- 二级缓存策略 -->
<shared-cache-mode>ENABLE_SELECTIVE</shared-cache-mode>

可以选择的策略有:

  • ALL:所有实体类都被缓存
  • NONE:所有实体类都不被缓存
  • ENABLE_SELECTIVE:标识 @Cacheable(true) 注解的实体类将被缓存
  • DISABLE_SELECTIVE:除标识 @Cacheable(false) 以外的所有实体将被缓存
  • UNSPECIFIED:默认值,JPA 产品默认值将被使用

添加 Ehcache 的配置文件src/main/resources/ehcache.xml

<config xmlns="http://www.ehcache.org/v3"xmlns:jsr107="http://www.ehcache.org/v3/jsr107"><service><jsr107:defaults enable-management="true" enable-statistics="true"/></service><cache alias="entityCache"><key-type>java.lang.Object</key-type><value-type>java.lang.Object</value-type><expiry><ttl unit="minutes">10</ttl></expiry><resources><heap unit="entries">1000</heap><offheap unit="MB">10</offheap></resources></cache><cache alias="queryCache"><key-type>java.lang.Object</key-type><value-type>java.lang.Object</value-type><expiry><ttl unit="minutes">5</ttl></expiry><resources><heap unit="entries">1000</heap></resources></cache>
</config>

为需要缓存的实体类(这里是Order)添加注解:

@Cacheable
@Entity
@Table(name = "tb_order")
@Data
@NoArgsConstructor
public class Order {// ...
}

再次执行测试用例就能看到二级缓存生效,SELECT 语句不会重复执行。

JPQL

JPQL(Java Persistence Query Language)是一种和 SQL 类似的中间性对象化查询语言,和 SQL 不同的是它其中使用的是实体类和属性,而不是表和字段。

快速开始

使用 JPQL 语句进行查询:

String jpql = "SELECT c FROM Customer c WHERE c.age>?1";
Query query = entityManager.createQuery(jpql);
query.setParameter(1, 10);
List<?> customers = query.getResultList();
System.out.println(customers);

JPQL 中的?1是位置参数,使用query.setParameter可以设置对应位置的参数值。

更常见的方式是使用命名参数:

String jpql = "SELECT c FROM Customer c WHERE c.age>:age";
Query query = entityManager.createQuery(jpql);
query.setParameter("age", 10);

如果查询结果包含全部的实体属性,JPQL 中的 SELECT 部分也可以省略:

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

当然也可以让查询结果只返回部分属性:

String jpql = "SELECT c.age,c.lastName FROM Customer c WHERE c.age>:age";
Query query = entityManager.createQuery(jpql);
query.setParameter("age", 10);
List<?> customers = query.getResultList();
System.out.println(customers);

需要注意的是,此时customers中的每个元素是 Object[] 类型,这样可能对后续数据的处理和展示带来不便,因此可以这样:

String jpql = "SELECT new Customer(c.lastName,c.age) FROM Customer c WHERE c.age>:age";

在 JPQL 中显式地使用实体类的构造器封装了返回的结果,此时返回的结果都会是Customer对象。当然,这需要为实体类添加对应的构造器:

public class Customer {// ...public Customer(String lastName, Integer age) {this.lastName = lastName;this.age = age;}
}

可以在实体类上使用@NamedQuery定义具名 Query:

@Entity
@Table(name = "tb_customer")
@Data
@ToString
@NamedQuery(name = "Customer.findByLastName", query = "select c from Customer c where c.lastName = :lastName")
public class Customer {// ...
}

执行具名 Query:

Query query = entityManager.createNamedQuery("Customer.findByLastName");
query.setParameter("lastName", "张三");
List<?> customers = query.getResultList();
System.out.println(customers);

也可以通过 JPA 执行原生 SQL:

Query query = entityManager.createNativeQuery("SELECT * FROM tb_customer");
List<?> customers = query.getResultList();
System.out.println(customers);

查询缓存

示例:

Query query = entityManager.createNativeQuery("SELECT * FROM tb_customer");
List<?> customers = query.getResultList();
System.out.println(customers);
Query query2 = entityManager.createNativeQuery("SELECT * FROM tb_customer");
List<?> customers2 = query2.getResultList();
System.out.println(customers2);

会执行两条 SELECT 语句。

使用查询缓存:

Query query = entityManager.createNativeQuery("SELECT * FROM tb_customer").setHint(AvailableHints.HINT_CACHEABLE, true);
List<?> customers = query.getResultList();
System.out.println(customers);
Query query2 = entityManager.createNativeQuery("SELECT * FROM tb_customer").setHint(AvailableHints.HINT_CACHEABLE, true);;
List<?> customers2 = query2.getResultList();
System.out.println(customers2);

只会执行一次 SELECT 语句。

当然,前提是已经在 JPA 配置中开启了 Hibernate 的查询缓存:

<property name="hibernate.cache.use_query_cache" value="true"/>

Group By

在 JPQL 中同样可以使用 Group By:

TypedQuery<Customer> query = entityManager.createQuery("SELECT o.customer FROM Order o " +"GROUP BY o.customer HAVING count(o.customer)>=3", Customer.class);
List<Customer> customers = query.getResultList();
System.out.println(customers);

关联查询

示例:

TypedQuery<Person> query = entityManager.createQuery("select p from Person p where p.id=:id", Person.class);
query.setParameter("id", 1L);
List<Person> persons = query.getResultList();
System.out.println(persons);

实体PersonCar是一对多的双向关联关系,这里查询会使用两条 SELECT 获取结果。

可以在 JPQL 中使用外连接,这样使用一条 SQL 就能查询出结果:

TypedQuery<Person> query = entityManager.createQuery("select p from Person p left outer join fetch p.cars where p.id=:id", Person.class);
query.setParameter("id", 1L);
List<Person> persons = query.getResultList();
System.out.println(persons);

子查询

JPQL 也可以使用子查询:

TypedQuery<Order> query = entityManager.createQuery("select o from Order o where o.customer in ((select c from Customer c where c.age>:age))", Order.class);
query.setParameter("age", 20);
List<Order> orders = query.getResultList();
System.out.println(orders);

函数

JPQL 也可以使用预定义函数,这些函数的命名和用途与 SQL 中的函数类似:

TypedQuery<String> query = entityManager.createQuery("select concat(c.lastName,'(',c.birth,')') from Customer c", String.class);
List<String> names = query.getResultList();
System.out.println(names);

UPDATE

JPQL 也可以完成更新操作:

entityManager.createQuery("update Customer c set c.lastName=:lastName where c.id=:id").setParameter("id", 1L).setParameter("lastName", "张三丰").executeUpdate();

本文的所有示例代码可以从这里获取。

参考资料

  • 尚硅谷jpa开发教程全套完整版
http://www.dtcms.com/a/498670.html

相关文章:

  • Linux网络编程:Socket编程UDP
  • 详解Redis锁误删、原子性难题及Redisson加锁底层原理、WatchDog续约机制
  • 【Java 集合】核心知识点梳理
  • 郑州建设厅官方网站地方网站推广
  • 宁波网站建设建站厂家wordpress 站点描述
  • 兴扬汽车网站谁做的公司设计图
  • 上海石化有做网站设计的吗广西网络广播电视台直播
  • 网站和推广在一家做的好处卓智网络科技有限公司
  • 推广网站有哪些做网站销售水果
  • 产品网站推广淄博做网站建设的公司
  • 整站下载器 安卓版企业网站多大空间够用
  • 博罗做网站战队头像在线制作免费
  • 东莞专业网站建设推广欧洲c2c平台
  • 手机网站做指向沃尔玛网上商城可以用购物卡吗
  • 中国建设银行北京市互联网网站成都小程序开发公司找哪家
  • 电商网站要素如何提升网站营销力
  • 成都模板网站建设服务深圳创纪录暴雨19小时
  • 网站地图后缀响应式博客网站模板
  • 嘉兴企业网站模板聚美优品
  • 展示网站模版源码wordpress 2栏主题
  • 网站建设与排名网站广告位代码
  • 营销型企业网站策划方案村网站开设两学一做栏目
  • 靓号网建站百度推广手机app下载
  • WordPress网站打不开nginx关wordpress更新
  • 四川省住房与城乡建设厅网站官网wordpress 中文版 英文版
  • aspx网站模板手机模块网站
  • 单位网站建设程序网站建设与推广的实训报告
  • 什么网站可以做投资做网站需要关注哪些
  • 做网站补贴网页广告拦截
  • 网站优化关键词怎么做贵阳搜索引擎排名推广