Spring Boot + Mybatis + H2 database 数据库
2024年4月6日大约 5 分钟
整合 Spring Boot/Mybatis/H2 database 用来简单开发、测试。
todo mybatis 一级缓存、二级缓存
参考:
H2 介绍
H2 由纯 Java 编写的开源关系数据库,可以直接嵌入到应用程序中,不受平台约束,便于测试。同时提供了 web 界面用于管理数据库。
H2 支持运行三种模式
ServerMode(传统模式):需要配置本地(或远程)数据库
jdbc:h2:tcp://<server>[:<port>]/[<path>]<databaseName> jdbc:h2:tcp://localhost/~/test jdbc:h2:tcp://dbserv:8084/~/sample jdbc:h2:tcp://localhost/mem:test jdbc:h2:ssl://<server>[:<port>]/[<path>]<databaseName> jdbc:h2:ssl://localhost:8085/~/sample; jdbc:h2:tcp://localhost/~/test;AUTO_SERVER=TRUE # 是否允许混合模式,即同时支持多个网络连接
Embedded(嵌入式):数据库连接关闭时,数据与表结构依然存在
jdbc:h2:[file:][<path>]<databaseName> jdbc:h2:~/test # 示数据库存储在用户主目录中以 “test” 开头的文件中 jdbc:h2:file:/data/sample # 支持绝对位置 jdbc:h2:file:C:/data/sample (Windows only) jdbc:h2:file:/path/to/your/database;IFEXISTS=TRUE # 如果文件已经存在,则IFEXISTS=TRUE会避免尝试重新创建数据库而引发错误
In-Memory(内存模式):数据库连接关闭时,数据与表结构删除。多个线程可以访问同一个数据库,但是数据只能在同一个进程中可见。
jdbc:h2:mem: jdbc:h2:mem:<databaseName> jdbc:h2:mem:test_mem # 打开一个名为 “test_mem” 的内存数据库 jdbc:h2:tcp://localhost/mem:db1 jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 # 最后一个数据库连接关闭后,DB_CLOSE_DELAY=-1 让数据库不会被自动关闭,这样可保持数据在应用重启之间仍然可用 jdbc:h2:file:/data/mydb;CIPHER=AES # 数据库加密(如果需要的话)
相关信息
通过指定 url 方式指定运行模式。 更详细的 url 写法参考官网: https://www.h2database.com/html/features.html#database_url
整合
代码: https://github.com/LawssssCat/blog/tree/master/code/demo-springboot-h2/
项目初始化
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
配置
server:
port: 9088
spring:
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:test;mode=mysql #配置 h2 数据库的连接地址
# schema: # 过时
# data: # 过时
username: sa
password:
h2:
console:
enabled: true #开启 web console 功能。需要 spring-boot-starter-web 依赖
# path: /h2-console
sql:
init:
schema-locations: classpath:db/schema.sql #,每次启动程序,程序都会运行
data-locations: classpath:db/data.sql #,每次启动程序,程序都会运行
# mode: always # 在所有 bean 初始化完成后执行 SQL 脚本
mybatis:
mapper-locations: /mapper/*.xml
数据库操作
数据库结构
DROP TABLE IF EXISTS employees;
CREATE TABLE employees
(
id integer not null COMMENT '主键ID',
first_name varchar(255) not null comment '姓氏',
last_name varchar(255) null DEFAULT NULL COMMENT '名称',
age integer NULL DEFAULT 0 COMMENT '年龄',
email_address varchar(255) NULL DEFAULT NULL COMMENT '邮箱',
primary key(id)
);
CREATE TABLE events (
id int primary key COMMENT '主键',
param json COMMENT '数据'
)
数据库数据
DELETE FROM employees;
INSERT INTO employees (id, first_name, last_name, age, email_address) VALUES
(1, 'Jone', 'han', 18, 'test1@baomidou.com'),
(2, 'Jack', 'han', 18, 'test2@baomidou.com'),
(3, 'Tom', 'han', 18, 'test3@baomidou.com'),
(4, 'Sandy', 'han', 18, 'test4@baomidou.com'),
(5, 'Billie', 'han', 18, 'test5@baomidou.com');
insert into events (id, param) values
(1, JSON '{"name":"alpha", "type": ["a1", "a2"]}'),
(2, JSON '{"name":"beta", "type": ["b1", "b2"]}');
启动后通过 http://localhost:9088/h2-console 可以进入 web 管理界面。
数据库增删改查
模板类
package org.example.model;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Employee {
private long id;
private String firstName;
private String lastName;
private int age;
private String emailId;
}
数据库查询接口
package org.example.repository;
import org.apache.ibatis.annotations.*;
import org.example.model.Employee;
import java.util.List;
@Mapper
public interface EmployeeMapper {
@Select("select * from employees")
List<Employee> findAll();
@Select("SELECT * FROM employees WHERE id = #{id}")
Employee findById(long id);
@Delete("DELETE FROM employees WHERE id = #{id}")
int deleteById(long id);
@Insert("INSERT INTO employees(id, first_name, last_name, age, email_address) " +
" VALUES (#{id}, #{firstName}, #{lastName}, #{age}, #{emailId})")
int insert(Employee employee);
@Update("Update employees set first_name=#{firstName}, " +
" last_name=#{lastName}, age=#{age}, email_address=#{emailId} where id=#{id}")
int update(Employee employee);
}
执行增删改查
package org.example;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Slf4j
@SpringBootApplication
public class DemoH2SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(DemoH2SpringbootApplication.class, args);
}
}
collection 级联查询
demo 类
市
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Urban {
private int id; //市的编号
private String cityId; //省的编号(此博文没用到)
private String urbanName; //城市名字
private List<School> schools; //对应的所有的学校
private List<Hospital> hospitals; //对应的所有的医院
}
学校
@Data
@AllArgsConstructor
@NoArgsConstructor
public class School {
private int id; //学校编号
private int urbanId; //市的编号
private String schoolName; //学校名字
private Long people; //学校人数
}
医院
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Hospital {
private int id; //医院编号
private int urbanId; //市的编号
private String hospitalName; //医院名称
private Long people; //医院人数
}
级联查询
java 级联
市
在市的 xml 中对学校和医院的 xml 进行一个调用(用 collection 中 select)
<?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 namespace="com.yh.mybatis.dao.mapper.UrbanMapper">
<resultMap id="findAllUrbanSandH" type="com.yh.mybatis.dao.pojo.Urban">
<collection property="schools" javaType="java.util.List" ofType="com.yh.mybatis.dao.pojo.School"
select="com.yh.mybatis.dao.mapper.SchoolMapper.urbanSchool"
column="{urbanId=id}">
</collection>
<collection property="hospitals" javaType="java.util.List" ofType="com.yh.mybatis.dao.pojo.Hospital"
select="com.yh.mybatis.dao.mapper.HospitalMapper.findAllByUId"
column="{urbanId=id}">
</collection>
</resultMap>
<!--
resultMap中的 <id><result>都可以不写,直接写List<School>和List<Hospital>
type还是sql的返回类型
collection中 property 是Urban中对应的字段
javaType 是这个字段的类型
ofType 是这个字段的泛型 这一项和上一项其实都可以不写,写上了看着更清晰
select 是子表的按照市的编号查询所有数据的方法 这里要写下全路径
column 作为select语句的参数传入, 也就是把市的编号id 传给医院和学校xml的urbanId
-->
<select id="findAllUrbanSandH" resultMap="findAllUrbanSandH">
select * from urban
</select>
</mapper>
学校
<?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 namespace="com.yh.mybatis.dao.mapper.SchoolMapper">
<select id="urbanSchool" resultType="com.yh.mybatis.dao.pojo.School">
select * from school where urban_id = #{urbanId}
</select>
</mapper>
医院
<?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 namespace="com.yh.mybatis.dao.mapper.HospitalMapper">
<select id="findAllByUId" resultType="com.yh.mybatis.dao.pojo.Hospital">
select * from hospital where urban_id = #{urbanId}
</select>
<!--实际工作不建议用 *,id就是mapper接口中对应的方法名,resultType就是查询出结果后返回的list的泛型
urban_id = #{urbanId} 按照urban_id去查找-->
</mapper>
sql 级联
<?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 namespace="com.yh.mybatis.dao.mapper.UrbanMapper">
<resultMap id="findAllUrbanSandH2" type="com.yh.mybatis.dao.pojo.Urban">
<id property="id" column="id"/>
<result property="cityId" column="city_id"/>
<result property="urbanName" column="urban_name"/>
<!--这上面这几个字段就是urban表中,自带的那几个字段-->
<collection property="schools" javaType="java.util.List" ofType="com.yh.mybatis.dao.pojo.School">
<id property="id" column="sid"/>
<result property="urbanId" column="surban_id"/>
<result property="schoolName" column="school_name"/>
<result property="people" column="speople"/>
</collection>
<!--这上面就是school表中的字段
javaType是urban类中定义的school的类型 可以不写
ofType就是泛型,这个还是很有必要的,接下来的id result 就是这个类中定义的各种字段,要写全
如果涉及到的任何表中,在数据库中有重复的字段名,那就必须要起别名。(例如各个表中的id)
起别名直接在下面的sql中就可以。
-->
<collection property="hospitals" javaType="java.util.List" ofType="com.yh.mybatis.dao.pojo.Hospital">
<id property="id" column="hid"/>
<result property="urbanId" column="hurban_id"/>
<result property="hospitalName" column="hospital_name"/>
<result property="people" column="hpeople"/>
</collection>
</resultMap>
<select id="findAllUrbanSandH2" resultMap="findAllUrbanSandH2">
select urban.city_id
,urban.id
,urban.urban_name
,school.id sid
,school.urban_id surban_id
,school.school_name
,school.people speople
,hospital.id hid
,hospital.urban_id hurban_id
,hospital.hospital_name
,hospital.people hpeople
from urban
inner join school on urban.id = school.urban_id
inner join hospital on urban.id = hospital.urban_id
</select>
</mapper>