大数跨境
0
0

只会用JPA中的save方法?Spring Boot 中隐藏的10个强大方法

只会用JPA中的save方法?Spring Boot 中隐藏的10个强大方法 Spring全家桶实战案例
2025-10-16
0
导读:只会用JPA中的save方法?Spring Boot 中隐藏的10个强大方法
Spring Boot 3实战案例锦集PDF电子书已更新至130篇!

🎉🎉《Spring Boot实战案例合集》目前已更新181个案例,我们将持续不断的更新。文末有电子书目录。

💪💪永久更新承诺

我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务

💌💌如何获取
订阅我们的合集点我订阅,并通过私信联系我们,我们将第一时间将电子书发送给您。

→ 现在就订阅合集

环境:SpringBoot3.4.2



1. 简介

Spring Data JPA作为Spring Data大家族中的一员,能够轻松实现基于JPA(Java持久化API)的存储库。它简化了构建使用数据访问技术的Spring驱动应用程序的过程。Spring Data JPA旨在通过将所需的工作量减少到实际必要的程度,来显著改善数据访问层的实现。

在使用Spring Data JPA开展持久化操作时,起初我们可能仅需掌握一些基础方法,如save()、findById() 以及delete() ,就能满足日常开发需求。然而,随着项目规模持续拓展、复杂度不断提升,我们逐渐发现,JPA所具备的功能远比想象中丰富得多。若能在合适的场景下精准运用恰当的方法,不仅能为你节省大量调试时间,减少数小时的无效摸索,还能有效降低数据库的负载压力。

本篇文章,我们将为你介绍最实用的 JPA 方法。我会结合实例,保持内容的实用性,并解释何时使用这些方法(以及何时不应使用)。

2.实战案例

2.1 save() 和 saveAll()

我们先从简单的开始。

  • save(entity) → 保存或更新单个实体

  • saveAll(entities) → 一次性保存多个实体

List<User> users = List.of(  new User("Pack", "pack@gmail.com"),  new User("Xg", "xg@gmail.com"));userRepository.saveAll(users) ;

与其遍历每个用户并调用 save(),不如使用 saveAll() 批量处理它们。如果要插入数千行数据,可以考虑在 JPA 配置中启用批量插入(如果没有开启,saveAll实际也就是便利的save)。这样可以显著提高性能。

2.2 findById() 和 findAllById()

当通过主键获取实体时,这些是首选方法。

Optional<User> user = userRepository.findById(1L);List<User> users = userRepository.findAllById(List.of(1L, 2L, 3L));

findById() 返回的是 Optional,而不是实体本身。这有助于避免 NullPointerException,但别忘了检查值是否存在。

2.3 getReferenceById()

这个方法经常被忽视。与立即查询数据库的 findById() 不同,getReferenceById()返回的是一个延迟引用的代理对象。

User user = userRepository.getReferenceById(1L);

此时,不会触发任何查询。只有当你访问 user 的某个字段时,查询才会执行。何时使用:当你只需要一个用于关联关系的引用(比如设置外键),而不需要完整的实体时,这个方法非常适用。

2.4 existsById()

有没有过那种情况,你只是想看看某条记录在不在数据库里,根本不想把它获取到手里?

boolean exists = userRepository.existsById(1L);

这样可以避免获取不必要的数据,比仅为了检查存在性而调用 findById() 快得多。

2.5 deleteById() 和 deleteAllById()

这是删除实体的直观方法,但许多开发者会忽略一个细微的细节。

userRepository.deleteById(1L);

这些方法会先获取实体,然后再删除它。对于大型数据集来说,这样做效率很低。更聪明的替代方案:如果不需要在删除前加载实体,可以使用带有 DELETE 的自定义 @Query

