锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. 【Spring Boot】JPA 的查询方式

【Spring Boot】JPA 的查询方式

0
  • 软件开发
  • 发布于 2024-08-17
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

《JPA 从入门到精通》系列包含以下文章:

  • Java 持久层 API:JPA
  • 认识 JPA 的接口
  • JPA 的查询方式
  • 基于 JPA 开发的文章管理系统(CRUD)
  • 关系映射开发(一):一对一映射
  • 关系映射开发(二):一对多映射
  • 关系映射开发(三):多对多映射

JPA 的查询方式

  • 1. 使用约定方法名
  • 2. 用 JPQL 进行查询
  • 3. 用原生 SQL 进行查询
    • 3.1 根据 ID 查询用户
    • 3.2 查询所有用户
    • 3.3 根据 email 查询用户
    • 3.4 根据 name 查询用户,并返回分页对象 Page
    • 3.5 根据名字来修改 email 的值
    • 3.6 使用事务
  • 4. 用 Specifications 进行查询
  • 5. 用 ExampleMatcher 进行查询
  • 6. 用谓语 QueryDSL 进行查询
  • 7. 用 NamedQuery 进行查询
  1. 使用约定方法名

约定方法名一定要根据命名规范来写,Spring Data 会根据前缀、中间连接词(Or、And、Like、NotNull 等类似 SQL 中的关键词)、内部拼接 SQL 代理生成方法的实现。

约定方法名的方法见下表(不完全示例):

SQL方法例子JPQL 语句
andfindByLastnameAndFirstnamewhere x.lastname = ?1 and x.firstname = ?2
orfindByLastnameOrFirstnamewhere x.lastname = ?1 or x.firstname = ?2
=findByFirstname / findByFirstnameIs / findByFirstnameEqualswhere x.firstname = ?1

接口方法的命名规则也很简单,只要明白 And、Or、Is、Equal、Greater、StartingWith 等英文单词的含义,就可以写接口方法。具体用法如下:

public interface UserRepository extends Repository<User, Long> {
    List<User> findByEmailOrName(String email, String name);
}

上述代码表示,通过 email 或 name 来查找 User 对象。

约定方法名还可以支持以下几种语法:

  • User findFirstByOrderByNameAsc()
  • Page<User> queryFirst100ByName(String name, Pageable pageable)
  • Slice<User> findTop100ByName(String name, Pageable pageable)
  • List<User> findFirst100ByName(String name, Sort sort)
  • List<User> findTop100ByName(String name, Pageable pageable)
  1. 用 JPQL 进行查询

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

JPQL 语言通过 Query 接口封装执行,Query 接口封装了执行数据库查询的相关方法。调用 EntityManager 的 Query、NamedQuery 及 NativeQuery 方法可以获得查询对象,进而可调用 Query 接口的相关方法来执行查询操作。

JPQL 是面向对象进行查询的语言,可以通过自定义的 JPQL 完成 UPDATE 和 DELETE 操作。JPQL 不支持使用 INSERT。对于 UPDATE 或 DELETE 操作,必须使用注解 @Modifying 进行修饰。

JPQL 的用法见以下两段代码。

(1)下面代码表示根据 name 值进行查找。

public interface UserRepository extends JpaRepository<User, Long> (
  @Query("select u from User u where u.name = ?1")
  User findByName(String name);
}

(2)下面代码表示根据 name 值进行模糊查找。

public interface UserRepository extends JpaRepository<User, Long> { 
	@Query("select u from User u where u.name like %?1")
	List<User> findByName(String name);
}
  1. 用原生 SQL 进行查询

在使用原生 SQL 查询时,也使用注解 @Query。此时,nativeQuery 参数需要设置为 true。 下面先看一些简单的查询代码。

3.1 根据 ID 查询用户

public interface UserRepository extends JpaRepository<User, Long> (
	// 根据 ID 查询用户
	@Query(value = "select * from user u where u.id=:id", nativeQuery = true)
	User findById(@Param("id")Long id);
)

3.2 查询所有用户

public interface UserRepository extends JpaRepository<User, Long> {
	// 查询所有用户
	@Query(value = "select * from user", nativeQuery = true)
	List<User> findAllNative();
}

3.3 根据 email 查询用户

public interface UserRepository extends JpaRepository<User, Long> (
	// 根据 email 查询用户
	@Query(value = "select * from user where email= ?1", nativeQuery = true) 
	User findByEmail(String email);
)

3.4 根据 name 查询用户,并返回分页对象 Page

public interface UserRepository extends JpaRepository<User, Long> (
	@Query(value = "select * from user where name= ?1",
		   countQuery = "select count(*) from user where name= ?1", nativeQuery = true)
	Page<User> findByName(String name, Pageable pageable);
}

3.5 根据名字来修改 email 的值

@Modifying 
@Query("update user set email = :email where name =:name")
Void updateUserEmailByName(@Param("name")String name, @Param("email")String email);

3.6 使用事务

UPDATE 或 DELETE 操作需要使用事务。此时需要先定义 Service 层,然后在 Service 层的方法上添加事务操作。

对于自定义的方法,如果需要改变 Spring Data 提供的事务默认方式,则可以在方法上使用注解 @Transactional,如以下代码:

@Service
public classUserService {
  @Autowired
  private UserRepository userRepository;
  @Transactional
  public void updateEmailByName(String name, String email) {
    userRepository.updateUserEmaiByName(name, email);
  }
)

测试代码:

