目录
1. 理解 JPA(Java Persistence API)
1.1 什么是 JPA?
1.2 JPA 与 Hibernate 的关系
1.3 JPA 的基本注解:@Entity, @Table, @Id, @GeneratedValue
1.4 JPA 与数据库表的映射
2. Spring Data JPA 概述
2.1 什么是 Spring Data JPA?
2.2 spring-boot-starter-data-jpa 的作用
2.3 配置 spring-boot-starter-data-jpa 依赖
3. Spring Boot 和 JPA 整合案例
3.1 创建一个基本的 CRUD Web 应用
3.2 Spring Boot 集成 JPA 的实践
3.2.1 引入基本的 Spring Data JPA 依赖
3.2.2 引入数据库连接池(默认使用 HikariCP)推荐默认的就行
使用 DBCP2 连接池
使用 C3P0 连接池
3.2.3 数据库驱动
3.2.4 配置 application.properties 或 application.yml
4. 实体类与数据库表映射
4.1 数据库建表语句
4.2 创建一个 JPA 实体类
4.2 使用 JPA 的基本类型映射(如:@Column, @OneToMany, @ManyToOne 等)
JPA 提供了许多注解来描述实体类与数据库表之间的关系,包括对列、主键、关系的映射。
4.2.1 @Column
4.2.2 @ManyToOne 和 @OneToMany
4.2.3 @OneToOne
4.2.4 @ManyToMany
5. Spring Data JPA Repository
5.1 JpaRepository 和 CrudRepository 接口
5.1.1 CrudRepository 接口
5.1.2 JpaRepository 接口(推荐,实现的多)
5.2 创建 Repository 接口
5.3 常用的 CRUD 操作(保存、查询、删除、更新)
5.4 自定义查询方法(如:通过方法名自动生成查询)
5.5 使用 @Query 注解编写自定义 SQL 查询
5.5.1 使用 JPQL(推荐方式)
5.5.2 使用原生 SQL
5.5.3 使用 @Modifying 和 @Transactional 进行更新/删除操作
6. JPA 查询功能
6.1 JPQL查询
6.2 Criteria API(动态查询)
6.3 使用 Spring Data JPA 提供的分页与排序功能
6.3.1 分页
6.3.2 排序
7. 事务管理
7.1 什么是事务?
7.2 Spring 的事务管理基础
7.3 使用 @Transactional 注解
7.4 事务的传播行为和隔离级别
7.5 事务回滚机制
8. 配置与优化
8.1 配置数据源(DataSource)
8.2 使用 application.properties 配置数据库连接池
8.3 配置 JPA 的 Hibernate 属性(如:DDL 自动生成策略、SQL 日志输出等)
8.3.1 配置 Hibernate 的 DDL 自动生成策略
8.3.2 配置 Hibernate 的 SQL 日志输出
-
1. 理解 JPA(Java Persistence API)
-
1.1 什么是 JPA?
-
JPA(Java Persistence API)是 Java 平台的一部分,专门用于简化 Java 应用程序与关系型数据库之间的交互。它定义了一种标准的方式来映射 Java 对象到数据库表,同时提供了操作数据库(增、删、改、查)的接口,支持对象关系映射(ORM)。JPA 作为一种规范,定义了如何在 Java 应用程序中实现持久化,但并没有提供具体的实现。
JPA 的实现可以有多个,其中最常见的实现是 Hibernate,但也有其他实现,如 EclipseLink、OpenJPA 等。
-
-
1.2 JPA 与 Hibernate 的关系
-
Hibernate 是一个非常流行的 JPA 实现。它是一个开源的 ORM 框架,遵循了 JPA 的规范,并提供了一些额外的功能,比如自动生成 SQL、缓存机制等。简单来说,Hibernate 实现了 JPA 规范,它可以作为 JPA 的一个持久化提供者。
-
JPA 是一个规范,它规定了 Java 对象和关系型数据库之间的映射规则以及操作数据库的 API。
-
Hibernate 是一个实现,它提供了具体的代码来执行 JPA 规范中定义的操作。
-
例如,你可以使用 Hibernate 来实现 JPA 标准的接口,或者直接使用 Hibernate 提供的扩展功能。JPA 的目标是解耦持久化层,使得应用程序不依赖于某一个具体的实现,而是遵循 JPA 规范,便于切换实现。
-
-
1.3 JPA 的基本注解:
@Entity
,@Table
,@Id
,@GeneratedValue
-
在 JPA 中,注解是实现对象关系映射的关键。常见的注解包括:
-
@Entity:标识一个类是一个实体类,表示该类的实例将会与数据库中的一条记录映射。每个
@Entity
注解的类对应数据库中的一张表。-
@Entity public class User { // 类体 }
-
-
@Table:指定数据库表的名称。如果不使用该注解,默认情况下,实体类的名字将会被用作表名。通过
@Table
可以自定义表名以及其他表级别的配置(如 schema、catalog 等)。-
@Entity @Table(name = "users") public class User { // 类体 }
-
-
@Id:指定实体类的主键字段。每个实体类必须至少有一个主键字段,JPA 使用该字段来唯一标识实体类的对象。
-
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 其他字段 }
-
-
@GeneratedValue:指定主键的生成策略。通常与
@Id
一起使用,表示主键的值如何自动生成。可以选择不同的生成策略,如AUTO
、IDENTITY
、SEQUENCE
等。GenerationType.AUTO
:JPA 容器根据数据库的不同选择合适的生成策略。GenerationType.IDENTITY
:通过数据库的自增列生成主键值。GenerationType.SEQUENCE
:使用数据库序列生成主键值(通常用于数据库支持序列的情况)。GenerationType.TABLE
:使用一个特殊的数据库表来生成主键(这种方式比较少见)。-
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; // 其他字段 }
-
-
1.4 JPA 与数据库表的映射
-
JPA 将 Java 对象(类)和数据库表之间进行映射,这种映射关系主要由注解和配置来定义。JPA 实现了对象关系映射(ORM),它能够将数据库中的记录转换为 Java 对象,并允许开发人员通过操作对象来间接操作数据库。
-
常见的映射关系如下:
-
一对一关系(@OneToOne):一个实体对应另一个实体的一个实例。
-
一对多关系(@OneToMany):一个实体对应多个实体。
-
多对一关系(@ManyToOne):多个实体对应一个实体。
-
多对多关系(@ManyToMany):多个实体对应多个实体。
-
-
举个例子,假设我们有一个
User
实体类和一个Address
实体类,一个用户可能有多个地址(多对一关系),那么我们可以使用@OneToMany
和@ManyToOne
来进行映射: -
@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @OneToMany(mappedBy = "user") private List<Address> addresses; // 其他字段和方法 } @Entity public class Address { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @ManyToOne @JoinColumn(name = "user_id") private User user; }
-
-
-
2. Spring Data JPA 概述
-
2.1 什么是 Spring Data JPA?
-
Spring Data JPA 是 Spring Data 项目的一个子项目,它简化了基于 JPA(Java Persistence API)进行数据库操作的开发。Spring Data JPA 基于 JPA 规范,结合 Spring 的优势,提供了对 JPA 持久化技术的简化支持,特别是在 Spring Boot 应用中使用时,极大地减少了样板代码(boilerplate code)。
Spring Data JPA 通过提供一个简单的接口
JpaRepository
和一些其他的 CRUD 接口,使开发者可以轻松实现对数据库的基本操作,如增、删、改、查等,而无需编写实现代码。它通过动态代理技术,自动生成实现类,简化了开发过程。核心特性:
-
简化 CRUD 操作:无需编写实现代码,直接通过继承接口来使用常见的数据库操作。
-
基于 JPA:继承了 JPA 的标准特性,如实体类映射、查询方法等。
-
查询方法自动生成:Spring Data JPA 根据方法名自动生成 SQL 查询,无需手写 SQL。
-
分页和排序支持:内置分页、排序功能,支持复杂的查询。
-
-
-
2.2
spring-boot-starter-data-jpa
的作用-
spring-boot-starter-data-jpa
是 Spring Boot 提供的一个启动器(starter),用于集成和配置 Spring Data JPA。在应用中引入该依赖后,Spring Boot 会自动配置与 JPA 相关的 Bean,简化了 JPA 的配置和初始化工作。 -
作用:
-
自动配置数据源(
DataSource
)、JPA 相关的配置(如EntityManagerFactory
)、事务管理器(PlatformTransactionManager
)等。 -
集成常见的 JPA 实现(如 Hibernate),并提供简单的配置方式。
-
支持自动创建数据库表和执行初始化 SQL(根据
spring.jpa.hibernate.ddl-auto
配置项)。 -
提供分页、排序等功能,结合 Spring Data JPA 的接口,可以非常方便地执行复杂查询。
-
-
自动配置:
-
配置数据库连接、实体类管理、事务管理等。
-
使用
JpaRepository
等接口简化持久化操作。
-
-
-
2.3 配置
spring-boot-starter-data-jpa
依赖- 要在 Spring Boot 项目中使用 Spring Data JPA,首先需要在项目的
pom.xml
文件中添加spring-boot-starter-data-jpa
依赖。这个依赖会自动引入 Spring Data JPA 和 Hibernate 等相关库。 - 在
pom.xml
中添加spring-boot-starter-data-jpa
依赖。-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
-
- 要在 Spring Boot 项目中使用 Spring Data JPA,首先需要在项目的
-
-
3. Spring Boot 和 JPA 整合案例
-
3.1 创建一个基本的 CRUD Web 应用
-
3.2 Spring Boot 集成 JPA 的实践
-
3.2.1 引入基本的 Spring Data JPA 依赖
-
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency>
-
-
3.2.2 引入数据库连接池(默认使用 HikariCP)推荐默认的就行
-
Spring Boot 默认使用 HikariCP 作为连接池,它不需要额外的依赖。如果你使用的是 HikariCP,你不需要手动添加依赖,因为它已经包含在 Spring Boot 的默认配置中。
-
如果你需要使用其他连接池(例如 DBCP2 或 C3P0),可以选择手动添加相应的依赖:
-
使用 DBCP2 连接池
-
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-dbcp2</artifactId> </dependency>
-
-
使用 C3P0 连接池
-
<dependency> <groupId>com.mchange</groupId> <artifactId>c3p0</artifactId> </dependency>
-
-
-
-
3.2.3 数据库驱动
- 根据你使用的数据库,你需要添加相应的数据库驱动。例如,假设你使用 MySQL,你需要添加 MySQL 驱动:
-
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
-
3.2.4 配置
application.properties
或application.yml
- application.properties
-
# 数据库配置 spring.datasource.url=jdbc:mysql://localhost:3306/lirui?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 使用HikariCP作为连接池(Spring Boot默认使用HikariCP) spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.minimum-idle=5 # JPA配置 spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true
-
- application.yml
-
spring: datasource: url: jdbc:mysql://localhost:3306/lirui?useSSL=false&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: update show-sql: true database-platform: org.hibernate.dialect.MySQL5Dialect hikari: maximum-pool-size: 10 minimum-idle: 5
-
- application.properties
-
-
-
4. 实体类与数据库表映射
-
4.1 数据库建表语句
-
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `email` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4;
-
-
4.2 创建一个 JPA 实体类
-
package com.lirui.springbootmoduledemo.dao; import javax.persistence.*; @Entity // 标识该类为一个 JPA 实体类 @Table(name = "users") // 映射到数据库中的 users 表 public class User { @Id // 主键标识 @GeneratedValue(strategy = GenerationType.IDENTITY) // 自增主键 @Column(name = "id") // 映射到数据库中的 id 列 private Integer id; @Column(name = "name", nullable = false, length = 255) // 映射到 name 列 private String name; @Column(name = "email", nullable = false, length = 255) // 映射到 email 列 private String email; // Getter 和 Setter 方法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", email='" + email + '\'' + '}'; } }
-
@Entity
:声明User
类是一个 JPA 实体类,Spring Data JPA 会根据该类生成对应的数据库表操作。 -
@Table(name = "users")
:指定实体类对应的数据库表名为users
。如果类名与表名相同,可以省略这个注解。 -
@Id
:标识该字段是实体的主键(id
字段)。 -
@GeneratedValue(strategy = GenerationType.IDENTITY)
:指定主键的生成策略为IDENTITY
,表示使用数据库的自增机制。适用于 MySQL 等数据库。 -
@Column(name = "column_name")
:映射实体类字段到数据库表中的列(如name
、email
)。使用nullable = false
来指定列不允许为NULL
,并且可以设置列的最大长度(如length = 255
)。 -
Integer
类型的id
字段:我们将id
定义为Integer
类型,这样它可以自动与数据库中的int
类型映射。如果你更倾向于使用Long
类型,也可以使用Long
类型。
-
4.2 使用 JPA 的基本类型映射(如:
@Column
,@OneToMany
,@ManyToOne
等)-
JPA 提供了许多注解来描述实体类与数据库表之间的关系,包括对列、主键、关系的映射。
-
4.2.1 @Column
- 映射实体类属性到数据库表的列。
- 可设置属性(如:
nullable
、length
、unique
、columnDefinition
等)。-
nullable
-
定义列是否允许
NULL
值。 -
默认值:
true
(表示列允许为NULL
)。 -
使用场景:如果我们希望某个字段在数据库中不能为空,可以将
nullable
设置为false
,表示该列必须有值。
-
-
length
-
定义字符串类型的列(如
String
)的最大长度。 -
使用场景:主要用于设置数据库中
VARCHAR
类型的字段的最大长度。如果没有显式设置,默认长度为 255。 -
适用类型:
String
、char
和Character
类型。
-
-
unique
-
功能:指定列是否应该是唯一的。
-
默认值:
false
(表示列不要求唯一)。 -
使用场景:如果我们希望某个字段的值在数据库中是唯一的,可以设置
unique = true
,表示该列的值在数据库表中必须是唯一的。
-
-
columnDefinition
-
功能:指定列的具体定义,包括数据类型、默认值、约束等。
-
使用场景:当我们需要对列进行更精细的控制,例如设置数据库列的数据类型、默认值、约束条件等时,可以使用
columnDefinition
。它允许你直接写 SQL 语句来控制列的定义。 -
@Column(columnDefinition = "VARCHAR(100) NOT NULL DEFAULT 'Unknown'") private String username;
-
-
name
-
定义数据库中列的名称。如果不设置,默认使用字段名。
-
-
insertable
-
指定是否允许在插入时写入该列。默认值是
true
。
-
-
updatable
-
指定是否允许更新该列。默认值是
true
。
-
-
precision
和scale
-
主要用于
BigDecimal
类型,用于定义列的精度和小数位数。
-
-
-
4.2.2 @ManyToOne
和@OneToMany
-
@ManyToOne
:表示多对一的关系。例如,一个用户有多个订单,但每个订单只能属于一个用户。 -
@OneToMany
:表示一对多的关系。例如,一个用户可以有多个订单。 -
import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.OneToMany; import java.util.List; @Entity @Table(name = "user") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String username; @OneToMany(mappedBy = "user") // 通过 user 字段映射到 Order private List<Order> orders; // 一对多关系 // Getter 和 Setter }
import javax.persistence.Entity; import javax.persistence.Id; import javax.persistence.ManyToOne; @Entity @Table(name = "order") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String productName; @ManyToOne // 多对一关系 @JoinColumn(name = "user_id") // 映射外键 private User user; // 每个订单对应一个用户 // Getter 和 Setter }
-
-
4.2.3 @OneToOne
-
如果实体之间存在一对一关系,可以使用@OneToOne
注解。@Entity public class Profile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String bio; @OneToOne @JoinColumn(name = "user_id") // 外键字段 private User user; // 一对一关系 // Getter 和 Setter }
-
-
4.2.4 @ManyToMany
-
如果两个实体之间存在多对多的关系,可以使用
@ManyToMany
注解。@Entity public class Course { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; @ManyToMany @JoinTable( name = "course_student", joinColumns = @JoinColumn(name = "course_id"), inverseJoinColumns = @JoinColumn(name = "student_id") ) private List<Student> students; // Getter 和 Setter }
-
-
-
-
5. Spring Data JPA Repository
-
5.1
JpaRepository
和CrudRepository
接口-
5.1.1
CrudRepository
接口-
CrudRepository
是 Spring Data JPA 提供的基本接口之一,它提供了最基本的 CRUD 操作方法。它是所有 JPA Repository 接口的基础。 -
常用的方法:
-
save(S entity)
:保存实体。 -
findById(ID id)
:根据主键查找实体。 -
findAll()
:查找所有实体。 -
deleteById(ID id)
:根据 ID 删除实体。
-
-
-
5.1.2 JpaRepository
接口(推荐,实现的多)-
JpaRepository
扩展了PagingAndSortingRepository
和CrudRepository
,因此它不仅提供了 CRUD 功能,还支持分页和排序功能。 -
常用的方法:
-
findAll(Pageable pageable)
:分页查询。 -
findAll(Sort sort)
:排序查询。 -
flush()
:刷新持久化上下文。 -
saveAndFlush(S entity)
:保存并立即刷新实体。
-
-
-
-
5.2 创建 Repository 接口
-
Spring Data JPA 中,我们只需要定义一个接口并继承
JpaRepository
或CrudRepository
,Spring 会自动实现所有方法。 -
@Repository // 标记为一个 Spring 的 Repository public interface UserRepository extends JpaRepository<User, Integer> { // 可以在这里定义查询方法,例如: // List<User> findByName(String name); }
-
-
5.3 常用的 CRUD 操作(保存、查询、删除、更新)
-
@Service // 标注为 Spring 管理的 Service 类 public class UserService { @Autowired private UserRepository userRepository; // 注入 UserRepository // 保存用户 public User saveUser(User user) { return userRepository.save(user); // 调用 UserRepository 的 save 方法 } // 获取所有用户 public List<User> getAllUsers() { return userRepository.findAll(); // 获取所有用户 } // 根据 ID 查找用户 public Optional<User> getUserById(Integer id) { return userRepository.findById(id); // 根据 ID 查找用户 } // 删除用户 public void deleteUser(Integer id) { userRepository.deleteById(id); // 删除用户 } }
-
-
5.4 自定义查询方法(如:通过方法名自动生成查询)
-
Spring Data JPA 支持通过方法名自动生成查询。只需要按照一定的规则命名方法,Spring Data JPA 就能自动实现查询。
- 常见的查询方法命名规则:
-
根据属性查找:
findBy<属性名>
例如:findByName(String name)
,表示根据name
属性查找。 -
根据多个属性查找:
findBy<属性1>And<属性2>
例如:findByNameAndEmail(String name, String email)
,表示根据name
和email
属性查找。 -
根据属性模糊查找:
findBy<属性名>Like
例如:findByNameLike(String name)
,表示根据name
属性模糊查找。
-
-
@Repository // 标记为一个 Spring 的 Repository public interface UserRepository extends JpaRepository<User, Integer> { // 可以在这里定义查询方法,例如: List<User> findByName(String name); // 根据名字查询用户 List<User> findByEmail(String email); // 根据 email 查询用户 List<User> findByNameAndEmail(String name, String email); // 根据名字和 email 查询用户 List<User> findByNameLike(String name); // 根据名字模糊查询用户 }
-
-
5.5 使用
@Query
注解编写自定义 SQL 查询-
如果方法名不能满足需求,或者你需要更复杂的查询,可以使用
@Query
注解来编写自定义的 JPQL或者原生 SQL 查询。 -
5.5.1 使用 JPQL(推荐方式)
JPQL 是针对实体对象的查询语言,而不是针对数据库表的 SQL 语言。你可以使用
@Query
注解来编写 JPQL 查询。-
package com.lirui.springbootmoduledemo.repository; import com.lirui.springbootmoduledemo.dao.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import java.util.List; @Repository // 标记为一个 Spring 的 Repository public interface UserRepository extends JpaRepository<User, Integer> { // 可以在这里定义查询方法,例如: @Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询 List<User> findByName(String name); // 根据名字查询用户 List<User> findByEmail(String email); // 根据 email 查询用户 List<User> findByNameAndEmail(String name, String email); // 根据名字和 email 查询用户 List<User> findByNameLike(String name); // 根据名字模糊查询用户 }
-
-
5.5.2 使用原生 SQL
如果你需要直接使用原生 SQL 查询,可以通过
nativeQuery = true
来指定。-
@Query(value = "SELECT * FROM users WHERE email LIKE %?1%", nativeQuery = true) // 原生 SQL 使用 LIKE 查询 List<User> findByEmail(String email); // 根据 email 查询用户
-
-
5.5.3 使用
@Modifying
和@Transactional
进行更新/删除操作-
如果你需要执行更新或删除操作,必须使用
@Modifying
注解,并且方法应该是事务性的。 -
package com.lirui.springbootmoduledemo.repository; import com.lirui.springbootmoduledemo.dao.User; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; import javax.transaction.Transactional; import java.util.List; @Repository // 标记为一个 Spring 的 Repository public interface UserRepository extends JpaRepository<User, Integer> { // 可以在这里定义查询方法,例如: @Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询 List<User> findByName(String name); // 根据名字查询用户 @Query(value = "SELECT * FROM users WHERE email LIKE %?1%", nativeQuery = true) // 原生 SQL 使用 LIKE 查询 List<User> findByEmail(String email); // 根据 email 查询用户 List<User> findByNameAndEmail(String name, String email); // 根据名字和 email 查询用户 List<User> findByNameLike(String name); // 根据名字模糊查询用户 @Modifying // 标注为修改操作 @Transactional // 事务支持 @Query("UPDATE User u SET u.name = ?1 WHERE u.id = ?2") int updateUserNameById(String name, Integer id); @Modifying @Transactional @Query("DELETE FROM User u WHERE u.id = ?1") void deleteUserById(Integer id); }
-
-
-
-
6. JPA 查询功能
-
6.1 JPQL查询
- 同上
-
6.2 Criteria API(动态查询)
-
Criteria API 是 JPA 提供的一个用于构建动态查询的 API,它允许我们通过编程方式构造查询,适用于那些查询条件动态变化的情况。使用 Criteria API,可以避免拼接字符串,避免 SQL 注入风险。
-
Criteria API 的常用组件:
-
CriteriaBuilder
:用于创建查询的各种条件和表达式。 -
CriteriaQuery
:表示查询的具体内容。 -
Root
:表示查询的根对象,通常是实体类。 -
Predicate
:表示查询条件。
-
-
package com.lirui.springbootmoduledemo.service; import com.lirui.springbootmoduledemo.dao.User; import com.lirui.springbootmoduledemo.repository.UserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.persistence.EntityManager; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.util.List; import java.util.Optional; @Service // 标注为 Spring 管理的 Service 类 public class UserService { @Autowired private UserRepository userRepository; // 注入 UserRepository @Autowired private EntityManager entityManager; public List<User> findUsersByCriteria(String name, String email) { CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder(); CriteriaQuery<User> criteriaQuery = criteriaBuilder.createQuery(User.class); Root<User> userRoot = criteriaQuery.from(User.class); // 设置查询根对象 // 构建查询条件 Predicate predicate = criteriaBuilder.conjunction(); // 创建一个“与”的条件 if (name != null) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(userRoot.get("name"), name)); // 添加 name 的条件 } if (email != null) { predicate = criteriaBuilder.and(predicate, criteriaBuilder.equal(userRoot.get("email"), email)); // 添加 email 的条件 } criteriaQuery.where(predicate); // 设置查询条件 return entityManager.createQuery(criteriaQuery).getResultList(); // 执行查询 } // 保存用户 public User saveUser(User user) { return userRepository.save(user); // 调用 UserRepository 的 save 方法 } // 获取所有用户 public List<User> getAllUsers() { return userRepository.findAll(); // 获取所有用户 } // 根据 ID 查找用户 public Optional<User> getUserById(Integer id) { return userRepository.findById(id); // 根据 ID 查找用户 } // 删除用户 public void deleteUser(Integer id) { userRepository.deleteById(id); // 删除用户 } }
-
-
6.3 使用 Spring Data JPA 提供的分页与排序功能
-
6.3.1 分页
- Spring Data JPA 为我们提供了
Pageable
接口,用来表示分页请求。Pageable
可以指定页码(page)和每页大小(size)。 -
@Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询 List<User> findByName(String name, Pageable pageable); // 根据名字查询用户
public User findByName(User user) { Pageable pageable = PageRequest.of(0, 10); // 创建 Pageable 实例 List<User> byName = userRepository.findByName(user.getName(), pageable); return byName.get(0); // 调用 UserRepository 的 save 方法 }
- Spring Data JPA 为我们提供了
-
6.3.2 排序
- 用法一样
-
@Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询 List<User> findByName(String name, Pageable pageable, Sort sort); // 根据名字查询用户
public User findByName(User user) { Pageable pageable = PageRequest.of(0, 10); // 创建 Pageable 实例 Sort sort = Sort.by(Sort.Order.asc("name")); // 根据 name 字段升序排序 List<User> byName = userRepository.findByName(user.getName(), pageable,sort); return byName.get(0); // 调用 UserRepository 的 save 方法 }
-
-
-
7. 事务管理
-
7.1 什么是事务?
- 事务(Transaction) 是一组操作的集合,这些操作要么全部执行成功,要么在出现问题时全部回滚。事务是数据库管理系统(DBMS)中用于确保数据一致性、完整性、可靠性的一个关键概念。
-
事务的 ACID 特性:
- 原子性(Atomicity):事务中的操作要么全部成功,要么全部失败,不会出现部分成功的情况。
- 一致性(Consistency):事务执行前后,数据库的状态必须是合法的。
- 隔离性(Isolation):一个事务的执行不应该受到其他事务的干扰,事务的执行结果对其他事务是隔离的。
- 持久性(Durability):一旦事务提交,操作的结果会永久保存到数据库中,即使系统崩溃也不会丢失。
-
7.2 Spring 的事务管理基础
-
Spring 提供了两种主要的事务管理方式:
-
声明式事务管理:通过
@Transactional
注解或 AOP 配置来管理事务。 -
编程式事务管理:通过编程方式(即通过
TransactionTemplate
或PlatformTransactionManager
)显式控制事务的开始、提交和回滚。 -
声明式事务管理是 Spring 推荐的事务管理方式,通常使用
@Transactional
注解。
-
-
7.3 使用
@Transactional
注解-
Spring 的
@Transactional
注解可以用来声明式地管理事务。通过将其应用于方法或类上,Spring 会自动在方法执行时开启事务,方法执行结束时提交事务,若方法抛出异常则回滚事务。基本使用
-
应用于类:应用于类级别时,类中的所有方法都会被事务管理。
-
应用于方法:应用于方法时,只有该方法会被事务管理。
-
-
@Transactional
常用属性:-
propagation
:定义事务的传播行为(如如何处理事务的嵌套)。 -
isolation
:定义事务的隔离级别(如多个事务之间如何隔离)。 -
rollbackFor
:指定哪些异常会导致事务回滚。 -
noRollbackFor
:指定哪些异常不会导致事务回滚。 -
timeout
:指定事务的超时时间(单位:秒)。 -
readOnly
:设置事务是否为只读事务,优化数据库性能(对于只读取数据的操作)。 -
value
:指定事务管理器的名字,默认情况下 Spring 会使用默认的事务管理器。
-
-
-
7.4 事务的传播行为和隔离级别
-
事务传播行为(Propagation)
-
事务传播行为定义了一个事务方法如何与另一个事务方法交互。Spring 支持以下几种传播行为:
-
REQUIRED
(默认值):如果当前没有事务,则创建一个新的事务。如果当前已经存在事务,当前方法将加入到这个事务中。 -
REQUIRES_NEW
:无论当前是否存在事务,都会创建一个新的事务。如果当前有事务,则会暂停当前事务。 -
SUPPORTS
:如果当前有事务,则加入事务。如果没有事务,则以非事务的方式执行。 -
NOT_SUPPORTED
:如果当前有事务,则将事务挂起,并以非事务方式执行。 -
MANDATORY
:当前必须有事务。如果当前没有事务,则抛出异常。 -
NEVER
:当前不能有事务。如果当前有事务,则抛出异常。 -
NESTED
:如果当前没有事务,则创建一个新的事务。如果当前有事务,则创建一个嵌套事务。嵌套事务的提交与回滚会独立于外部事务。
-
-
-
事务隔离级别(Isolation)
-
事务隔离级别定义了不同事务之间如何隔离。Spring 支持以下几种隔离级别:
-
READ_UNCOMMITTED
:允许读取未提交的数据,可能导致脏读、不可重复读、幻读问题。 -
READ_COMMITTED
:只能读取已经提交的数据,避免脏读,但可能会有不可重复读和幻读。 -
REPEATABLE_READ
:读取的是一致的数据,避免脏读和不可重复读,但可能会有幻读问题。 -
SERIALIZABLE
:强制事务串行化,避免脏读、不可重复读和幻读,但性能开销较大。
-
-
-
-
7.5 事务回滚机制
-
Spring 的事务回滚机制允许我们控制事务在出现异常时是否回滚。默认情况下,Spring 仅在遇到 运行时异常(
RuntimeException
) 或 错误(Error
) 时回滚事务。 -
默认回滚规则:
-
回滚事务:遇到
RuntimeException
或Error
。 -
不回滚事务:遇到
checked exceptions
(检查型异常)。
-
-
-
-
8. 配置与优化
-
8.1 配置数据源(
DataSource
)-
如果你使用的是关系型数据库(如 MySQL、PostgreSQL、H2 等),Spring Boot 会自动配置数据源。
例如,对于 MySQL,Spring Boot 会自动使用
HikariCP
(Spring Boot 默认的数据源连接池)来管理连接池。
-
-
8.2 使用
application.properties
配置数据库连接池-
Spring Boot 默认使用
HikariCP
作为连接池,它是一个高性能的 JDBC 连接池,可以有效地管理数据库连接。在application.properties
文件中,可以配置数据库连接池的相关属性。 -
配置连接池的基本参数
-
# 数据源配置 spring.datasource.url=jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # 连接池配置 spring.datasource.hikari.maximum-pool-size=20 # 最大连接池大小 spring.datasource.hikari.minimum-idle=5 # 最小空闲连接数 spring.datasource.hikari.idle-timeout=600000 # 空闲连接超时时间(毫秒) spring.datasource.hikari.max-lifetime=1800000 # 连接最大生命周期(毫秒) spring.datasource.hikari.connection-timeout=30000 # 连接超时时间(毫秒)
-
-
配置其他连接池特性
-
spring.datasource.hikari.auto-commit=true # 自动提交事务 spring.datasource.hikari.connection-test-query=SELECT 1 # 检测连接是否有效的查询语句 spring.datasource.hikari.pool-name=HikariCP # 连接池名称 spring.datasource.hikari.leak-detection-threshold=15000 # 设置连接泄露检测时间(毫秒)
-
-
配置连接池性能优化
-
spring.datasource.hikari.maximum-pool-size
:控制最大连接数。根据业务量进行调整,过大或过小都会影响性能。 -
spring.datasource.hikari.minimum-idle
:空闲连接数,设置为空闲连接池的最小值。可以帮助快速响应查询请求。 -
spring.datasource.hikari.idle-timeout
:空闲连接超时时间,当连接池中的连接空闲时间超过此值时,将被销毁。 -
spring.datasource.hikari.max-lifetime
:连接最大生命周期,当连接池中的连接达到此最大生命周期时,它们将被销毁并重新创建。
-
-
-
8.3 配置 JPA 的 Hibernate 属性(如:DDL 自动生成策略、SQL 日志输出等)
-
8.3.1 配置 Hibernate 的 DDL 自动生成策略
-
spring.jpa.hibernate.ddl-auto
:控制 Hibernate 如何自动管理数据库模式。 -
常见的值包括:
-
none
:不进行任何操作,数据库架构由外部管理。 -
update
:Hibernate 会根据实体类的变更自动更新数据库架构。 -
create
:每次应用启动时都会创建数据库架构。 -
create-drop
:在应用启动时创建架构,并在应用关闭时删除架构。-
spring.jpa.hibernate.ddl-auto=update
-
-
-
-
8.3.2 配置 Hibernate 的 SQL 日志输出
-
spring.jpa.show-sql
:控制是否输出 SQL 语句。 -
spring.jpa.properties.hibernate.format_sql
:格式化 SQL,使其更易读。 -
spring.jpa.properties.hibernate.use_sql_comments
:是否输出 SQL 注释,帮助理解 SQL 执行过程。-
spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true spring.jpa.properties.hibernate.use_sql_comments=true
-
-
-
-