在本教程中,我们将使用 Maven 构建一个 Spring Boot Rest CRUD API 示例,该示例使用 Spring Data JPA 与 H2 数据库进行交互。你会知道:
- 如何配置 Spring Data、JPA、Hibernate 以使用数据库
- 如何定义数据模型和存储库接口
- 创建 Spring Rest Controller 处理 HTTP 请求的方法
- 使用 Spring Data JPA 与 H2 数据库交互的方式
Spring Boot JPA + H2 示例概述
我们将使用 Spring Data JPA 和 H2 数据库为教程应用程序构建一个 Spring Boot Rest APIs:
- 每个教程都有 ID、标题、描述、发布状态。
- API 帮助创建、检索、更新、删除教程。
- APIs 还支持自定义查找器方法,例如按已发布状态或按标题查找。
这些是我们需要提供的 API:
方法 | 网址 | 行动 |
---|---|---|
POST | /api/tutorials | 创建新教程 |
GET | /api/tutorials | 检索所有教程 |
GET | /api/tutorials/:id | 检索教程 :id |
PUT | /api/tutorials/:id | 更新教程 :id |
DELETE | /api/tutorials/:id | 删除教程 :id |
DELETE | /api/tutorials | 删除所有教程 |
GET | /api/tutorials/published | 查找所有已发布的教程 |
GET | /api/tutorials?title=[关键字] | 查找标题包含的所有教程 keyword |
– 我们使用 Spring Data JPA 的 CRUD 操作和查找方法JpaRepository
。
– 通过配置项目依赖和数据源,数据库将成为 H2 数据库(在内存中或磁盘上)。
技术
- Java 8
- Spring Boot 2.4(带有 Spring Web MVC、Spring Data JPA)
- H2 数据库
- Maven 3.6.1
项目结构
让我简要解释一下。
–Tutorial
数据模型类对应实体和表教程。
–是为 CRUD 方法和自定义查找器方法TutorialRepository
扩展JpaRepository的接口。它将自动装配到TutorialController
.
–TutorialController
是一个RestController,它具有 RESTful 请求的请求映射方法,例如:getAllTutorials、createTutorial、updateTutorial、deleteTutorial、findByPublished …… – 在application.properties
中配置 Spring Datasource、JPA 和 Hibernate 。– pom.xml
包含 Spring Boot 和 H2 数据库的依赖项。
我们可以通过为每个教程添加注释来改进示例。这是一对多关系,我为此写了一个教程:
Spring Boot One To Many example with JPA, Hibernate
创建和设置 Spring Boot 项目
使用Spring Web 工具或您的开发工具(Spring Tool Suite、Eclipse、Intellij)创建一个 Spring Boot 项目。
然后打开pom.xml并添加这些依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
配置 Spring Boot、JPA、h2、Hibernate
在src / main / resources文件夹下,打开application.properties并写下这些行。
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto= update
spring.h2.console.enabled=true
# default path: h2-console
spring.h2.console.path=/h2-ui
spring.datasource.url
:jdbc:h2:mem:[database-name]
用于内存数据库和jdbc:h2:file:[path/database-name]
基于磁盘的数据库。spring.datasource.username
&spring.datasource.password
属性与您的数据库安装相同。- Spring Boot 使用 Hibernate 进行 JPA 实现,我们
H2Dialect
为 H2 数据库配置 spring.jpa.hibernate.ddl-auto
用于数据库初始化。我们将 value 设置为 value,update
以便在数据库中创建一个表,自动对应于定义的数据模型。对模型的任何更改也将触发对表的更新。对于生产,这个属性应该是validate
。spring.h2.console.enabled=true
告诉 Spring 启动 H2 数据库管理工具,您可以在浏览器上访问此工具:http://localhost:8080/h2-console
.spring.h2.console.path=/h2-ui
用于 H2 控制台的 url,因此默认 urlhttp://localhost:8080/h2-console
将更改为http://localhost:8080/h2-ui
.
定义数据模型
我们的数据模型是具有四个字段的教程:id、title、description、published。
在模型包中,我们定义了Tutorial
类。
model/Tutorial.java
package com.bezkoder.spring.jpa.h2.model;
import javax.persistence.*;
@Entity
@Table(name = "tutorials")
public class Tutorial {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;
@Column(name = "title")
private String title;
@Column(name = "description")
private String description;
@Column(name = "published")
private boolean published;
public Tutorial() {
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isPublished() {
return published;
}
public void setPublished(boolean isPublished) {
this.published = isPublished;
}
@Override
public String toString() {
return "Tutorial [id=" + id + ", title=" + title + ", desc=" + description + ", published=" + published + "]";
}
}
–@Entity
注解表明该类是一个持久的 Java 类。
–@Table
注释提供映射该实体的表。
–@Id
注释用于主键。
–@GeneratedValue
注解用于定义主键的生成策略。GenerationType.AUTO
表示自动增量字段。
–@Column
注释用于定义数据库中映射注释字段的列。
创建存储库接口
让我们创建一个存储库以与数据库中的教程进行交互。
在存储库包中,创建TutorialRepository
扩展接口JpaRepository
。
repository/TutorialRepository.java
package com.bezkoder.spring.jpa.h2.repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import com.bezkoder.spring.jpa.h2.model.Tutorial;
public interface TutorialRepository extends JpaRepository<Tutorial, Long> {
List<Tutorial> findByPublished(boolean published);
List<Tutorial> findByTitleContaining(String title);
}
现在我们可以使用 JpaRepository 的方法:save()
, findOne()
, findById()
, findAll()
, count()
, delete()
, deleteById()
... 而不实现这些方法。
我们还定义了自定义查找器方法:
– findByPublished()
:返回所有published
具有输入值的教程published
。
- findByTitleContaining()
:返回所有标题包含输入的教程title
。
该实现由Spring Data JPA自动插入。
您可以修改此存储库:
– 使用分页,可以在以下位置找到说明:
Spring Boot 分页和过滤器示例 | Spring JPA,Pageable
– 或使用教程按多个字段排序/排序:
Spring Data JPA 按多列排序/排序 | 弹簧靴
您还可以在以下位置找到为此 JPA 存储库编写单元测试的方法:
Spring Boot Unit Test for JPA Repositiory with @DataJpaTest
创建 Spring Rest API 控制器
最后,我们创建一个控制器,提供用于创建、检索、更新、删除和查找教程的 API。
controller/TutorialController.java
package com.bezkoder.spring.jpa.h2.controller;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.bezkoder.spring.jpa.h2.model.Tutorial;
import com.bezkoder.spring.jpa.h2.repository.TutorialRepository;
@CrossOrigin(origins = "http://localhost:8081")
@RestController
@RequestMapping("/api")
public class TutorialController {
@Autowired
TutorialRepository tutorialRepository;
@GetMapping("/tutorials")
public ResponseEntity<List<Tutorial>> getAllTutorials(@RequestParam(required = false) String title) {
try {
List<Tutorial> tutorials = new ArrayList<Tutorial>();
if (title == null)
tutorialRepository.findAll().forEach(tutorials::add);
else
tutorialRepository.findByTitleContaining(title).forEach(tutorials::add);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> getTutorialById(@PathVariable("id") long id) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
return new ResponseEntity<>(tutorialData.get(), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/tutorials")
public ResponseEntity<Tutorial> createTutorial(@RequestBody Tutorial tutorial) {
try {
Tutorial _tutorial = tutorialRepository
.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
return new ResponseEntity<>(_tutorial, HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/tutorials/{id}")
public ResponseEntity<Tutorial> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
Optional<Tutorial> tutorialData = tutorialRepository.findById(id);
if (tutorialData.isPresent()) {
Tutorial _tutorial = tutorialData.get();
_tutorial.setTitle(tutorial.getTitle());
_tutorial.setDescription(tutorial.getDescription());
_tutorial.setPublished(tutorial.isPublished());
return new ResponseEntity<>(tutorialRepository.save(_tutorial), HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/tutorials/{id}")
public ResponseEntity<HttpStatus> deleteTutorial(@PathVariable("id") long id) {
try {
tutorialRepository.deleteById(id);
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/tutorials")
public ResponseEntity<HttpStatus> deleteAllTutorials() {
try {
tutorialRepository.deleteAll();
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@GetMapping("/tutorials/published")
public ResponseEntity<List<Tutorial>> findByPublished() {
try {
List<Tutorial> tutorials = tutorialRepository.findByPublished(true);
if (tutorials.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(tutorials, HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
–@CrossOrigin
用于配置允许的来源。
–@RestController
注释用于定义控制器并指示方法的返回值应绑定到 Web 响应正文。
–@RequestMapping("/api")
声明控制器中所有 APIs 的 url 都以/api
.
– 我们使用@Autowired
注入TutorialRepository
bean 到局部变量。
运行和测试
使用命令运行 Spring Boot 应用程序:mvn spring-boot:run
。
教程表将在数据库中自动生成。
让我们使用 url: 来打开 H2 控制台http://localhost:8080/h2-ui
:
– 对于内存数据库:
– 对于磁盘数据库:
单击连接按钮,然后检查 H2 数据库,您可以看到如下内容:
创建一些教程:
之后的H2数据库tutorials
表:
更新一些教程:
表数据发生变化:
检索所有教程:
按 ID 检索教程:
查找所有已发布的教程:
查找所有标题包含字符串 'ot' 的教程:
删除教程:
删除所有教程:
H2 数据库表现在是干净的:
您还可以在以下帖子之一中使用 Client 测试此 Spring Boot 应用程序:
- 使用 Axios 的简单 HTTP 客户端
- 使用 Fetch API 的简单 HTTP 客户端
- 带有 Web API 的 Angular 8 CRUD 应用程序示例
- 带有 Web API 的 Angular 10 CRUD 应用程序示例
- 带有 Web API 的 Angular 11 CRUD 应用程序示例
- 带有 Web API 的 Angular 12 CRUD 应用程序示例
- 带有 Vue 路由器和 Axios 的 Vue 2 CRUD 应用程序
- 带有 Axios 和 Vue 路由器的 Vue 3 CRUD 应用程序
- React CRUD 示例以使用 Web API
- 使用 API 调用 React Redux CRUD 示例
结论
今天我们使用 Spring Boot 构建了一个 Rest CRUD API,Spring Data JPA 与 H2 数据库示例一起使用。
我们还看到它JpaRepository
支持一种很好的方式来进行 CRUD 操作和自定义查找器方法,而无需样板代码。
如果你想在这个 Spring 项目中添加 Pagination,你可以在以下位置找到说明:
Spring Boot Pagination & Filter example | Spring JPA,可分页
按多个字段排序/排序:
Spring Data JPA 按多列排序/排序 | 弹簧靴
处理此 Rest API 的异常是必要的:
– Spring Boot @ControllerAdvice & @ExceptionHandler 示例
– Spring Boot 中的 @RestControllerAdvice 示例
或为 JPA 存储库编写单元测试的方法:
Spring Boot Unit Test for JPA Repositiory with @DataJpaTest
快乐学习!再见。
延伸阅读
全栈 CRUD 应用程序:
– Vue + Spring Boot 示例
– Angular 8 + Spring Boot 示例
– Angular 10 + Spring Boot 示例
– Angular 11 + Spring Boot 示例
– Angular 12 + Spring Boot 示例
– Angular 13 + Spring Boot 示例
– React + Spring Boot例子
更多实践:
–使用 Spring Security 和 JWT 身份验证保护 Spring Boot 应用程序
– Spring Boot Rest XML 示例 – 具有 XML 响应的 Web 服务
– Spring Boot 多部分文件上传示例
– Spring Boot 分页和排序示例
我们可以通过为每个教程添加注释来改进示例。这是一对多关系,我为此写了一个教程:
Spring Boot One To Many example with JPA, Hibernate
源代码
您可以在Github上找到本教程的完整源代码。
使用其他数据库:
– Spring JPA + SQL Server
– Spring JPA + PostgreSQL
– Spring JPA + MySQL
– Spring Data + MongoDB
或者 JdbcTemplate 代替:
Spring Boot JdbcTemplate 示例:CRUD Rest API