@Test
public void testUsingModifingAnnotation() {
  userService.updateEmailByName("pipi", "88888888@qq.com");
}

在进行多个 Repository 操作时,也应该使这些操作在同一个事务中。按照分层架构的思想,这些操作属于业务逻辑层,因此需要在 Service 层实现对多个 Repository 的调用,并在相应的方法上声明事务。

  1. 用 Specifications 进行查询

如果要使 Repository 支持 Specification 查询,则需要在 Repository 中继承 JpaSpecificationExecutor 接口,具体使用见如下代码:

public interface CardRepository extends JpaRepository<Card,Long>, JpaSpecificationExecutor<Card> {
	Card findById(long id);
}

下面以一个例子来说明 Specifications 的具体用法:

@SpringBootTest
@RunWith(SpringRunner.class)
public class testJpaSpecificationExecutor (
  @Autowired
  private CardRepository cardRepository;
  @Test
  public void testJpaSpecificationExecutor() (
    int pageNo = 0;
    int pageSize = 5;
    PageRequest pageable = PageRequest.of(pageNo, pageSize);
    // 通常使用 Specification 的匿名内部类
    Specification<Card> specification = new Specification<Card>() {
        @Override
        public Predicate toPredicate(Root<Card> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
          Path path = root.get("id");
          // gt 是大于的意思。这里代表 id 大于 2
          Predicate predicate1 = cb.gt(path, 2);
          // equal 是等于的意思,代表查询 num 值为 422803 的数据记录
          Predicate predicate2 = cb.equal(root.get("num"), 422803);
          // 构建组合的 Predicate
          Predicate predicate = cb.and(predicate1, predicate2); 
          return predicate;
        }
    };
    Page<Card> page = cardRepository.findAll(specification, pageable);
    System.out.println("总记录数: " + page.getTotalElements());
    System.out.println("当前第: " + (page.getNumber() + 1) + "页");
    System.out.println("总页数: " + page.getTotalPages());
    System.out.println("当前页面的 List: " + page.getContent());
    System.out.println("当前页面的记录数: "+ page.getNumberOfElements());
  }
)

代码解释如下:

  • CriteriaQuery 接口:specific 的顶层查询对象,它包含查询的各个部分,比如,select、from、 where、group by、order by 等。CriteriaQuery 对象只对实体类型或嵌入式类型的 Criteria 查询起作用。
  • root:代表查询的实体类是 Criteria 查询的根对象。Criteria 查询的根定义了实体类型,能为将来的导航获得想要的结果。它与 SQL 查询中的 From 子句类似。Root 实例是类型化的, 且规定了 From 子句中能够出现的类型。查询根实例通过传入一个实体类型给 AbstractQuery.from 方法获得。
  • query:可以从中得到 Root 对象,即告知 JPA Criteria 查询要查询哪一个实体类。还可以添加查询条件,并结合 EntityManager 对象得到最终查询的 TypedQuery 对象。
  • CriteriaBuilder 对象:用于创建 Criteria 相关对象的工厂,可以从中获取到 Predicate 对象。
  • Predicate 类型:代表一个查询条件。

运行上面的测试代码,在控制台会输出如下结果(确保数据库已经存在数据):

Hibernate: select card0_.id as id1_0_, card0_.num as num2_0_ from card cardO_ where card0_.id>2 and card0_.num=422803 limit ?
Hibernate: select count(card0_.id) as col_0_0_ from card card0_ where card0_.id>2 and card0_.num=422803
总记录数: 6
当前第: 1页 
总页数: 2 
当前页面的 List: [Card(id=4, num=422803), Card(id=8, num=422803), Card(id=10, num=422803), Card(id=20, num=422803), Card(id=23, num=422803)] 
当前页面的记录数: 5
  1. 用 ExampleMatcher 进行查询

Spring Data 可以通过 Example 对象来构造 JPQL 查询,具体用法见以下代码:

User user = new User();
//构建查询条件
user.setName("pipi");
ExampleMatcher matcher = ExampleMatcher.matching()
	// 创建一个 ExampleMatcher, 不区分大小写匹配 name
	.withIgnorePaths("name")
	// 包括 null 值
	.withIncludeNullValues()
	// 执行后缀匹配
	.withStringMatcherEnding();
// 通过 Example 构建查询
Example<User> example = Example.of(user, matcher);
List<User> list = userRepository.findALL(example);

默认情况下,ExampleMatcher 会匹配所有字段。

可以指定单个属性的行为(如 name 或内嵌属性 name.user)。如:

  • withMatcher("name", endsWith())
  • withMatcher("name", startsWith().ignoreCase())
  1. 用谓语 QueryDSL 进行查询

QueryDSL 也是基于各种 ORM 之上的一个通用查询框架,它与 Spring Data JPA 是同级别的。使用 QueryDSL 的 API 可以写出 SQL 语句 (Java 代码,非真正标准 SQL),不需要懂 SQL 语句。它能够构建类型安全的查询。这与 JPA 使用原生查询时有很大的不同,可以不必再对 Object[] 进行操作。它还可以和 JPA 联合使用。

  1. 用 NamedQuery 进行查询

官方不推荐使用 NamedQuery,因为它的代码必须写在实体类上面,这样不够独立。其使用方法见以下代码:

©Entity 
@NamedQuery(name = "User.findByName", query = "select u from User u where u.name = ?1") 
public class User {
}
标签: #Spring Boot 173 #软件开发 1171 #JAVA 991
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.