ZetCode

Spring Boot REST H2

最后修改于 2023 年 7 月 25 日

本文将创建一个简单的 Spring Boot RESTful 应用程序,并使用 H2 数据库。

Spring 是一个流行的 Java 应用程序框架,用于创建企业应用程序。Spring Boot 是一种以最小的努力创建独立、生产级别的 Spring 应用程序的方式。

Apache Tomcat 是 Apache 软件基金会 (ASF) 开发的开源 Java Servlet 容器。Tomcat 实现多个 Java EE 规范,包括 Java Servlet、JavaServer Pages (JSP)、Java EL 和 WebSocket。Tomcat 可以独立运行,也可以嵌入式运行。

H2 是一个完全用 Java 编写的开源关系数据库管理系统。它可以嵌入到 Java 应用程序中,也可以在客户端-服务器模式下运行。它易于部署和安装,并且占用资源少。

JdbcTemplate 是一个 Spring 库,它帮助程序员创建与关系数据库和 JDBC 交互的应用程序。它处理许多繁琐且容易出错的底层细节,例如事务处理、资源清理和正确异常处理。JdbcTemplate 包含在 Spring 的 spring-jdbc 模块中。

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。它易于人类阅读和编写,也易于机器解析和生成。JSON 的官方 Internet 媒体类型是 application/json。JSON 文件扩展名为 .json

RESTful 应用程序遵循 REST 架构风格,用于设计网络应用程序。RESTful 应用程序生成 HTTP 请求,这些请求对资源执行 CRUD(创建/读取/更新/删除)操作。

应用程序

我们的应用程序是一个 Spring Boot RESTful 应用程序,它运行在嵌入式 Tomcat 服务器上。它以 JSON 格式返回 H2 数据库中的数据。该应用程序使用 JdbcTemplate 来简化 JDBC 编程。

build.gradle
...
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           ├── bean
│   │           │   └── City.java
│   │           ├── controller
│   │           │   └── MyController.java
│   │           └── service
│   │               ├── CityService.java
│   │               └── ICityService.java
│   └── resources
│       ├── application.yml
│       ├── data-h2.sql
│       └── schema-h2.sql
└── test
    └── java

这是项目结构。

build.gradle
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'
    implementation 'com.h2database:h2'
}

这是 Gradle 构建文件。h2 依赖项是 H2 数据库的驱动程序。spring-boot-starter-web 是用于使用 Spring MVC 构建 Web(包括 RESTful)应用程序的启动器。spring-boot-starter-jdbc 是在 Spring Boot 中使用 JDBC 的启动器。

application.yml
server:
  port: 8086
  servlet:
    context-path: /rest

spring:
  main:
    banner-mode: "off"
  datasource:
    driverClassName: org.h2.Driver
    url: jdbc:h2:mem:testdb
  sql:
    init:
      platform: h2

logging:
  level:
    org:
      springframework: ERROR

application.yml 文件包含 Spring Boot 应用程序的各种配置设置。我们有服务器端口和上下文路径(应用程序名称)的映射。使用 banner-mode 属性可以关闭 Spring 启动横幅。平台值用于 SQL 初始化脚本:schema-${platform}.sqldata-${platform}.sql。H2 数据库在内存中运行。此外,我们将 spring 框架的日志级别设置为 ERROR。该文件位于 src/main/resources 目录中。

com/zetcode/bean/City.java
package com.zetcode.bean;

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 String toString() {
        return "City{" + "id=" + id + ", name=" + name +
                ", population=" + population + '}';
    }
}

这是 City bean。

schema-h2.sql
CREATE TABLE cities(id BIGINT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(255), population BIGINT);

此 SQL 脚本创建 cities 表。

data-h2.sql
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);

此脚本用数据填充表。这两个脚本都位于类路径的根目录。

com/zetcode/service/ICityService.java
package com.zetcode.service;

import com.zetcode.bean.City;
import java.util.List;

public interface ICityService {

    List<City> findAll();
    City findById(Long id);
}

ICityService 提供了从数据源获取所有城市和按 ID 获取城市的方法契约。

com/zetcode/CityService.java
package com.zetcode.service;

import com.zetcode.bean.City;
import org.springframework.beans.factory.annotation.Autowired;
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;

    @Autowired
    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 包含 findAllfindById 方法的实现。我们使用 Spring JdbcTemplate 执行 SQL 代码。

private final JdbcTemplate jtm;

@Autowired
public CityService(JdbcTemplate jtm) {
    this.jtm = jtm;
}

JdbcTemplate 已注入。

String sql = "SELECT * FROM cities";

这是从 cities 表中选择所有城市的 SQL。

return jtm.query(sql, new BeanPropertyRowMapper<>(City.class));

该语句使用 query 执行。BeanPropertyRowMapper 将一行转换为指定映射目标类的新实例。

String sql = "SELECT * FROM cities WHERE id=?";

这是从 cities 表中选择由 id 标识的特定城市的 SQL。

return jtm.queryForObject(sql, new BeanPropertyRowMapper<>(City.class), id);

要从 cities 表中获取一行,我们使用 queryForObject 方法。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.bean.City;
import com.zetcode.service.ICityService;
import org.springframework.beans.factory.annotation.Autowired;
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;

    @Autowired
    public MyController(ICityService cityService) {
        this.cityService = cityService;
    }

    @RequestMapping("/cities")
    public List<City> findCities() {

        return cityService.findAll();
    }

    @RequestMapping("/cities/{userId}")
    public City findCity(@PathVariable Long userId) {

        return cityService.findById(userId);
    }
}

这是 Spring Boot RESTful 应用程序的控制器类。@RestController 注解创建了一个 RESTful 控制器。传统的 MVC 控制器使用 ModelAndView,而 RESTful 控制器直接返回对象,对象数据以 JSON 或 XML 格式直接写入 HTTP 响应。

private final ICityService cityService;

@Autowired
public MyController(ICityService cityService) {
    this.cityService = cityService;
}

我们将 ICityService 注入到 countryService 字段中。

@RequestMapping("/cities")
public List<City> findCities() {

    return cityService.findAll();
}

@RequestMapping 注解用于将 Web 请求映射到 Spring 控制器方法。在这里,我们将 /cities 路径的请求映射到控制器的 findCities 方法。默认请求是 GET 请求。

我们不需要手动将 City 域对象转换为 JSON。因为 Jackson 2 位于类路径中,并通过 spring-boot-starter-web 包含,Spring 会自动选择 MappingJackson2HttpMessageConverterCity 实例转换为 JSON。

com/zetcode/Application.java
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 应用程序。@SpringBootApplication 启用了自动配置和组件扫描。

$ ./gradlew bootRun

使用 ./gradlew bootRun 命令运行应用程序。应用程序部署在嵌入式 Tomcat 服务器上。

$ curl localhost:8086/rest/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}]

使用 curl 命令,我们可以获取所有城市。

$ curl localhost:8086/rest/cities/1
{"id":1,"name":"Bratislava","population":432000}

在这里,我们获取一个由其 id 标识的城市。

在本文中,我们创建了一个 Spring Boot RESTful 应用程序,该应用程序以 JSON 格式返回 H2 数据库中的数据。