在本教程中,我们将构建一个使用 Spring JdbcTemplate与 H2 数据库交互的 Spring Boot Rest CRUD API 示例。你会知道:
- 如何配置 Spring Data JDBC 以使用数据库
- 如何定义数据模型和存储库接口
- 创建 Spring Rest Controller 处理 HTTP 请求的方法
- 使用 Spring JdbcTemplate 与 H2 数据库交互的方式
Spring Boot JdbcTemplate CRUD 示例概述
我们将使用 Spring Data Jdbc 和 H2 数据库为教程应用程序构建一个 Spring Boot Rest API:
- 每个教程都有 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 Jdbc 进行 CRUD 操作和查找方法。
– 通过配置项目依赖和数据源,数据库将成为 H2 数据库(在内存中或磁盘上)。
技术
- Java 8
- Spring Boot 2.5.4(带有 Spring Web MVC、Spring Data JDBC)
- H2 数据库
- Maven 3.6.1
项目结构
让我简要解释一下。
–Tutorial
数据模型类对应实体和表教程。
–TutorialRepository
是一个为 CRUD 操作和自定义查找器方法提供抽象方法的接口。它将自动装配到TutorialController
.
–JdbcTutorialRepository
实施TutorialRepository
。它使用JdbcTemplate执行 SQL 查询或更新以与数据库交互。
–TutorialController
是一个RestController,它具有 RESTful 请求的请求映射方法,例如:getAllTutorials、createTutorial、updateTutorial、deleteTutorial、findByPublished ……
– 在application.properties中配置 Spring Datasource、H2 数据库。
– pom.xml包含 Spring Boot Web、JDBC 和 H2 数据库的依赖项。
创建和设置 Spring Boot 项目
使用Spring Web 工具或您的开发工具(Spring Tool Suite、Eclipse、Intellij)创建一个 Spring Boot 项目。
然后打开pom.xml并添加这些依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</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 Data 和 H2 数据库
在src / main / resources文件夹下,打开application.properties并写下这些行。
spring.h2.console.enabled=true
# default path: h2-console
spring.h2.console.path=/h2-ui
spring.datasource.url=jdbc:h2:file:H:/h2db/testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
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
.spring.datasource.url
:jdbc:h2:mem:[database-name]
用于内存数据库和jdbc:h2:file:[path/database-name]
基于磁盘的数据库。spring.datasource.username
&spring.datasource.password
属性与您的数据库安装相同。
定义数据模型
我们的数据模型是具有四个字段的教程:id、title、description、published。
在模型包中,我们定义了Tutorial
类。
model/Tutorial.java
package com.bezkoder.spring.jdbc.model;
public class Tutorial {
private long id;
private String title;
private String description;
private boolean published;
public Tutorial() {
}
public Tutorial(long id, String title, String description, boolean published) {
this.id = id;
this.title = title;
this.description = description;
this.published = published;
}
public Tutorial(String title, String description, boolean published) {
this.title = title;
this.description = description;
this.published = published;
}
public void setId(long id) {
this.id = id;
}
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 + "]";
}
}
创建 JDBC 存储库
让我们创建一个存储库以与数据库中的教程进行交互。
在存储库包中,创建TutorialRepository
提供抽象方法的接口:
- 对于 CRUD 操作:
save
,findById
,findAll
,update
,deleteById
,deleteAll
. - 自定义查找器方法:
findByPublished
,findByTitleContaining
.
repository/TutorialRepository.java
package com.bezkoder.spring.jdbc.repository;
import java.util.List;
import com.bezkoder.spring.jdbc.model.Tutorial;
public interface TutorialRepository {
int save(Tutorial book);
int update(Tutorial book);
Tutorial findById(Long id);
int deleteById(Long id);
List<Tutorial> findAll();
List<Tutorial> findByPublished(boolean published);
List<Tutorial> findByTitleContaining(String title);
int deleteAll();
}
我们继续定义JdbcTutorialRepository
哪些实现TutorialRepository
。它使用JdbcTemplate
对象执行 SQL 查询或更新以与数据库交互。
repository/JdbcTutorialRepository.java
package com.bezkoder.spring.jdbc.repository;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
import com.bezkoder.spring.jdbc.model.Tutorial;
@Repository
public class JdbcTutorialRepository implements TutorialRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public int save(Tutorial tutorial) {
return jdbcTemplate.update("INSERT INTO tutorials (title, description, published) VALUES(?,?,?)",
new Object[] { tutorial.getTitle(), tutorial.getDescription(), tutorial.isPublished() });
}
@Override
public int update(Tutorial tutorial) {
return jdbcTemplate.update("UPDATE tutorials SET title=?, description=?, published=? WHERE id=?",
new Object[] { tutorial.getTitle(), tutorial.getDescription(), tutorial.isPublished(), tutorial.getId() });
}
@Override
public Tutorial findById(Long id) {
try {
Tutorial tutorial = jdbcTemplate.queryForObject("SELECT * FROM tutorials WHERE id=?",
BeanPropertyRowMapper.newInstance(Tutorial.class), id);
return tutorial;
} catch (IncorrectResultSizeDataAccessException e) {
return null;
}
}
@Override
public int deleteById(Long id) {
return jdbcTemplate.update("DELETE FROM tutorials WHERE id=?", id);
}
@Override
public List<Tutorial> findAll() {
return jdbcTemplate.query("SELECT * from tutorials", BeanPropertyRowMapper.newInstance(Tutorial.class));
}
@Override
public List<Tutorial> findByPublished(boolean published) {
return jdbcTemplate.query("SELECT * from tutorials WHERE published=?",
BeanPropertyRowMapper.newInstance(Tutorial.class), published);
}
@Override
public List<Tutorial> findByTitleContaining(String title) {
String q = "SELECT * from tutorials WHERE title ILIKE '%" + title + "%'";
return jdbcTemplate.query(q, BeanPropertyRowMapper.newInstance(Tutorial.class));
}
@Override
public int deleteAll() {
return jdbcTemplate.update("DELETE from tutorials");
}
}
-JDBCTemplate
器具JdbcOperations
,其提供有用的方法:execute()
,query()
,update()
,queryForObject()
...
-BeanPropertyRowMapper
工具RowMapper
,一个表行转换成指定的映射的目标类的一个新实例(Tutorial
)。
–Tutorial
类必须是顶级类并具有默认构造函数(无参数)。
创建 Spring Rest API 控制器
最后,我们创建一个控制器,提供用于创建、检索、更新、删除和查找教程的 API。
controller/TutorialController.java
package com.bezkoder.spring.jdbc.controller;
import java.util.ArrayList;
import java.util.List;
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.jdbc.model.Tutorial;
import com.bezkoder.spring.jdbc.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) {
Tutorial tutorial = tutorialRepository.findById(id);
if (tutorial != null) {
return new ResponseEntity<>(tutorial, HttpStatus.OK);
} else {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
@PostMapping("/tutorials")
public ResponseEntity<String> createTutorial(@RequestBody Tutorial tutorial) {
try {
tutorialRepository.save(new Tutorial(tutorial.getTitle(), tutorial.getDescription(), false));
return new ResponseEntity<>("Tutorial was created successfully.", HttpStatus.CREATED);
} catch (Exception e) {
return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@PutMapping("/tutorials/{id}")
public ResponseEntity<String> updateTutorial(@PathVariable("id") long id, @RequestBody Tutorial tutorial) {
Tutorial _tutorial = tutorialRepository.findById(id);
if (_tutorial != null) {
_tutorial.setId(id);
_tutorial.setTitle(tutorial.getTitle());
_tutorial.setDescription(tutorial.getDescription());
_tutorial.setPublished(tutorial.isPublished());
tutorialRepository.update(_tutorial);
return new ResponseEntity<>("Tutorial was updated successfully.", HttpStatus.OK);
} else {
return new ResponseEntity<>("Cannot find Tutorial with id=" + id, HttpStatus.NOT_FOUND);
}
}
@DeleteMapping("/tutorials/{id}")
public ResponseEntity<String> deleteTutorial(@PathVariable("id") long id) {
try {
int result = tutorialRepository.deleteById(id);
if (result == 0) {
return new ResponseEntity<>("Cannot find Tutorial with id=" + id, HttpStatus.OK);
}
return new ResponseEntity<>("Tutorial was deleted successfully.", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>("Cannot delete tutorial.", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
@DeleteMapping("/tutorials")
public ResponseEntity<String> deleteAllTutorials() {
try {
int numRows = tutorialRepository.deleteAll();
return new ResponseEntity<>("Deleted " + numRows + " Tutorial(s) successfully.", HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>("Cannot delete tutorials.", 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 数据库,您可以看到类似这样的内容。
在SQL Statement中,编写 SQL 脚本来创建tutorials表:
CREATE TABLE tutorials
(
id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(255),
description VARCHAR(255),
published NUMBER(1)
);
创建一些教程:
之后的H2数据库tutorials
表:
更新一些教程:
表数据发生变化:
检索所有教程:
按 ID 检索教程:
查找所有已发布的教程:
查找标题包含字符串“jdbc”的所有教程:
删除教程:
删除所有教程:
数据库表现在很干净:
您还可以在以下帖子之一中使用 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 构建了一个 CRUD Rest API,Spring Data JDBCTemplate 与 H2 数据库示例一起使用。
您可以处理此 Rest API 的异常是必要的:
– Spring Boot @ControllerAdvice & @ExceptionHandler 示例
– Spring Boot 中的 @RestControllerAdvice 示例
快乐学习!再见。
延伸阅读
全栈 CRUD 应用程序:
– Spring Boot + Vue.js 示例
– Angular 8 + Spring Boot 示例
– Angular 10 + Spring Boot 示例
– Angular 11 + Spring Boot 示例
– Angular 12 + Spring Boot 示例
– React + Spring Boot 示例
更多实践:
–使用 Spring Security 和 JWT 身份验证保护 Spring Boot 应用程序
– Spring Boot Rest XML 示例 – 具有 XML 响应的 Web 服务
– Spring Boot 多部分文件上传示例
– Spring Boot 分页和排序示例
源代码
您可以在Github上找到本教程的完整源代码。
您还将看到它JpaRepository
支持一种很好的方式来进行 CRUD 操作和自定义查找器方法,而无需样板代码。请访问:
Spring Boot JPA + H2 示例:构建 CRUD Rest API
其他数据库:
–使用 MySQL 的
Spring Boot JdbcTemplate 示例–使用 PostgreSQL 的Spring Boot JdbcTemplate 示例 – 使用 SQL Server 的Spring Boot JdbcTemplate 示例 – 使用 Oracle 的 Spring Boot JdbcTemplate 示例