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
@Entityclass 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
| Value | What Hibernate Does | Use In |
|---|---|---|
none | Does nothing — your responsibility | Production (with Flyway/Liquibase) |
validate | Checks schema matches entities, throws if not | Production (schema already exists) |
update | Creates missing tables/columns, never drops | Development |
create | Drops and recreates all tables on startup | Development only ⚠️ |
create-drop | Creates on startup, drops on shutdown | Tests 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.
