Spring Boot JPA + H2 示例:构建 CRUD Rest API

Spring Boot JPA + H2 example: Build a CRUD Rest APIs - BezKodericon-default.png?t=M0H8https://www.bezkoder.com/spring-boot-jpa-h2-example/

在本教程中,我们将使用 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 请求的请求映射方法,例如:getAllTutorialscreateTutorialupdateTutorialdeleteTutorialfindByPublished …… – 在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注入TutorialRepositorybean 到局部变量。

运行和测试

使用命令运行 Spring Boot 应用程序:mvn spring-boot:run

教程表将在数据库中自动生成。

让我们使用 url: 来打开 H2 控制台http://localhost:8080/h2-ui

– 对于内存数据库:

– 对于磁盘数据库:

单击连接按钮,然后检查 H2 数据库,您可以看到如下内容:

创建一些教程:

之后的H2数据库tutorials表:

 

更新一些教程:

表数据发生变化:

 

检索所有教程:

按 ID 检索教程:

 

查找所有已发布的教程:

查找所有标题包含字符串 'ot' 的教程:

删除教程:

删除所有教程:

H2 数据库表现在是干净的:

您还可以在以下帖子之一中使用 Client 测试此 Spring Boot 应用程序:

结论

今天我们使用 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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值