How to Fix Spring Boot JPA Table Not Created Automatically

You’ve configured your DataSource, your app starts cleanly, and you’ve annotated your class with @Entity. But when you check the database — the table simply isn’t there. No error, no warning in the logs, just a missing table. This is one of the most silent failures in Spring Boot JPA setup.

What this typically looks like:

  • App starts successfully with no exceptions
  • Your @Entity class is written correctly (or so you think)
  • You query the database and the table doesn’t exist
  • At runtime you get Table 'mydb.user' doesn't exist

Hibernate can create tables for you automatically — but only when it’s explicitly told to, and only when all the pieces are in place. Here’s every reason it silently refuses, and how to fix each one.

Why JPA Isn’t Creating Your Tables

1. ddl-auto Is Set to None or Validate

The most common cause. Spring Boot’s default value for spring.jpa.hibernate.ddl-auto depends on the database you’re using. With embedded databases like H2, it defaults to create-drop. With a real database like MySQL or PostgreSQL, it defaults to none — meaning Hibernate does absolutely nothing to your schema. If you never set this property, your tables will never be created.

2. The Entity Class Is Missing @Entity or Is in the Wrong Package

Without @Entity, Hibernate doesn’t know the class is a managed entity. Similarly, if the entity class is outside the package that Spring scans (or not scanned by @EntityScan), Hibernate never sees it.

3. Missing @Id Annotation

Every JPA entity must have exactly one field annotated with @Id. Without it, Hibernate will refuse to map the class to a table — and it often does so silently.

4. Wrong or Missing Dialect

If Hibernate can’t determine the correct SQL dialect for your database, it may not generate valid DDL at all. This is particularly common when switching between database types or after a Spring Boot version upgrade.

5. Schema or Database Doesn’t Exist

Hibernate can create tables inside an existing database, but it won’t create the database itself. If the schema referenced in your spring.datasource.url doesn’t exist yet, table creation silently fails.

How to Fix It — Step by Step

Fix 1: Set ddl-auto to update or create

This is the most important property. Add it to your config file — choose your format:

application.properties:

# For development — creates tables if they don't exist, updates if they do
spring.jpa.hibernate.ddl-auto=update

# For fresh start — drops and recreates all tables on every startup
# WARNING: use only in development, never in production
spring.jpa.hibernate.ddl-auto=create

# For production — validates schema matches entities, throws error if not
spring.jpa.hibernate.ddl-auto=validate

# For production — does nothing, manage schema yourself (Flyway/Liquibase)
spring.jpa.hibernate.ddl-auto=none

application.yml:

spring:
  jpa:
    hibernate:
      ddl-auto: update   # swap with: create | validate | none

For most development setups, update is the right choice — it creates missing tables and columns without dropping existing data.

Fix 2: Annotate Your Entity Correctly

Every JPA entity needs at minimum three things: @Entity, a class-level identifier for the table (optional but recommended), and @Id on the primary key field.

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Entity
@Table(name = "users")  // optional — defaults to the class name if omitted
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    // constructors, getters, setters
}

⚠️ Note for Spring Boot 3.x users: The import is jakarta.persistence.Entity, not javax.persistence.Entity. Using the old import will silently fail — the class compiles fine but Hibernate doesn’t recognize it as an entity.

Fix 3: Check Package Scanning With @EntityScan

If your entity classes live in a different package from your @SpringBootApplication class, Spring won’t scan them automatically. Fix this with @EntityScan:

@SpringBootApplication
@EntityScan(basePackages = "com.example.demo.entities")
@EnableJpaRepositories(basePackages = "com.example.demo.repositories")
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

Fix 4: Set the Correct Hibernate Dialect

Add the dialect explicitly to avoid ambiguity:

application.properties:

# MySQL 8+
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect

# PostgreSQL
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

# H2
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

application.yml:

# MySQL 8+
spring:
  jpa:
    database-platform: org.hibernate.dialect.MySQLDialect

# PostgreSQL
spring:
  jpa:
    database-platform: org.hibernate.dialect.PostgreSQLDialect

# H2
spring:
  jpa:
    database-platform: org.hibernate.dialect.H2Dialect

Fix 5: Add show-sql to See What Hibernate Is (or Isn’t) Generating

Turn on SQL logging to see exactly what DDL Hibernate sends to the database — or to confirm that it’s sending nothing at all:

application.properties:

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.orm.jdbc.bind=TRACE

application.yml:

spring:
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true

logging:
  level:
    org.hibernate.SQL: DEBUG
    org.hibernate.orm.jdbc.bind: TRACE

On startup, if ddl-auto=update is working, you should see CREATE TABLE or ALTER TABLE statements in the log. If you see nothing after setting show-sql=true, the entity is not being picked up at all — go back to Fix 2 and Fix 3.

Recommended Profile-Based DDL Setup

A clean production-grade approach is to use different ddl-auto values per environment using Spring profiles:

Using .properties files:

# application-dev.properties
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# application-prod.properties
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=false

Using .yml files:

# application-dev.yml
spring:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

# application-prod.yml
spring:
  jpa:
    hibernate:
      ddl-auto: none
    show-sql: false

In production, use Flyway or Liquibase for schema management instead of relying on Hibernate’s DDL generation — it gives you full control over migrations, rollback, and version history.

DDL-Auto Values — Quick Reference

ValueWhat Hibernate DoesUse In
noneDoes nothing — your responsibilityProduction (with Flyway/Liquibase)
validateChecks schema matches entities, throws if notProduction (schema already exists)
updateCreates missing tables/columns, never dropsDevelopment
createDrops and recreates all tables on startupDevelopment only ⚠️
create-dropCreates on startup, drops on shutdownTests only ⚠️

Conclusion

When JPA tables aren’t being created, the issue is almost always one of three things: ddl-auto is not set (or set to none), the entity annotation is missing or using the wrong import (javax vs jakarta), or the entity is in a package that Spring isn’t scanning. Enable show-sql=true as your first debugging step — if you see DDL in the logs, Hibernate is working and the issue is elsewhere. If you see nothing, it’s a scanning or annotation problem.

Leave a Comment

Your email address will not be published. Required fields are marked *