Spring Data JPA 实战指南

 更新时间:2026年04月30日 08:12:50   作者:014-code  
本文主要介绍了Spring Data JPA 实战指南,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

用过 MyBatis 再用 JPA,可能会觉得 JPA 很别扭——SQL 都不用写了,框架自动搞定。

但用久了会发现,JPA 写起来其实很爽,尤其单表操作,几乎不需要写 SQL。

基础配置

依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>

配置

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update  # 开发用 update,生产用 validate
    show-sql: true      # 打印 SQL
    properties:
      hibernate:
        format_sql: true

ddl-auto 几个选项:

  • update:自动更新表结构(不会删数据)
  • create:每次启动删表重建
  • validate:只验证,不改表
  • none:什么都不做

实体映射

基本映射

@Entity
@Table(name = "sys_user")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    @Column(name = "user_name", length = 50, nullable = false)
    private String userName;
    private Integer age;
    @Column(columnDefinition = "varchar(20) default 'ACTIVE'")
    private String status;
    @Column(name = "create_time")
    private LocalDateTime createTime;
    @Transient  // 不映射到数据库
    private String tempField;
}

主键生成策略

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)  // MySQL 自增
// 或
@GeneratedValue(strategy = GenerationType.SEQUENCE)  // Oracle、序列
// 或
@GeneratedValue(strategy = GenerationType.UUID)      // UUID
private String id;

Repository 基础 CRUD

继承 JpaRepository 就有基本的增删改查:

public interface UserRepository extends JpaRepository<User, Long> {
    // 继承来的方法:
    // save(entity)       - 保存或更新
    // findById(id)      - 查询
    // deleteById(id)    - 删除
    // count()           - 计数
    // existsById(id)    - 是否存在
}
@Service
@RequiredArgsConstructor
public class UserService {
    private final UserRepository userRepository;
    public User createUser(String name, Integer age) {
        User user = new User();
        user.setUserName(name);
        user.setAge(age);
        return userRepository.save(user);  // 自动 insert
    }
    public Optional<User> getUser(Long id) {
        return userRepository.findById(id);  // 自动 select
    }
    public void deleteUser(Long id) {
        userRepository.deleteById(id);  // 自动 delete
    }
    public User updateUser(Long id, String newName) {
        User user = userRepository.findById(id)
            .orElseThrow(() -> new RuntimeException("用户不存在"));
        user.setUserName(newName);
        return userRepository.save(user);  // 自动 update
    }
}

方法名查询(单表神器)

这是 JPA 最爽的地方——不用写 SQL,方法名就是查询

基础规则

public interface UserRepository extends JpaRepository<User, Long> {
    // 等值查询:findBy + 字段名
    List<User> findByUserName(String userName);
    // → SELECT * FROM sys_user WHERE user_name = ?
    // 模糊查询:findBy + 字段名 + Like
    List<User> findByUserNameLike(String userName);
    // → SELECT * FROM sys_user WHERE user_name LIKE ?
    // 多条件:findBy + 字段1 + And + 字段2
    List<User> findByUserNameAndAge(String userName, Integer age);
    // → SELECT * FROM sys_user WHERE user_name = ? AND age = ?
    // Or 条件
    List<User> findByUserNameOrAge(String userName, Integer age);
    // → SELECT * FROM sys_user WHERE user_name = ? OR age = ?
}

常用关键词

// 大于/小于/等于
List<User> findByAgeGreaterThan(Integer age);      // age > ?
List<User> findByAgeLessThan(Integer age);        // age < ?
List<User> findByAgeGreaterThanEqual(Integer age); // age >= ?
List<User> findByStatusEquals(String status);      // status = ?
// BETWEEN 范围
List<User> findByAgeBetween(Integer min, Integer max);
// → WHERE age BETWEEN ? AND ?
// IN 查询
List<User> findByUserNameIn(List<String> names);
// → WHERE user_name IN (?, ?, ?)
// NULL 判断
List<User> findByEmailIsNull();
List<User> findByEmailIsNotNull();
// True/False
List<User> findByActiveTrue();   // WHERE active = true
List<User> findByActiveFalse();  // WHERE active = false
// 排序
List<User> findByStatusOrderByAgeDesc(String status);
// → WHERE status = ? ORDER BY age DESC

分页和排序

// 分页:参数加 Pageable
Page<User> findByStatus(String status, Pageable pageable);
// 排序:Sort
List<User> findByStatus(String status, Sort sort);
// 使用
@Service
public class UserService {
    public Page<User> getUserPage(int page, int size) {
        Pageable pageable = PageRequest.of(page, size, Sort.by("age").descending());
        return userRepository.findByStatus("ACTIVE", pageable);
    }
}

@Query 自定义查询

方法名解决不了的,用 @Query

JPQL 查询

@Query("SELECT u FROM User u WHERE u.userName = ?1")
User findByName(String userName);
// 占位符 ?1、?2 按参数顺序
@Query("SELECT u FROM User u WHERE u.userName = ?1 AND u.age > ?2")
List<User> findByNameAndAge(String name, Integer age);
// 命名参数(更清晰)
@Query("SELECT u FROM User u WHERE u.userName = :name AND u.age > :age")
List<User> findByNameAndAgeV2(@Param("name") String name, @Param("age") Integer age);

多表联查

假设 User 关联 Department:

@Entity
public class User {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id")
    private Department department;
}
// 查询用户并带出部门名称
@Query("SELECT u FROM User u JOIN FETCH u.department WHERE u.id = :id")
User findByIdWithDepartment(@Param("id") Long id);

原生 SQL

