一、什么是 JPA?
JPA(Java Persistence API):Java 持久化 API,是 Java EE 的标准规范。
Hibernate:JPA 的最流行实现,Spring Boot 默认使用 Hibernate。
Spring Data JPA:Spring 对 JPA 的封装,极大简化了数据库操作。
二、为什么使用 Spring Data JPA?
| 传统 JDBC | Spring Data JPA |
|---|---|
| 手动编写 SQL | 无需手写 SQL |
| 手动映射 ResultSet | 自动映射实体类 |
| 需要管理连接 | 自动管理连接池 |
| 代码冗长 | 代码简洁 |
示例对比
传统 JDBC(冗长)
java
public User findById(Long id) {
String sql = "SELECT * FROM users WHERE id = ?";
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setLong(1, id);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
return user;
}
return null;
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
Spring Data JPA(简洁)
java
public User findById(Long id) {
return userRepository.findById(id).orElse(null);
}
三、MySQL 连接步骤
步骤 1:安装 MySQL
Windows
- 下载 MySQL:dev.mysql.com/downloads/m...
- 安装 MySQL,记住设置的 root 密码
macOS
bash
brew install mysql
brew services start mysql
Linux(Ubuntu)
bash
sudo apt update
sudo apt install mysql-server
sudo systemctl start mysql
步骤 2:创建数据库
sql
-- 登录 MySQL
mysql -u root -p
-- 创建数据库
CREATE DATABASE spring_boot_demo;
-- 查看数据库
SHOW DATABASES;
-- 退出
EXIT;
步骤 3:添加依赖到 pom.xml
xml
<dependencies>
<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- MySQL 驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
步骤 4:配置数据库连接
创建 application.properties
properties
# MySQL 数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/spring_boot_demo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# JPA 配置
# 自动创建表:create(每次启动都创建)、create-drop(每次启动创建,关闭时删除)
# update(根据实体类更新表结构)、validate(验证表结构,不修改)
# none(不处理)
spring.jpa.hibernate.ddl-auto=update
# 显示 SQL 语句(方便调试)
spring.jpa.show-sql=true
# 格式化 SQL 语句
spring.jpa.properties.hibernate.format_sql=true
# 数据库方言(指定 MySQL)
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
# 服务器配置
server.port=8080
四、Entity(实体类)
User 实体类示例
java
package com.example.myapp.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "姓名不能为空")
@Size(min = 2, max = 50, message = "姓名长度必须在2-50之间")
@Column(nullable = false, length = 50)
private String name;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
@Column(nullable = false, unique = true, length = 100)
private String email;
public User() {}
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// getter/setter
public Long getId() {
return id;
}
public void setId(Long 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 + '\'' +
'}';
}
}
五、JPA 核心注解详解
User 实体类中的关键注解
| 注解 | 作用 | 说明 |
|---|---|---|
@Entity |
标记为实体类 | 对应数据库表 |
@Table(name = "users") |
指定表名 | 默认使用类名,可自定义 |
@Id |
标记主键 | 必须唯一标识 |
@GeneratedValue(strategy = GenerationType.IDENTITY) |
主键生成策略 | 自增主键 |
@Column(nullable = false) |
列约束 | 非空约束 |
@Column(unique = true) |
列约束 | 唯一约束 |
@Column(length = 50) |
列约束 | 长度限制 |
主键生成策略
| 策略 | 说明 | 适用数据库 |
|---|---|---|
IDENTITY |
数据库自增 | MySQL、PostgreSQL |
SEQUENCE |
数据库序列 | PostgreSQL、Oracle |
TABLE |
使用单独表存储主键 | 跨数据库兼容 |
AUTO |
自动选择 | Hibernate 根据数据库选择 |
六、Repository 层(数据访问层)
创建 UserRepository 接口
java
package com.example.myapp.repository;
import com.example.myapp.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 继承 JpaRepository 后,自动获得以下方法:
// - save(S entity):保存或更新
// - findById(ID id):根据 ID 查询
// - findAll():查询所有
// - deleteById(ID id):根据 ID 删除
// - count():统计数量
// - existsById(ID id):判断是否存在
// 自定义查询方法(方法名即查询语句)
Optional<User> findByEmail(String email);
boolean existsByEmail(String email);
}
七、JPA 查询方法
方法名查询(Query Method)
Spring Data JPA 支持通过方法名自动生成 SQL,无需手写查询语句。
| 方法名 | 生成的 SQL | 说明 |
|---|---|---|
findByEmail(String email) |
SELECT * FROM users WHERE email = ? |
根据邮箱查询 |
findByNameContaining(String name) |
SELECT * FROM users WHERE name LIKE %?% |
根据姓名模糊查询 |
findByEmailAndPassword(String email, String password) |
SELECT * FROM users WHERE email = ? AND password = ? |
根据邮箱和密码查询 |
countByName(String name) |
SELECT COUNT(*) FROM users WHERE name = ? |
统计数量 |
deleteById(Long id) |
DELETE FROM users WHERE id = ? |
删除指定记录 |
自定义查询示例
java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// 根据邮箱查询
Optional<User> findByEmail(String email);
// 根据姓名模糊查询
List<User> findByNameContaining(String name);
// 根据邮箱和密码查询
Optional<User> findByEmailAndPassword(String email, String password);
// 统计数量
long countByName(String name);
// 判断是否存在
boolean existsByEmail(String email);
// 使用 @Query 自定义 SQL
@Query("SELECT u FROM User u WHERE u.name = ?1 AND u.email = ?2")
List<User> findByNameAndEmail(String name, String email);
// 使用原生 SQL
@Query(value = "SELECT * FROM users WHERE name LIKE %?1%", nativeQuery = true)
List<User> findByNameLike(String name);
}
八、完整 CRUD 操作示例
1. 创建用户
java
// Controller
@PostMapping
public ResponseEntity<User> create(@Valid @RequestBody User user) {
// 检查邮箱是否已存在
if (userService.existsByEmail(user.getEmail())) {
return ResponseEntity.badRequest().build();
}
User createdUser = userService.create(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}
生成的 SQL:
sql
INSERT INTO users (name, email) VALUES (?, ?);
2. 查询所有用户
java
@GetMapping
public ResponseEntity<List<User>> findAll() {
List<User> users = userService.findAll();
return ResponseEntity.ok(users);
}
生成的 SQL:
sql
SELECT * FROM users;
3. 查询指定用户
java
@GetMapping("/{id}")
public ResponseEntity<User> findById(@PathVariable Long id) {
User user = userService.findById(id);
if (user == null) {
throw new ResourceNotFoundException("用户不存在,ID: " + id);
}
return ResponseEntity.ok(user);
}
生成的 SQL:
sql
SELECT * FROM users WHERE id = ?;
4. 更新用户
java
@PutMapping("/{id}")
public ResponseEntity<User> update(@PathVariable Long id, @Valid @RequestBody User user) {
if (!userService.existsById(id)) {
throw new ResourceNotFoundException("用户不存在,ID: " + id);
}
User updatedUser = userService.update(id, user);
return ResponseEntity.ok(updatedUser);
}
生成的 SQL:
sql
UPDATE users SET name = ?, email = ? WHERE id = ?;
5. 删除用户
java
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable Long id) {
if (!userService.existsById(id)) {
throw new ResourceNotFoundException("用户不存在,ID: " + id);
}
userService.delete(id);
return ResponseEntity.noContent().build();
}
生成的 SQL:
sql
DELETE FROM users WHERE id = ?;
九、测试 JPA 操作
使用 curl 测试
bash
# 1. 创建用户
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{
"name": "张三",
"email": "zhangsan@example.com"
}'
# 2. 查询所有用户
curl http://localhost:8080/api/users
# 3. 查询指定用户
curl http://localhost:8080/api/users/1
# 4. 更新用户
curl -X PUT http://localhost:8080/api/users/1 \
-H "Content-Type: application/json" \
-d '{
"name": "张三修改",
"email": "zhangsan_new@example.com"
}'
# 5. 删除用户
curl -X DELETE http://localhost:8080/api/users/1
十、JPA 核心概念总结
| 概念 | 说明 |
|---|---|
| Entity(实体) | 对应数据库表,用 @Entity 标记 |
| Repository(仓库) | 数据访问接口,继承 JpaRepository |
| EntityManager | JPA 核心 API,管理实体生命周期 |
| Persistence Context | 持久化上下文,管理实体状态 |
| Transaction(事务) | 保证数据一致性,Spring Boot 默认自动开启 |
十一、JPA 实体状态
| 状态 | 说明 | 示例 |
|---|---|---|
| Transient(瞬时) | 新创建的对象,未与数据库关联 | new User() |
| Managed(托管) | 被 EntityManager 管理,对应数据库记录 | userRepository.save(user) |
| Detached(游离) | 曾被管理,但已脱离 | 提交事务后的对象 |
| Removed(删除) | 已标记删除,等待事务提交 | userRepository.deleteById(id) |
十二、常见问题与解决方案
问题 1:表已存在,启动失败
错误信息:
sql
Table 'spring_boot_demo.users' already exists
解决方案: 修改 application.properties:
properties
# 改为 update(更新表结构)或 none(不处理)
spring.jpa.hibernate.ddl-auto=update
问题 2:连接数据库失败
错误信息:
vbscript
Could not create connection to database server
解决方案:
- 检查 MySQL 服务是否启动
- 检查
application.properties中的连接信息是否正确 - 检查防火墙是否阻止了连接
问题 3:主键冲突
错误信息:
rust
Duplicate entry '1' for key 'users.PRIMARY'
解决方案:
java
// 更新前先检查是否存在
if (!userRepository.existsById(id)) {
throw new ResourceNotFoundException("用户不存在");
}
user.setId(id);
userRepository.save(user); // 这里会更新,不会插入新记录
十三、最佳实践
1. 使用 Lombok 简化代码
添加依赖:
xml
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
简化后的 User 类:
java
@Entity
@Table(name = "users")
@Data // 自动生成 getter/setter/toString
@NoArgsConstructor // 自动生成无参构造
@AllArgsConstructor // 自动生成全参构造
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "姓名不能为空")
@Size(min = 2, max = 50)
private String name;
@NotBlank(message = "邮箱不能为空")
@Email
private String email;
}
2. 使用 @Transactional 管理事务
java
@Service
public class UserService {
@Transactional // 自动开启事务
public void createUserWithOrders(User user, List<Order> orders) {
userRepository.save(user);
orderRepository.saveAll(orders);
// 如果任一操作失败,全部回滚
}
}
3. 使用 DTO 分离实体和接口
java
// DTO:数据传输对象,用于接口层
public class UserDTO {
private String name;
private String email;
// 不包含敏感信息(如密码)
}
// Entity:数据库实体
@Entity
public class User {
@Id
private Long id;
private String name;
private String email;
private String password; // 敏感信息
}
4. 使用分页查询
java
// Repository
Page<User> findAll(Pageable pageable);
// Service
public Page<User> findAll(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
return userRepository.findAll(pageable);
}
// Controller
@GetMapping
public ResponseEntity<Page<User>> findAll(
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "10") int size
) {
Page<User> users = userService.findAll(page, size);
return ResponseEntity.ok(users);
}
十四、完整项目结构
css
my-app/
├── pom.xml
└── src/main/java/com/example/myapp/
├── MyApplication.java
├── model/
│ └── User.java
├── repository/
│ └── UserRepository.java
├── service/
│ └── UserService.java
├── controller/
│ ├── HelloController.java
│ └── UserController.java
├── exception/
│ ├── GlobalExceptionHandler.java
│ └── ResourceNotFoundException.java
└── resources/
└── application.properties
十五、总结
| 概念 | 说明 |
|---|---|
| Spring Data JPA | Spring 对 JPA 的封装,简化数据库操作 |
| Entity | 数据库表映射,用 @Entity 标记 |
| Repository | 数据访问接口,继承 JpaRepository |
| 方法名查询 | 通过方法名自动生成 SQL |
| @Query | 自定义查询语句 |
| @Transactional | 事务管理,保证数据一致性 |
| Pageable | 分页查询 |