目录
[1. 理解 JPA(Java Persistence API)](#1. 理解 JPA(Java Persistence API))
[1.1 什么是 JPA?](#1.1 什么是 JPA?)
[1.2 JPA 与 Hibernate 的关系](#1.2 JPA 与 Hibernate 的关系)
[1.3 JPA 的基本注解:@Entity, @Table, @Id, @GeneratedValue](#1.3 JPA 的基本注解:@Entity, @Table, @Id, @GeneratedValue)
[1.4 JPA 与数据库表的映射](#1.4 JPA 与数据库表的映射)
[2. Spring Data JPA 概述](#2. Spring Data JPA 概述)
[2.1 什么是 Spring Data JPA?](#2.1 什么是 Spring Data JPA?)
[2.2 spring-boot-starter-data-jpa 的作用](#2.2 spring-boot-starter-data-jpa 的作用)
[2.3 配置 spring-boot-starter-data-jpa 依赖](#2.3 配置 spring-boot-starter-data-jpa 依赖)
[3. Spring Boot 和 JPA 整合案例](#3. Spring Boot 和 JPA 整合案例)
[3.1 创建一个基本的 CRUD Web 应用](#3.1 创建一个基本的 CRUD Web 应用)
[3.2 Spring Boot 集成 JPA 的实践](#3.2 Spring Boot 集成 JPA 的实践)
[3.2.1 引入基本的 Spring Data JPA 依赖](#3.2.1 引入基本的 Spring Data JPA 依赖)
[3.2.2 引入数据库连接池(默认使用 HikariCP)推荐默认的就行](#3.2.2 引入数据库连接池(默认使用 HikariCP)推荐默认的就行)
[使用 DBCP2 连接池](#使用 DBCP2 连接池)
[使用 C3P0 连接池](#使用 C3P0 连接池)
[3.2.3 数据库驱动](#3.2.3 数据库驱动)
[3.2.4 配置 application.properties 或 application.yml](#3.2.4 配置 application.properties 或 application.yml)
[4. 实体类与数据库表映射](#4. 实体类与数据库表映射)
[4.1 数据库建表语句](#4.1 数据库建表语句)
[4.2 创建一个 JPA 实体类](#4.2 创建一个 JPA 实体类)
[4.2 使用 JPA 的基本类型映射(如:@Column, @OneToMany, @ManyToOne 等)](#4.2 使用 JPA 的基本类型映射(如:@Column, @OneToMany, @ManyToOne 等))
[JPA 提供了许多注解来描述实体类与数据库表之间的关系,包括对列、主键、关系的映射。](#JPA 提供了许多注解来描述实体类与数据库表之间的关系,包括对列、主键、关系的映射。)
[4.2.1 @Column](#4.2.1 @Column)
[4.2.2 @ManyToOne 和 @OneToMany](#4.2.2 @ManyToOne 和 @OneToMany)
[4.2.3 @OneToOne](#4.2.3 @OneToOne)
[4.2.4 @ManyToMany](#4.2.4 @ManyToMany)
[5. Spring Data JPA Repository](#5. Spring Data JPA Repository)
[5.1 JpaRepository 和 CrudRepository 接口](#5.1 JpaRepository 和 CrudRepository 接口)
[5.1.1 CrudRepository 接口](#5.1.1 CrudRepository 接口)
[5.1.2 JpaRepository 接口(推荐,实现的多)](#5.1.2 JpaRepository 接口(推荐,实现的多))
[5.2 创建 Repository 接口](#5.2 创建 Repository 接口)
[5.3 常用的 CRUD 操作(保存、查询、删除、更新)](#5.3 常用的 CRUD 操作(保存、查询、删除、更新))
[5.4 自定义查询方法(如:通过方法名自动生成查询)](#5.4 自定义查询方法(如:通过方法名自动生成查询))
[5.5 使用 @Query 注解编写自定义 SQL 查询](#5.5 使用 @Query 注解编写自定义 SQL 查询)
[5.5.1 使用 JPQL(推荐方式)](#5.5.1 使用 JPQL(推荐方式))
[5.5.2 使用原生 SQL](#5.5.2 使用原生 SQL)
[5.5.3 使用 @Modifying 和 @Transactional 进行更新/删除操作](#5.5.3 使用 @Modifying 和 @Transactional 进行更新/删除操作)
[6. JPA 查询功能](#6. JPA 查询功能)
[6.1 JPQL查询](#6.1 JPQL查询)
[6.2 Criteria API(动态查询)](#6.2 Criteria API(动态查询))
[6.3 使用 Spring Data JPA 提供的分页与排序功能](#6.3 使用 Spring Data JPA 提供的分页与排序功能)
[6.3.1 分页](#6.3.1 分页)
[6.3.2 排序](#6.3.2 排序)
[7. 事务管理](#7. 事务管理)
[7.1 什么是事务?](#7.1 什么是事务?)
[7.2 Spring 的事务管理基础](#7.2 Spring 的事务管理基础)
[7.3 使用 @Transactional 注解](#7.3 使用 @Transactional 注解)
[7.4 事务的传播行为和隔离级别](#7.4 事务的传播行为和隔离级别)
[7.5 事务回滚机制](#7.5 事务回滚机制)
[8. 配置与优化](#8. 配置与优化)
[8.1 配置数据源(DataSource)](#8.1 配置数据源(DataSource))
[8.2 使用 application.properties 配置数据库连接池](#8.2 使用 application.properties 配置数据库连接池)
[8.3 配置 JPA 的 Hibernate 属性(如:DDL 自动生成策略、SQL 日志输出等)](#8.3 配置 JPA 的 Hibernate 属性(如:DDL 自动生成策略、SQL 日志输出等))
[8.3.1 配置 Hibernate 的 DDL 自动生成策略](#8.3.1 配置 Hibernate 的 DDL 自动生成策略)
[8.3.2 配置 Hibernate 的 SQL 日志输出](#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` 来进行映射:
*
```java
@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` 依赖。
*
```java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```
3. Spring Boot 和 JPA 整合案例
### 3.1 创建一个基本的 CRUD Web 应用
### 3.2 Spring Boot 集成 JPA 的实践
*
#### 3.2.1 引入基本的 Spring Data JPA 依赖
*
```java
<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 连接池
*
```java
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
```
*
##### 使用 C3P0 连接池
*
```java
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
</dependency>
```
*
#### 3.2.3 数据库驱动
* 根据你使用的数据库,你需要添加相应的数据库驱动。例如,假设你使用 MySQL,你需要添加 MySQL 驱动:
*
```java
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
```
*
#### 3.2.4 配置 `application.properties` 或 `application.yml`
* application.properties
*
```java
# 数据库配置
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
*
```java
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
```
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 实体类
* ![](https://i-blog.csdnimg.cn/direct/a5a98123443749d9852ef4a9eff07bef.png)
*
```java
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 语句来控制列的定义。
*
```java
@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`:表示一对多的关系。例如,一个用户可以有多个订单。
*
```java
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
}
```
```java
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` 注解。
```java
@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` 注解。
```java
@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 会自动实现所有方法。
*
```java
@Repository // 标记为一个 Spring 的 Repository
public interface UserRepository extends JpaRepository<User, Integer> {
// 可以在这里定义查询方法,例如:
// List<User> findByName(String name);
}
```
### 5.3 常用的 CRUD 操作(保存、查询、删除、更新)
*
```java
@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` 属性模糊查找。
*
```java
@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 查询。
*
```java
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` 来指定。
*
```java
@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` 注解,并且方法应该是事务性的。
*
```java
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`**:表示查询条件。
*
```java
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)。
*
```java
@Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询
List<User> findByName(String name, Pageable pageable); // 根据名字查询用户
```
```java
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 方法
}
```
*
#### 6.3.2 排序
* 用法一样
*
```java
@Query("SELECT u FROM User u WHERE u.name = ?1") // JPQL 查询
List<User> findByName(String name, Pageable pageable, Sort sort); // 根据名字查询用户
```
```java
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` 文件中,可以配置数据库连接池的相关属性。
* 配置连接池的基本参数
*
```java
# 数据源配置
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 # 连接超时时间(毫秒)
```
* 配置其他连接池特性
*
```java
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`:在应用启动时创建架构,并在应用关闭时删除架构。
*
```java
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 执行过程。
*
```java
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
```