Spring Data JPA 是 Spring 框架提供的一个模块,用于简化与关系型数据库的交互和数据访问。它基于JPA(Java Persistence API)标准,并提供了一组易于使用的API和工具,帮助开发人员更轻松地进行数据库操作。通过Spring Data JPA,开发人员可以通过编写简洁的代码来执行常见的 CRUD 操作,同时还支持高级查询、分页、事务管理等功能。它的目标是提供一种更简单、更高效的方式来处理数据库操作,减少开发人员的工作量,并提高应用程序的可维护性和可扩展性。
本文将会指导你如何在 Spring Boot 应用中整合、使用 Spring Data Jpa。
软件版本:
- Java:
17
- Spring Boot:
3.1.3
- MySQL:
8.0.27
创建工程 {#创建工程}
点击 start.springboot.io 快速创建 Spring Boot 整合 Spring Data Jpa 的示例应用。
我们选择了 spring-boot-starter-web
、spring-boot-starter-data-jpa
、mysql-connector-j
和 spring-boot-starter-test
依赖。
pom.xml
如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
spring-boot-starter-data-jpa
默认使用 Hibernate
作为 JPA 实现。本文使用 MYSQL 数据库进行演示,如果你使用其他数据库需要修改驱动。
项目设置 {#项目设置}
创建实体类 {#创建实体类}
在 cn.springdoc.demo.entity
包中创建 SysUser
实体类。
package cn.springdoc.demo.entity;
import java.time.LocalDateTime;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "sys_user") // 表名称
public class SysUser {
// ID
@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY) // 使用数据库自增
private Long id;
// 账户
@Column
private String account;
// 是否启用
@Column
private Boolean enabled;
// 创建时间
@Column
private LocalDateTime createAt;
// 省略 get/set/toString 方法
}
@Entity
表示这个类是一个需要受 EntityManager
管理的实体类。@Table(name = "sys_user")
注解指定了实体在数据库中所对应的表名称。@Id
用于标识ID字段,@GeneratedValue(strategy = GenerationType.IDENTITY)
注解指定了 ID 值的生成方式,其中 GenerationType.IDENTITY
表示主键由数据库自动生成(自增)。@Column
注解表示对象字段和数据表列的映射关系。
注意,实体类一定要有一个 无参构造函数。
SysUser
实体对应的表结构如下:
CREATE TABLE `sys_user` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`account` varchar(50) COLLATE utf8mb4_general_ci NOT NULL COMMENT '账户',
`create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
`enabled` tinyint unsigned NOT NULL COMMENT '是否启用。0:禁用,1:启用',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='系统用户';
本文重点在于介绍如何整合、使用 spring-data-jpa,对于更多可用的实体注解,及其详细用法可以参考 官方文档。
创建 Repository 接口 {#创建-repository-接口}
在 cn.springdoc.demo.repository
包下创建 SysUserRepository
接口,并且继承 JpaRepository
、JpaSpecificationExecutor
接口。
package cn.springdoc.demo.repository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;
import cn.springdoc.demo.entity.SysUser;
@Repository
public interface SysUserRepository extends JpaRepository<SysUser, Long>, JpaSpecificationExecutor <SysUser> {
}
通过继承 JpaRepository
、JpaSpecificationExecutor
就可以获得已经预定义的各种 CRUD 方法。其中 JpaRepository
的泛型对象是实体类型和 ID 类型,JpaSpecificationExecutor
的泛型对象只有实体类型。
使用 @Repository
注解表示这是一个 Repository
接口。
定义 @EnableJpaRepositories
/ @EntityScan
注解 {#定义-enablejparepositories-entityscan-注解}
接下来,需要在启动类上定义 @EnableJpaRepositories
、@EntityScan
注解,分别指定 repository 和实体类所在的包。
package cn.springdoc.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication
@EnableJpaRepositories(basePackages = "cn.springdoc.demo.repository") // repository 所在的包
@EntityScan(basePackages = "cn.springdoc.demo.entity") // 实体所在的包
public class SpringdocJpaApplication {
public static void main(String[] args) {
SpringApplication.run(SpringdocJpaApplication.class, args);
}
}
这两个注解的 basePackages
属性都是一个 String[]
,也就是说可以同时指定多个目录。
application.properties 配置 {#applicationproperties-配置}
你也可以把 application.properties
替换为 application.yaml
。
#---------------------
# 数据源配置
#---------------------
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&allowMultiQueries=true
spring.datasource.username=root
spring.datasource.password=root
#---------------------
# 日志配置
#---------------------
# 打印执行 SQL 中绑定的参数
logging.level.org.hibernate.orm.jdbc.bind="TRACE"
#---------------------
# JPA 配置
#---------------------
# 数据库方言
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
# 是否打印 SQL
spring.jpa.show-sql=true
# 格式化打印的 SQL
spring.jpa.properties.hibernate.format_sql=true
# 应用启动时候的建表策略
spring.jpa.hibernate.ddl-auto=none
上述常用配置中,数据源的配置是必须的。其他配置项都有默认值。
其中 spring.jpa.hibernate.ddl-auto
比较重要,表示建表的策略,可选的枚举值如下:
create
:不管表是否存在,每次启动都会重新建表(会导致数据丢失)。create-drop
:启动的时候创建表,程序退出(SessionFactory
关闭)的时候删除表。none
:不进行任何操作。update
:如果数据表不存在则创建,在实体对象被修改后,下次启动重新修改表结构(不会删除已经存在的数据)。validate
:启动的时候验证数据表的结构。
建议使用 none
,手动维护数据表结构,以避免不小心修改了实体对象后导致表结构被修改,甚至是数据丢失。
这里仅列出了部分核心配置,关于完整的配置项及其详情请参阅 官方文档。
测试 {#测试}
创建测试类 SpringdocJpaApplicationTests
。
package cn.springdoc.demo;
import java.time.LocalDateTime;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import cn.springdoc.demo.entity.SysUser;
import cn.springdoc.demo.repository.SysUserRepository;
@SpringBootTest(classes = SpringdocJpaApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
class SpringdocJpaApplicationTests {
static final Logger logger = LoggerFactory.getLogger(SpringdocJpaApplicationTests.class);
@Autowired
SysUserRepository sysUserRepository;
@Test
void contextLoads() {
// 创建实体类
SysUser sysUser = new SysUser();
sysUser.setAccount("springdoc");
sysUser.setEnabled(Boolean.TRUE);
sysUser.setCreateAt(LocalDateTime.now());
// 保存实体
this.sysUserRepository.saveAndFlush(sysUser);
// 根据自增ID检索实体
Optional<SysUser> user = this.sysUserRepository.findById(sysUser.getId());
logger.info("user={}", user.get());
}
}
在该测试中,先创建、初始化了一个 SysUser
实体对象,然后使用 sysUserRepository
先进行保存。然后再根据回写的自增ID,进行检索。
执行测试,输出日志如下:
Hibernate:
insert
into
sys_user
(account,create_at,enabled)
values
(?,?,?)
2023-09-04T10:21:05.841+08:00 TRACE 11444 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [1] as [VARCHAR] - [springdoc]
2023-09-04T10:21:05.842+08:00 TRACE 11444 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [2] as [TIMESTAMP] - [2023-09-04T10:21:05.747462800]
2023-09-04T10:21:05.843+08:00 TRACE 11444 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [3] as [BOOLEAN] - [true]
Hibernate:
select
s1_0.id,
s1_0.account,
s1_0.create_at,
s1_0.enabled
from
sys_user s1_0
where
s1_0.id=?
2023-09-04T10:21:05.964+08:00 TRACE 11444 --- [ main] org.hibernate.orm.jdbc.bind : binding parameter [1] as [BIGINT] - [1]
2023-09-04T10:21:05.982+08:00 INFO 11444 --- [ main] c.s.demo.SpringdocJpaApplicationTests : user=SysUser [id=1, account=springdoc, enabled=true, createAt=2023-09-04T10:21:06]
测试通过,日志中可以看到测试所执行的 insert
、select
SQL 以及绑定的参数,一切OK,整合成功!
最后附上工程完整的目录结构。
总结 {#总结}
在 Spring Boot 中整合、使用 JPA 的步骤如下:
- 添加
spring-boot-starter-data-jpa
依赖,以及对应的数据库驱动。 - 创建实体类,使用
@Entity
和@Table
进注解,必须要有@Id
字段。 - 创建 Repository 接口,可以通过继承
JpaRepository
和JpaSpecificationExecutor
接口来继承预定义的 CRUD 方法。 - 在配置文件(properties/yaml)中配置数据源、JPA 的属性。
- 在启动类上添加
@EnableJpaRepositories
和@EntityScan
注解,以指定Repository
接口和Entity
类所在的包。