1. Overview
In this quick tutorial, we’ll demonstrate how to access the same in-memory H2 database from multiple Spring Boot applications.
To do this, we’ll create two distinct Spring Boot applications. The first Spring Boot application will start an in-memory H2 instance, whereas the second one will access an embedded H2 instance of the first application over TCP.
2. Background
As we know, an in-memory database is faster and often used in an embedded mode within an application. However, the in-memory database doesn’t persist data across server restarts.
For additional background, please check our articles on the most commonly used in-memory databases and the usage of an in-memory database in automated testing.
3. The Maven Dependencies
The two Spring Boot applications in this article require the same dependencies:
<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. Setting Up the H2 Datasource
Firstly, let’s define the most important component — a Spring bean for an in-memory H2 database — and expose it via a TCP port:
@Bean(initMethod = "start", destroyMethod = "stop")
public Server inMemoryH2DatabaseaServer() throws SQLException {
return Server.createTcpServer(
"-tcp", "-tcpAllowOthers", "-tcpPort", "9090");
}
The methods defined by the initMethod and destroyMethod parameters are called by Spring to start and stop H2 database.
The -tcp parameter instructs H2 to use a TCP server to launch H2. We specify the TCP port to be used in the third and fourth parameters of the createTcpServer method.
The parameter tcpAllowOthers opens up H2 for access from external applications running on the same host or remote hosts.
Next, let’s override the default data source created by Spring Boot’s auto-configuration feature by adding a few properties to the application.properties file:
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
It’s important to override these properties because we’ll need to use the same properties and values in the other applications that want to share the same H2 database.
5. Bootstrapping the First Spring Boot Application
Next, to bootstrap our Spring Boot application, we’ll create a class with the @SpringBootApplication annotation:
@SpringBootApplication
public class SpringBootApp {
public static void main(String[] args) {
SpringApplication.run(SpringBootApp.class, args);
}
}
To test that everything is wired properly, let’s add code to create some test data.
We’ll define a method named initDb and annotate it with @PostConstruct so that the Spring container automatically calls this method as soon as the main class is initialized:
@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);
});
// Query test data and print results
}
6. The Second Spring Boot Application
Now let’s look at the components of the client application, which requires the same Maven dependencies as defined above.
First, we’ll override the data source properties. We need to ensure that the port number in the JDBC URL is the same as the one on which H2 is listening for incoming connections in the first application.
Here’s the application.properties file of the client application:
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
Lastly, we create a main class of the client Spring Boot application.
Again for simplicity, we define a @SpringBootApplication containing an initDb method with the @PostConstruct annotation:
@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);
});
// Fetch data using SELECT statement and print results
}
}
7. Sample Output
Now, when we run both the applications one by one, we can check the console logs and confirm that the second application prints the data as expected.
Here are the console logs of the first Spring Boot application:
****** 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
And here are the console logs of the second Spring Boot application:
****** 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. Conclusion
In this quick article, we saw how we can access the same in-memory H2 database instance from multiple Spring Boot applications.
As always, working code examples are available over on GitHub.