H2 In-Memory Database
H2 is a lightweight, pure-Java relational database that runs inside your application’s JVM. Because it requires no external server, it is the fastest way to get a working datasource for demos, prototypes, and integration tests. Spring Boot detects H2 on the classpath and auto-configures everything for you.
Why H2
When you just want to exercise persistence code without installing MySQL or PostgreSQL, H2 is ideal. It speaks standard SQL, supports a MySQL/PostgreSQL compatibility mode, and starts in milliseconds. The trade-off is that the default in-memory mode loses all data when the JVM stops, so reach for it during development and replace it with a real database in production.
Adding the dependency
H2 is a runtime dependency: your code compiles against JDBC and JPA, and the driver is only needed at runtime.
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
You also need a way to talk to it. The most common pairing is Spring Data JPA:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Tip: With just these two dependencies and no datasource properties at all, Spring Boot auto-configures an embedded in-memory H2 database named
testdb. That is enough to run a demo end to end.
In-memory mode
In-memory databases live only as long as the connection (or the JVM, with DB_CLOSE_DELAY). This is the default and the right choice for tests.
spring:
datasource:
url: jdbc:h2:mem:shopdb;DB_CLOSE_DELAY=-1
driver-class-name: org.h2.Driver
username: sa
password: ""
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
mem:shopdbnames the in-memory database.DB_CLOSE_DELAY=-1keeps the database alive until the JVM exits, even if all connections close.ddl-auto: create-droprecreates the schema on startup and drops it on shutdown, which matches an in-memory lifecycle.
File mode
If you want data to survive restarts during development, point the URL at a file path instead of mem:.
spring:
datasource:
url: jdbc:h2:file:./data/shopdb;AUTO_SERVER=TRUE
driver-class-name: org.h2.Driver
username: sa
password: ""
jpa:
hibernate:
ddl-auto: update
AUTO_SERVER=TRUE lets multiple processes (your app plus an external SQL tool) open the same file. Use ddl-auto: update so the schema persists across runs.
| Mode | URL prefix | Data survives restart? | Best for |
|---|---|---|---|
| In-memory | jdbc:h2:mem: | No | Tests, ephemeral demos |
| File | jdbc:h2:file: | Yes | Local development |
| Server (TCP) | jdbc:h2:tcp:// | Yes | Sharing a DB between apps |
The H2 console
H2 ships a browser-based SQL console. Spring Boot can mount it on a web endpoint so you can inspect tables and run ad-hoc queries while the app runs.
spring:
h2:
console:
enabled: true
path: /h2-console # default
settings:
web-allow-others: false
With spring.h2.console.enabled: true, start the app and open http://localhost:8080/h2-console. In the login form, set the JDBC URL to exactly the value of spring.datasource.url (for example jdbc:h2:mem:shopdb), user sa, and an empty password.
Warning: The H2 console is for development only. Never enable it in production, and keep
web-allow-others: falseso the console is not exposed to other hosts. If you use Spring Security, you must also permit the console path and relax frame options.
Seeding data
H2 automatically runs a schema.sql and data.sql on the classpath, or you can seed programmatically with a CommandLineRunner.
@Bean
CommandLineRunner seed(ProductRepository repo) {
return args -> {
repo.save(new Product("Keyboard", new BigDecimal("79.99")));
repo.save(new Product("Mouse", new BigDecimal("39.50")));
System.out.println("Seeded " + repo.count() + " products");
};
}
Output:
Hibernate: insert into products (id, name, price) values (default, ?, ?)
Hibernate: insert into products (id, name, price) values (default, ?, ?)
Seeded 2 products
H2 in tests
In tests you usually do not configure anything. A @DataJpaTest slice replaces the configured datasource with an embedded in-memory H2 instance automatically, giving every test a clean schema.
@DataJpaTest
class ProductRepositoryTest {
@Autowired
ProductRepository repo;
@Test
void savesAndFinds() {
repo.save(new Product("Pen", new BigDecimal("1.50")));
assertThat(repo.findByNameContainingIgnoreCase("pen")).hasSize(1);
}
}
Best Practices
- Use H2 only for development, demos, and tests; switch to a managed database for production.
- Match H2’s compatibility mode to your real database (
jdbc:h2:mem:db;MODE=PostgreSQL) so SQL behaves consistently. - Keep the console disabled by default and never expose it on a public interface.
- Set the JDBC URL in the console login screen to match your datasource URL exactly, or you will connect to a different empty database.
- Prefer
create-dropfor in-memory and migrations (Flyway/Liquibase) once you move to a persistent database.