@Query(value = "SELECT * FROM sys_user WHERE user_name = :name", nativeQuery = true)
User findByNameNative(@Param("name") String name);
// 原生 SQL 分页(注意 countQuery)
@Query(
    value = "SELECT * FROM sys_user WHERE status = :status ORDER BY age DESC",
    countQuery = "SELECT count(*) FROM sys_user WHERE status = :status",
    nativeQuery = true
)
Page<User> findByStatusPage(@Param("status") String status, Pageable pageable);

@Modifying 修改操作

@Modifying
@Query("UPDATE User u SET u.status = :status WHERE u.id = :id")
int updateStatus(@Param("id") Long id, @Param("status") String status);
@Modifying
@Query("DELETE FROM User u WHERE u.status = 'INACTIVE'")
void deleteInactiveUsers();

记得在 Service 层加事务:

@Transactional
public void updateStatus(Long id, String status) {
    userRepository.updateStatus(id, status);
}

一对多 / 多对一

实体定义

@Entity
@Table(name = "department")
@Data
public class Department {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    @OneToMany(mappedBy = "department", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<User> users;
}
@Entity
@Table(name = "sys_user")
@Data
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String userName;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id")
    private Department department;
}

查询

// 根据部门查用户
List<User> findByDepartmentId(Long deptId);

// 根据用户查部门(直接访问属性就行)
User user = userRepository.findById(id).orElseThrow(...);
String deptName = user.getDepartment().getName();  // 懒加载,会再查一次

// 一次性查出来(解决 N+1)
@Query("SELECT u FROM User u JOIN FETCH u.department WHERE u.id = :id")
User findByIdWithDept(@Param("id") Long id);

常见问题

1. 懒加载异常

// 报错:LazyInitializationException
User user = userRepository.findById(1L).orElseThrow(...);
String deptName = user.getDepartment().getName();  // session 已关闭

解决:在 Service 层加 @Transactional,或者用 JOIN FETCH 一次性加载。

2. N+1 问题

// N+1:查 1 个用户,再查 N 次部门
List<User> users = userRepository.findAll();
// SELECT * FROM sys_user
// SELECT * FROM department WHERE id = ?
// SELECT * FROM department WHERE id = ?
// ...

解决:用 @EntityGraphJOIN FETCH

@EntityGraph(attributePaths = {"department"})
List<User> findAllWithDept();

// 或
@Query("SELECT u FROM User u JOIN FETCH u.department")
List<User> findAllWithDept();

3. save 和 update 的区别

// save() - 如果 id 已存在就是 update,不存在就是 insert
User user = new User();
user.setId(1L);  // id 存在,变成 update
user.setUserName("newName");
userRepository.save(user);  // UPDATE

总结

方式适用场景
继承 JpaRepository基本 CRUD
方法名查询单表简单查询
@Query + JPQL多表关联、复杂查询
@Query + 原生 SQL特定数据库语法
JOIN FETCH解决懒加载和 N+1

JPA 最大的好处是不用写 SQL,单表操作特别爽。但多表关联和复杂查询,还是得用 @Query。用久了会发现,JPA + @Query 配合起来,效率比 MyBatis 高多了。

到此这篇关于Spring Data JPA 实战指南的文章就介绍到这了,更多相关Spring Data JPA 实战内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java集合框架之Set的实现类与实战使用详解

    Java集合框架之Set的实现类与实战使用详解

    本文深入解析Java集合框架中的Set接口及其实现类,重点内容包括Set接口不可重复性和无序性的设计理念,HashSet基于哈希表的底层实现与去重机制,LinkedHashSet维护插入顺序的原理等,需要的朋友可以参考下
    2026-02-02
  • 使用mtrace追踪JVM堆外内存泄露的方法

    使用mtrace追踪JVM堆外内存泄露的方法

    这篇文章主要给大家介绍了如何使用mtrace追踪JVM堆外内存泄露,文章通过代码示例介绍的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
    2023-09-09
  • springsecurity 基本使用详解

    springsecurity 基本使用详解

    这篇文章主要介绍了springsecurity 基本使用,本文通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-11-11
  • Javaweb实现完整个人博客系统流程

    Javaweb实现完整个人博客系统流程

    这篇文章主要介绍了怎样用Java来实现一个完整的个人博客系统,我们通过实操上手的方式可以高效的巩固所学的基础知识,感兴趣的朋友一起来看看吧
    2022-03-03
  • Java应用自动扩缩容的三种实现方案

    Java应用自动扩缩容的三种实现方案

    你是否曾经为如何应对突如其来的流量高峰而头疼不已?或者在面对资源浪费时感到无奈?别担心!本文将带你深入探索Java应用的三大自动扩缩容策略无论你是编程新手还是经验丰富的开发者,这篇文章都将为你提供详尽的指导,帮助你在自动扩缩容领域一展身手
    2025-06-06
  • Spring中SpEL表达式的使用全解

    Spring中SpEL表达式的使用全解

    SpEL是Spring框架中用于表达式语言的一种方式,本文主要介绍了Spring中SpEL表达式的使用全解,文中通过示例代码介绍的非常详细,需要的朋友们下面随着小编来一起学习学习吧
    2024-04-04
  • Apache Commons Imaging处理图像实例深究

    Apache Commons Imaging处理图像实例深究

    这篇文章主要为大家介绍了Apache Commons Imaging处理图像的实例探索深究,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Java中Spring扩展点详解

    Java中Spring扩展点详解

    这篇文章主要介绍了Java中Spring技巧之扩展点的应用,下文Spring容器的启动流程图展开其内容的相关资料,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-06-06
  • JavaSE实现三子棋游戏

    JavaSE实现三子棋游戏

    这篇文章主要为大家详细介绍了JavaSE实现三子棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 通过Spring层面进行事务回滚的实现

    通过Spring层面进行事务回滚的实现

    本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下
    2025-04-04

最新评论