Spring Boot H2
最后修改于 2023 年 8 月 2 日
在本文中,我们将展示如何在 Spring Boot 应用程序中使用嵌入式 H2 内存数据库。 示例将创建一个简单的 RESTful 应用程序。
Spring 是一个用于开发 Java 企业应用程序的 Java 应用程序框架。 它也有助于集成各种企业组件。 Spring Boot 简化了创建由 Spring 驱动的、生产级的应用程序和服务,并且具有最少的设置要求。
H2 是一个完全用 Java 编写的开源关系数据库管理系统。它可以嵌入到 Java 应用程序中,也可以在客户端-服务器模式下运行。它易于部署和安装,并且占用资源少。
JdbcTemplate 是一个 Spring 库,它帮助程序员创建与关系数据库和 JDBC 协同工作的应用程序。 它处理许多繁琐且容易出错的低级细节,例如处理事务、清理资源和正确处理异常。 JdbcTemplate 在 Spring 的 spring-jdbc
模块中提供。
JSON (JavaScript 对象表示法) 是一种轻量级的数据交换格式。 人类可以轻松读写,机器可以解析和生成 JSON。 JSON 的官方互联网媒体类型是 application/json
。 JSON 文件扩展名是 .json
。
RESTFul 应用程序遵循 REST 架构风格,该风格用于设计网络应用程序。 RESTful 应用程序生成 HTTP 请求,这些请求对资源执行 CRUD(创建/读取/更新/删除)操作。
Spring Boot RESTFul 应用程序
H2 可以轻松地与 Spring Boot 一起使用。 当 Spring Boot 在 POM 文件中检测到 H2 时,它会自动为应用程序配置一个内存 H2 数据库。
以下是一个简单的 Spring Boot 应用程序,它具有 RESTFul 服务。
build.gradle ... src ├───main │ ├───java │ │ └───com │ │ └───zetcode │ │ │ Application.java │ │ ├───controller │ │ │ MyController.java │ │ ├───model │ │ │ City.java │ │ └───service │ │ CityService.java │ │ ICityService.java │ └───resources │ application.properties │ data-h2.sql │ schema-h2.sql └── test ├── java └── resources
这是项目结构。
plugins { id 'org.springframework.boot' version '3.1.1' id 'io.spring.dependency-management' version '1.1.0' id 'java' } group = 'com.zetcode' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-jdbc' runtimeOnly 'com.h2database:h2' }
build.gradle
文件包含必要的依赖项。
package com.zetcode.model; import java.util.Objects; import java.util.StringJoiner; public class City { private Long id; private String name; private int population; public City() { } public City(Long id, String name, int population) { this.id = id; this.name = name; this.population = population; } 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 int getPopulation() { return population; } public void setPopulation(int population) { this.population = population; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; City city = (City) o; return population == city.population && Objects.equals(id, city.id) && Objects.equals(name, city.name); } @Override public int hashCode() { return Objects.hash(id, name, population); } @Override public String toString() { return new StringJoiner(", ", City.class.getSimpleName() + "[", "]") .add("id=" + id) .add("name='" + name + "'") .add("population=" + population) .toString(); } }
这是 City
Bean 类。 它包含以下属性:id、name 和 population。
spring.main.banner-mode=off spring.sql.init.platform=h2 spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:mem:testdb
application.properties
是主要的 Spring Boot 配置文件。 我们关闭 Spring Boot 横幅并配置 H2 数据库。 platform 值用于 SQL 初始化脚本:schema-${platform}.sql
和 data-${platform}.sql
。
请注意,我们没有配置数据源。 这是因为如果没有任何配置数据,Spring 会自动在内存模式下配置 H2。 我们希望拥有一个内存数据库,因此我们让 Spring 自动配置。
CREATE TABLE cities(id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255), population BIGINT);
此 SQL 脚本创建 cities
表。
INSERT INTO cities(name, population) VALUES('Bratislava', 432000); INSERT INTO cities(name, population) VALUES('Budapest', 1759000); INSERT INTO cities(name, population) VALUES('Prague', 1280000); INSERT INTO cities(name, population) VALUES('Warsaw', 1748000); INSERT INTO cities(name, population) VALUES('Los Angeles', 3971000); INSERT INTO cities(name, population) VALUES('New York', 8550000); INSERT INTO cities(name, population) VALUES('Edinburgh', 464000); INSERT INTO cities(name, population) VALUES('Berlin', 3671000);
此脚本用数据填充表。这两个脚本都位于类路径的根目录。
package com.zetcode.service; import com.zetcode.model.City; import java.util.List; public interface ICityService { List<City> findAll(); City findById(Long id); }
ICityService
提供了从数据源获取所有城市并按其 Id 获取城市的契约方法。
package com.zetcode.service; import com.zetcode.model.City; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import java.util.List; @Service public class CityService implements ICityService { private final JdbcTemplate jtm; public CityService(JdbcTemplate jtm) { this.jtm = jtm; } @Override public List<City> findAll() { String sql = "SELECT * FROM cities"; return jtm.query(sql, new BeanPropertyRowMapper<>(City.class)); } @Override public City findById(Long id) { String sql = "SELECT * FROM cities WHERE id = ?"; return jtm.queryForObject(sql, new BeanPropertyRowMapper<>(City.class), id); } }
CityService
包含 findAll
和 findById
方法的实现。 我们借助 JdbcTemplate
从 cities
表中检索所有城市。
private final JdbcTemplate jtm; public CityService(JdbcTemplate jtm) { this.jtm = jtm; }
JdbcTemplate
已注入。
String sql = "SELECT * FROM cities";
使用此 SQL,我们从 cities
表中选择所有城市。
return jtm.query(sql, new BeanPropertyRowMapper<>(City.class));
BeanPropertyRowMapper
将一行转换为指定映射目标类的新实例。
package com.zetcode.controller; import com.zetcode.model.City; import com.zetcode.service.ICityService; import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.List; @RestController public class MyController { private final ICityService cityService; public MyController(ICityService cityService) { this.cityService = cityService; } @RequestMapping("/cities") public List<City> findCities() { return cityService.findAll(); } @RequestMapping("/cities/{cityId}") public City findCity(@PathVariable Long cityId) { return cityService.findById(cityId); } @ExceptionHandler(EmptyResultDataAccessException.class) public ResponseEntity<String> noCityFound(EmptyResultDataAccessException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No City found"); } }
在 MyController
中,我们有两个对两个请求做出反应的方法。 传统的 MVC 控制器使用视图技术来执行服务器端渲染到 HTML。 RESTful Web 服务控制器将数据写入 HTTP 响应。 默认格式是 JSON。
@RestController public class MyController {
@RestController
注解在 Spring 中创建 RESTFul Web 服务。
private final ICityService cityService; public MyController(ICityService cityService) { this.cityService = cityService; }
我们注入 CityService
对象。 它用于从数据库中检索数据。
@RequestMapping("/cities") public List<City> findCities() { return cityService.findAll(); }
我们获取所有城市。
@RequestMapping("/cities/{cityId}") public City findCity(@PathVariable Long cityId) { return cityService.findById(cityId); }
我们找到一个特定的城市。
@ExceptionHandler(EmptyResultDataAccessException.class) public ResponseEntity<String> noCityFound(EmptyResultDataAccessException e) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("No City found"); }
如果没有找到城市,我们会发送错误消息。
package com.zetcode; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
`Application` 设置了 Spring Boot 应用程序。
$ ./gradlew bootRun
我们启动 Spring Boot 应用程序。
$ curl localhost:8080/cities [{"id":1,"name":"Bratislava","population":432000},{"id":2,"name":"Budapest","population":1759000}, {"id":3,"name":"Prague","population":1280000},{"id":4,"name":"Warsaw","population":1748000}, {"id":5,"name":"Los Angeles","population":3971000},{"id":6,"name":"New York","population":8550000}, {"id":7,"name":"Edinburgh","population":464000},{"id":8,"name":"Berlin","population":3671000}]
在这里,我们以 JSON 字符串的形式获取城市列表。
在本文中,我们在 RESTFul Web 应用程序中使用了内存 H2 数据库。