@Modifying@Query("DELETE FROM User u WHERE u.email = :email")void deleteByEmail(@Param("email"String email);

这样可以避免不必要的 SELECT 查询,直接执行 DELETE 语句,提升性能。

2.6 count()

一个简单但强大的方法:

long count = userRepository.count();

这对于仪表盘、监控或快速检查而言十分便利。但需谨慎:对于大型表,它可能会很慢。在实时API中使用它之前一定要三思。

2.7 flush()和saveAndFlush()

大多数开发者并不使用这些方法,但在某些场景下它们可能成为救星。通常情况下,JPA会等到事务提交后才将更改与数据库同步。但使用flush(),你可以强制立即写入更改。

userRepository.saveAndFlush(user);

何时使用:

  • 当你在同一事务中执行其他操作之前,需要确保实体已存在于数据库中时

  • 对于调试事务问题很有用

2.8 findAll(Sort sort) and findAll(Pageable pageable)

对于小型表,使用findAll()获取所有内容并无大碍。但在实际项目中,这会导致性能灾难。相反,应使用排序和分页:

List<User> users = userRepository.findAll(Sort.by("name"));Page<User> page = userRepository.findAll(PageRequest.of(020));

想象这么个场景,你在搭建管理员仪表板的用户列表。总不能把100万条数据一次性全弄到内存里吧,这多伤应用和服务器啊。分页功能这时候就是“救星”,能解决问题。

2.9 @Query 用于自定义方法

有时,派生查询方法(如findByEmail、findByName等)远远不够。这时,@Query就派上用场了。

@Query("SELECT u FROM User u WHERE u.email LIKE %:keyword%")List<User> searchByEmail(@Param("keyword") String keyword);

当JPQL(Java持久化查询语言)无法满足需求时,你甚至可以使用原生查询:

@Query(value = "SELECT * FROM users WHERE email LIKE %:keyword%", nativeQuery = true)List<UsersearchByEmailNative(@Param("keyword"String keyword);

2.10 @Modifying 用于更新操作

默认情况下,JPA查询是只读的。如果你想在不获取实体的情况下执行更新操作,就需要使用@Modifying

@Modifying@Query("UPDATE User u SET u.name = :name WHERE u.id = :id")void updateUserName(@Param("id") Long id, @Param("name") String name);

这避免了加载实体、修改实体然后再保存实体所带来的开销。

2.11 QBE查询

Query by Example (QBE) 是 Spring Data JPA 提供的一种查询方式,它允许你通过创建一个示例对象来构建查询。这个示例对象包含了查询条件,Spring Data JPA 会自动将这些条件转化为 SQL 查询语句。

private final EmpRepository empRepository ;public Optional<Emp> queryEmp1(Integer age) {  Emp emp = new Emp() ;  emp.setAge(age) ;  emp.setName("张三") ;  Example<Emp> example = Example.of(emp) ;  // Example作为参数。  return this.empRepository.findOne(example) ;}

执行上面的方法,控制台输出的SQL

Hibernate: select emp0_.id as id1_0_, emp0_.address as address2_0_, emp0_.age as age3_0_, emp0_.name as name4_0_, emp0_.state as state5_0_ from emp emp0_ where emp0_.age=20 and emp0_.name=? limit ?

SQL的where条件只包含了age与name条件。这是默认行为,会自动将domain对象中的空值忽略,自动将有值的字段添加到查询条件中。这样的查询是不是非常的灵活,要加条件或减条件,只需要前台控制查询参数即可。

2.12 构建动态查询条件

我们可以通过继承 JpaSpecificationExecutor 接口,实现动态查询条件的构建,该接口提供了多个方法,可以用来接收 Specification 参数实现查询条件的动态构建。

import org.springframework.data.jpa.domain.Specification;import static org.springframework.data.jpa.domain.Specification.where
public List<ProductsearchProducts(String name, String category, Double minPrice, Double maxPrice) {  Specification<Product> spec = where(null);  if (name != null && !name.isEmpty()) {    spec = spec.and(ProductSpecifications.hasNameLike(name));  }  if (category != null && !category.isEmpty()) {    spec = spec.and(ProductSpecifications.hasCategory(category));  }  if (minPrice != null && maxPrice != null) {    spec = spec.and(ProductSpecifications.isPriceBetween(minPrice, maxPrice));  }  return productRepository.findAll(spec);}public class ProductSpecifications {  public static Specification<ProducthasNameLike(String name) {    return (root, query, cb) ->      cb.like(root.get("name"), "%" + name + "%");  }  public static Specification<ProducthasCategory(String category) {    return (root, query, cb) ->      cb.equal(root.get("category"), category);  }  public static Specification<ProductisPriceBetween(Double minPrice, Double maxPrice) {    return (root, query, cb) ->      cb.between(root.get("price"), minPrice, maxPrice);  }}




以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

new 的对象也能被 Spring 注入?AspectJ LTW 打破限制!

Spring Boot防御性编程:8种让代码"自愈"的黄金模式

Spring Boot 基于注解实现字段级完整性校验,支持JPA、Mybatis

优化!Controller 接口设计、性能调优等8大黄金法则

企业级实践!Spring Boot 一个注解实现存储加密,支持MyBatis、JPA

零侵入插件!Spring Boot 一键生成 8种格式API接口文档

10 个 Spring Boot 高效编码技巧 + 7个 Tomcat性能调优秘诀

Spring Boot 动态生成签名 URL:私有文件安全访问方案

Spring Boot超大文件上传的正确方式

绝了!Spring Batch 百万数据分区处理,仅需5秒搞定

优雅!Spring Boot多模块独立开发新玩法

告别OOM!Spring Boot 流式导出百万数据:支持MyBatis/JPA/Jdbc

图片
图片
图片
图片
图片
图片
图片
图片

【声明】内容源于网络
0
0
Spring全家桶实战案例
Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
内容 832
粉丝 0
Spring全家桶实战案例 Java全栈开发,前端Vue2/3全家桶;Spring, SpringBoot 2/3, Spring Cloud各种实战案例及源码解读
总阅读38
粉丝0
内容832