Spring Boot
embedded database
error handling
database table
troubleshooting

Spring embeddeb db table already exists error

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

Introduction

A "table already exists" error in a Spring application with an embedded database usually means the schema is being initialized more than once. That can happen because schema.sql runs repeatedly, Hibernate is also creating tables, or the database is not as ephemeral as you expected. The fix is usually not complicated, but you need to identify which component is managing schema creation and make that ownership unambiguous.

Understand the Usual Causes

In a typical Spring Boot setup with H2, HSQLDB, or Derby, schema creation may come from more than one place:

  • 'schema.sql'
  • Hibernate DDL generation such as create or update
  • migration tools such as Flyway or Liquibase
  • a persistent file-based embedded database reused across runs

If more than one of these mechanisms tries to create the same table, startup fails with a "table already exists" error.

Pick One Schema Management Strategy

The cleanest rule is simple: one mechanism should own schema creation.

If you use raw SQL scripts:

properties
spring.jpa.hibernate.ddl-auto=none

Then put the schema in schema.sql.

sql
1CREATE TABLE IF NOT EXISTS users (
2    id BIGINT PRIMARY KEY,
3    username VARCHAR(100) NOT NULL
4);

If you want Hibernate to generate the schema during development instead, avoid duplicating the same table creation in schema.sql.

Watch Out for File-Based Embedded Databases

Developers often assume an embedded database is always recreated on each restart. That is true for purely in-memory URLs, but not for file-based ones.

For example, this H2 configuration persists between runs:

properties
spring.datasource.url=jdbc:h2:file:./data/demo

If your app creates the schema on the first run and then re-runs schema.sql on the next startup, the table already exists and the second startup fails.

If you really want a fresh database each run for development or tests, use an in-memory database URL instead.

properties
spring.datasource.url=jdbc:h2:mem:testdb

Keep Tests Isolated

This error shows up frequently in tests when the same context or file-backed database is reused across test methods.

A good test setup either:

  • uses a fresh in-memory database per test context
  • cleans the database before the next test
  • uses idempotent schema creation such as IF NOT EXISTS

For test SQL initialization, idempotent DDL is often the quickest fix.

Flyway and Liquibase Change the Rule

If you use Flyway or Liquibase, those tools should own schema evolution. In that case, avoid having Hibernate or raw startup scripts trying to create the same objects.

For example, with Flyway you would typically:

properties
spring.jpa.hibernate.ddl-auto=validate

and let Flyway migrations define the schema.

That way Hibernate checks the schema instead of building it.

Debugging Checklist

When this error appears, check these in order:

  • is schema.sql creating the table
  • is Hibernate also set to create or update tables
  • is a migration tool managing the same schema
  • is the embedded database file reused across restarts

Once you know which of those are active, the conflict is usually obvious.

Common Pitfalls

The most common mistake is allowing both schema.sql and Hibernate DDL auto-generation to create the same table.

Another issue is assuming an embedded database is always temporary, while the actual JDBC URL points to a file-backed database that persists between runs.

Developers also often fix the symptom with IF NOT EXISTS everywhere without deciding which tool should really own schema evolution. That may hide the duplication instead of simplifying it.

Finally, if Flyway or Liquibase is in use, do not let ad hoc startup scripts compete with your migration history.

Summary

  • A "table already exists" error usually means schema creation is happening from more than one place.
  • Choose one clear owner for schema creation: SQL scripts, Hibernate, or migrations.
  • Distinguish between in-memory embedded databases and file-backed ones.
  • Keep tests isolated so initialization does not collide between runs.
  • Use idempotent DDL carefully, but solve the ownership conflict instead of only masking it.

Related reading
Course
Beginner
27 lessons
10 hours
System Design Fundamentals

Build a strong foundation in designing scalable, reliable distributed systems.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

System Design practice on Codemia

Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.

Practice system design

All Rights Reserved.