1. 概述
在本教程中,我们将演示如何让多个 Spring Boot 应用共享同一个内存 H2 数据库实例。
实现思路是:创建两个独立的 Spring Boot 应用,第一个应用启动一个内存 H2 实例,并通过 TCP 暴露出来;第二个应用则通过 TCP 连接到第一个应用所暴露的 H2 实例。
2. 背景知识
我们知道,内存数据库速度快,通常以嵌入模式运行在应用内部。但它的缺点也很明显 —— 重启后数据不会保留。
如果你对内存数据库感兴趣,可以参考我们之前的文章:
3. Maven 依赖配置
两个 Spring Boot 应用所需的依赖完全一致:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
4. 配置 H2 数据源
首先,我们要定义一个关键组件 —— 一个可暴露为 TCP 服务的 H2 内存数据库 Bean:
@Bean(initMethod = "start", destroyMethod = "stop")
public Server inMemoryH2DatabaseaServer() throws SQLException {
return Server.createTcpServer(
"-tcp", "-tcpAllowOthers", "-tcpPort", "9090");
}
initMethod = "start"
和destroyMethod = "stop"
是告诉 Spring 在容器启动和关闭时自动调用 H2 Server 的开启和关闭方法。-tcp
表示启用 TCP 服务器。-tcpAllowOthers
允许其他主机访问这个数据库(⚠️注意安全问题)。-tcpPort 9090
设置监听端口为 9090。
接下来,在 application.properties
文件中覆盖默认的数据源配置:
spring.datasource.url=jdbc:h2:mem:mydb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
✅ 重点提示:这些配置项在所有需要连接该 H2 实例的应用中必须保持一致!
5. 启动第一个 Spring Boot 应用
我们创建一个主类并加上 @SpringBootApplication
注解:
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
为了验证数据库是否正常工作,我们添加一些初始化数据逻辑。使用 @PostConstruct
标记的方法会在 Spring 初始化完成后自动执行:
@PostConstruct
private void initDb() {
String sqlStatements[] = {
"drop table employees if exists",
"create table employees(id serial,first_name varchar(255),last_name varchar(255))",
"insert into employees(first_name, last_name) values('Eugen','Paraschiv')",
"insert into employees(first_name, last_name) values('Scott','Tiger')"
};
Arrays.asList(sqlStatements).forEach(sql -> {
jdbcTemplate.execute(sql);
});
// 查询并打印结果
}
6. 第二个 Spring Boot 客户端应用
第二个应用也需要相同的 Maven 依赖。
关键在于修改数据源配置,确保 JDBC URL 中的端口号与第一个应用中设置的 TCP 端口一致:
spring.datasource.url=jdbc:h2:tcp://localhost:9090/mem:mydb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.hibernate.ddl-auto=create
然后创建客户端主类:
@SpringBootApplication
public class ClientSpringBootApp {
public static void main(String[] args) {
SpringApplication.run(ClientSpringBootApp.class, args);
}
@PostConstruct
private void initDb() {
String sqlStatements[] = {
"insert into employees(first_name, last_name) values('Donald','Trump')",
"insert into employees(first_name, last_name) values('Barack','Obama')"
};
Arrays.asList(sqlStatements).forEach(sql -> {
jdbcTemplate.execute(sql);
});
// 使用 SELECT 查询并打印结果
}
}
7. 示例输出
分别运行两个应用后,我们可以看到控制台日志如下:
第一个应用输出:
****** Creating table: Employees, and Inserting test data ******
drop table employees if exists
create table employees(id serial,first_name varchar(255),last_name varchar(255))
insert into employees(first_name, last_name) values('Eugen','Paraschiv')
insert into employees(first_name, last_name) values('Scott','Tiger')
****** Fetching from table: Employees ******
id:1,first_name:Eugen,last_name:Paraschiv
id:2,first_name:Scott,last_name:Tiger
第二个应用输出:
****** Inserting more test data in the table: Employees ******
insert into employees(first_name, last_name) values('Donald','Trump')
insert into employees(first_name, last_name) values('Barack','Obama')
****** Fetching from table: Employees ******
id:1,first_name:Eugen,last_name:Paraschiv
id:2,first_name:Scott,last_name:Tiger
id:3,first_name:Donald,last_name:Trump
id:4,first_name:Barack,last_name:Obama
可以看到,第二个应用不仅能访问到第一个应用插入的数据,还能继续插入自己的数据。
8. 总结
在这篇文章中,我们展示了如何通过 TCP 方式让多个 Spring Boot 应用共享同一个内存 H2 数据库实例。
这种方式适用于开发阶段快速验证多应用间的数据交互,或者用于构建集成测试环境。
✅ 踩坑提醒:
- 确保两个应用使用的数据库名称、用户名、密码一致;
- 注意防火墙和权限设置,避免外部非法访问;
- 生产环境慎用,仅限本地调试或测试环境使用。
完整代码示例可在 GitHub 获取。