使用Spring Boot创建Web应用程序的DB访问部分
首先
我想用Spring Boot + Thymeleaf来集成开发Web应用程序。
以下是上一篇「使用Spring Boot创建Web应用程序〜Hello World篇〜」的延续。
我希望在本次开发中实现对数据库的访问(初始化、注册、查询)功能。本篇文章的目标是显示能够从数据库获取/注册值的以下屏幕。
这次的材料
-
- Spring Boot
-
- Flayway
-
- Mybatis
- h2database
完成的源代码可以在这里访问。
总结
这是本次使用材料的概要。
飞英
这是一个数据库迁移框架。简单来说,它是一个自动创建和升级数据库表格的工具。
优点
-
- 環境構築時に手動で SQL を流さなくても良い
-
- 現在の DB のバージョンを気にせずに起動すれば自動的に最新になる
- → アプリを起動したけど SQL エラーが発生した.原因を調査したら DB のテーブルが古かったということがなくなる
Mybatis -> Mybatis是一个Java持久层框架。
这是一个将SQL和Java对象进行关联的持久化框架。简单来说,它可以将XML中编写的SQL和Java代码进行对应,并自动进行类型转换。通过它,你可以不再使用Java的字符串连接来编写SQL。
H2数据库
运行在Java平台上的ACID关系数据库。
虽然可以使用Oracle或者PostgreSQL,但为了测试方便且不需要复杂的环境设置,我们选择了这个轻量级的数据库。
由于是内存数据库,所以很轻便,但是如果不进行持久化操作,在程序结束时所有数据都将会消失。
安装
为了将这次使用的三个添加到依赖关系中,我们将在 build.gradle 的 dependencies 中添加以下内容。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
annotationProcessor 'org.projectlombok:lombok'
providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
implementation 'org.flywaydb:flyway-core' // 追加
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.3' // 追加
runtimeOnly 'com.h2database:h2' // 追加
}
只需这样,安装就完成了。
数据库的初始设置
首先要创建数据库的定义文件。
默认情况下,它被设置在src/main/resources/db/migration目录下,所以在创建SQL文件时,我们需要创建不存在的文件夹。
文件名的格式如下:
V{版本号}__{描述}.sql
※ 分隔符是两个下划线。
文件名的例子
-
- V1.0__init.sql
- V1.0.1__insert_test_data.sql
版本会按照数字的大小顺序进行执行。本次将以V1.0__init.sql的形式插入表的定义和初始数据。
CREATE TABLE neko_table (
id INT,
name VARCHAR(30),
age INT
);
insert into neko_table (id, name, age) values (1, 'たま', 3);
insert into neko_table (id, name, age) values (2, 'みけ', 1);
insert into neko_table (id, name, age) values (3, 'くろ', 4);
只需这样,应用程序在启动时会查找当前数据库版本,如果是空数据库则创建,如果不是最新版本则更新到最新定义。
从数据库中获取信息
从数据库中获取值和进行注册使用的是MyBatis。
领域
因为与DB列名称一致创建变得更容易,所以我们将创建一个以DB列命名为同名的实体。
package com.example.demo.domain;
import lombok.Data;
@Data // Lombokでgetterやsetterを自動生成
public class Neko {
private int id;
private String name;
private int age;
}
你可以使用Lombok自动生成getter/setter从而减少代码量。
绘制地图的人
界面
定义了数据库访问的接口。
package com.example.demo.mapper;
import java.util.ArrayList;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import com.example.demo.domain.Neko;
@Repository // springのDIの対象となる
@Mapper // MybatisでxmlのSQLが対応づけられる
public interface NekoMapper {
public ArrayList<Neko> getNekoList();
public boolean insertNeko(Neko neko);
}
我会准备一个方法来简单地获取全部内容,并准备两个额外的方法。
通过@Repository注解将其注册到Spring的依赖注入中,
@Mapper注解将其与在xml中定义的SQL语句的接口进行对应。
该方法在执行时的SQL语句将在XML中定义。
虽然也可以使用@Sql注解进行说明, 但由于注解本身无法实现某些功能,并且当注解变得多行时,可读性会降低,因此选择在XML文件中进行定义。
配置
将数据库连接设置添加到 application.properties 文件中。
# FlywayのDB定義
spring.flyway.url=jdbc:h2:mem:NekoDB
spring.flyway.user=user
spring.flyway.password=password
# MybatisのDB定義
spring.datasource.url=jdbc:h2:mem:NekoDB
spring.datasource.username=user
spring.datasource.password=password
在Flyway和Mybatis中都需要进行配置。
数据库的URL将是jdbc:h2:mem:<DB名>。
用户和密码可以是任意的。
为了简化在 XML 中编写更多的 SQL,我们将以下内容添加到 application.properties 文件中。
# Mybatisのタイプエイリアス
mybatis.type-aliases-package=com.example.demo.domain,com.example.demo.mapper
# DB上のカラム「NEKO_TYPE」をJavaの「nekoType」に自動変換する
mybatis.configuration.map-underscore-to-camel-case=true
通过指定类型别名可以省去写入FQCN的麻烦。在数据库中,经常使用蛇形命名法,而在Java中,则经常使用驼峰命名法,因此启用自动转换设置。*在本次代码中不需要。
SQL (Structured Query Language)
接下来是SQL的定义。
需要使用与Mapper相同的文件名进行创建,所以将其保存在以下层次结构中。
/src/main/resources/com/example/demo/mapper/NekoMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- mapperのFQCNを指定 -->
<mapper namespace="com.example.demo.mapper.NekoMapper">
<!-- select文はselectタグを利用する.idはインターフェースのメソッド名とする.resultTypeで結果を格納するJavaのオブジェクトを指定 -->
<select id="getNekoList" resultType="Neko">
SELECT * FROM neko_table
</select>
<!-- insert文はinsertタグを利用する.idはインターフェースのメソッド名とする.-->
<!-- parameterTypeで引数のJavaのオブジェクトを指定.#{name}と指定すると,オブジェクトのgetName()から値を取得する. -->
<insert id="insertNeko" parameterType="Neko">
INSERT INTO neko_table (id, name, age) VALUES (#{id}, #{name}, #{age})
</insert>
</mapper>
由于在配置中指定了类型别名,所以只需在resultType中写入类名而不是FQCN即可。
服务
package com.example.demo.service;
import java.util.ArrayList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.example.demo.domain.Neko;
import com.example.demo.mapper.NekoMapper;
import lombok.RequiredArgsConstructor;
@Service
@RequiredArgsConstructor
public class NekoService {
@Autowired
private final NekoMapper mapper;
/**
* ねこの一覧を取得する
*
* @return 一覧
*/
public ArrayList<Neko> getNekoList() {
return mapper.getNekoList();
}
/**
* ねこを登録する
*
* @param neko
* @return
*/
public boolean insertNeko(Neko neko) {
return mapper.insertNeko(neko);
}
}
这次不像之前那样创建数组并返回,而是为了从数据库中获取信息,我们要执行映射器。
※由于没有进行错误检查等工作,所以几乎没有起到任何作用。
控制器
从上次到现在没有任何变更。
观看
没有从上次开始有任何变动。
确认行动
我认为表格的内容是由Flyway投入的初始数据。
将数据注册到数据库中。
我在数据引用方面已经做了大约一半的准备,但我也想实现数据插入(insert)。
观看
定义用于数据注册的表单。
<form th:action="@{neko}" th:method="POST">
<div>登録フォーム</div>
<input type="text" name="id" placeholder="ID" />
<input type="text" name="name" placeholder="名前" />
<input type="text" name="age" placeholder="年齢" />
<input type="submit" value="登録" />
</form>
请将POST请求也设定为与GET请求相同的“neko”。
控制器
为了使得PSOT能够正常工作,我们在NekoController.java中添加以下方法。
@PostMapping("neko") // nekoへのPOSTを制御する
public String insertNeko(@ModelAttribute Neko form, Model model) { // formとしてNekoをそのまま使う(手抜き)
service.insertNeko(form); // DBにinsert
model.addAttribute("nekoList", service.getNekoList()); // serviceから一覧を再取得
return "neko"; // neko.htmlをクライアントに返す
}
对于Form来说,应该创建一个新的对象,但是我懒散地直接使用了现成的Neko对象。
我会将传入的Form直接使用service的insertneko方法注册到数据库中。
确认动作
总结
我使用SpringBoot + Flyway + Mybatis实现了对数据库的访问。下一步,我计划实现表单验证功能。