Skip to content
Spring Boot sb databases 3 min read

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:shopdb names the in-memory database.
  • DB_CLOSE_DELAY=-1 keeps the database alive until the JVM exits, even if all connections close.
  • ddl-auto: create-drop recreates 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.

ModeURL prefixData survives restart?Best for
In-memoryjdbc:h2:mem:NoTests, ephemeral demos
Filejdbc:h2:file:YesLocal development
Server (TCP)jdbc:h2:tcp://YesSharing 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: false so 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-drop for in-memory and migrations (Flyway/Liquibase) once you move to a persistent database.
Last updated June 13, 2026
Was this helpful?