Skip to main content

Spring Boot Database Configuration: What Happens When Your App Starts

Β· 11 min read
Mahmut Salman
Software Developer

Ever wondered what those spring.datasource.* and spring.jpa.* properties actually do when your Spring Boot app starts? Let's trace the complete journey from configuration file to running database, step by step.

The Configuration File Pattern​

Open your application.properties and notice the pattern:

# Database Connection Settings (lines 11-15)
spring.datasource.url=jdbc:h2:file:./data/ecommerce_db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# JPA/Hibernate Behavior Settings (lines 16-19)
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

Notice the pattern:

PrefixPurposeControls
spring.datasource.*Database ConnectionHow to connect to the database
spring.jpa.*JPA/Hibernate BehaviorHow to manage entities and schema

Think of it like:

  • datasource.* β†’ The phone number to call the database
  • jpa.* β†’ What to say when you're connected

What Happens When You Start the App​

Let's trace exactly what happens behind the scenes when you run your Spring Boot application:

Step-by-Step Startup Sequence​

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 1. Spring Boot Starts β”‚
β”‚ β†’ java -jar myapp.jar β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 2. Reads application.properties β”‚
β”‚ β†’ Loads all spring.* properties β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 3. Sees H2 Configuration β”‚
β”‚ β†’ spring.datasource.url detected β”‚
β”‚ β†’ Initializes database connection β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 4. Creates data/ Folder β”‚
β”‚ β†’ if (!exists) mkdir data/ β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 5. Creates/Opens Database File β”‚
β”‚ β†’ data/ecommerce_db.mv.db β”‚
β”‚ β†’ data/ecommerce_db.trace.db β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 6. Connects Using Credentials β”‚
β”‚ β†’ Username: "sa" β”‚
β”‚ β†’ Password: "" (empty) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 7. Hibernate Scans for @Entity Classes β”‚
β”‚ β†’ Finds all classes with @Entity β”‚
β”‚ β†’ Reads their field definitions β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 8. Updates Database Schema β”‚
β”‚ β†’ Creates missing tables β”‚
β”‚ β†’ Adds missing columns β”‚
β”‚ β†’ (ddl-auto=update) β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
↓
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ 9. App is Ready! πŸŽ‰ β”‚
β”‚ β†’ Database connected β”‚
β”‚ β†’ Schema synchronized β”‚
β”‚ β†’ Ready to handle requests β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What Each Step Does in Detail​

Step 1: Spring Boot Starts​

$ mvn spring-boot:run
# or
$ java -jar backend/target/myapp.jar

Console output:

  .   ____          _            __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)

Step 2: Reads application.properties​

// Spring Boot internally does this:
Properties props = new Properties();
props.load(new FileInputStream("application.properties"));

String dbUrl = props.getProperty("spring.datasource.url");
String driver = props.getProperty("spring.datasource.driverClassName");
String ddlAuto = props.getProperty("spring.jpa.hibernate.ddl-auto");

Step 3: Sees H2 Configuration​

spring.datasource.url=jdbc:h2:file:./data/ecommerce_db
↑ ↑
protocol path

Spring Boot thinks:

  • "Oh, it's an H2 database (jdbc:h2:)"
  • "File-based storage (:file:)"
  • "Store in ./data/ecommerce_db"

Step 4: Creates data/ Folder​

# If data/ doesn't exist, H2 creates it
backend/
β”œβ”€β”€ src/
β”œβ”€β”€ target/
β”œβ”€β”€ pom.xml
└── data/ ← Created automatically!
└── (empty for now)

Step 5: Creates/Opens Database File​

data/
β”œβ”€β”€ ecommerce_db.mv.db ← Main database file
└── ecommerce_db.trace.db ← Debug trace file (optional)

File details:

  • .mv.db = "Multi-Version Database" (H2's format)
  • Binary file containing all your tables and data
  • Grows as you add data

Step 6: Connects Using Credentials​

// Spring Boot establishes connection:
Connection conn = DriverManager.getConnection(
"jdbc:h2:file:./data/ecommerce_db", // URL
"sa", // Username
"" // Password (empty)
);

Console output:

2024-10-18 10:30:15.123  INFO 12345 --- [main] com.zaxxer.hikari.HikariDataSource
: HikariPool-1 - Starting...
2024-10-18 10:30:15.456 INFO 12345 --- [main] com.zaxxer.hikari.HikariDataSource
: HikariPool-1 - Start completed.

Step 7: Hibernate Scans for @Entity Classes​

Your code:

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private Double price;
}

@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;
private String email;
}

Hibernate scans classpath:

Found @Entity: Product
Fields: id (Long), name (String), price (Double)

Found @Entity: User
Fields: id (Long), username (String), email (String)

Step 8: Updates Database Schema​

Because ddl-auto=update, Hibernate runs:

-- First time (tables don't exist)
CREATE TABLE product (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
price DOUBLE
);

CREATE TABLE user (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255),
email VARCHAR(255)
);

Console output (if show-sql=true):

Hibernate:
create table product (
id bigint generated by default as identity,
name varchar(255),
price double,
primary key (id)
)

Hibernate:
create table user (
id bigint generated by default as identity,
email varchar(255),
username varchar(255),
primary key (id)
)

Step 9: App is Ready!​

Console output:

2024-10-18 10:30:16.789  INFO 12345 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer
: Tomcat started on port(s): 8080 (http) with context path ''
2024-10-18 10:30:16.800 INFO 12345 --- [main] c.example.myapp.MyApplication
: Started MyApplication in 2.345 seconds (JVM running for 3.456)

Understanding the Two Configuration Sections​

Section 1: spring.datasource.* (Connection Settings)​

spring.datasource.url=jdbc:h2:file:./data/ecommerce_db
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

What each line does:

PropertyValuePurpose
urljdbc:h2:file:./data/ecommerce_dbWhere the database file is located
driverClassNameorg.h2.DriverWhich translator to use (Java ↔ H2)
usernamesaLogin username (default for H2)
password""Login password (empty = no password)

The Driver: Your Translator

Your Java Code: repository.findAll()
↓
H2 Driver (org.h2.Driver) translates to:
↓
SELECT * FROM product;
↓
Database File: data/ecommerce_db.mv.db

Think of it like:

You speak English (Java) Database speaks Binary (H2 protocol) Driver translates between them

Section 2: spring.jpa.* (Hibernate Behavior)​

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

What each line does:

PropertyValuePurpose
ddl-autoupdateAutomatically update schema to match Java classes
show-sqltruePrint SQL statements to console
format_sqltrueMake SQL pretty (multiline, indented)

The ddl-auto Options:

OptionWhat HappensData SafetyUse Case
createDrop all tables, recreateπŸ”₯ DATA LOST!Never in production
create-dropCreate on start, drop on shutdownπŸ”₯ DATA LOST!Integration tests
updateAdd missing tables/columnsβœ… SAFEDevelopment
validateJust check if schema matchesβœ… SafeProduction
noneDo nothingβœ… SafeManual control

Key Concepts Explained​

Concept 1: File-Based Database​

spring.datasource.url=jdbc:h2:file:./data/ecommerce_db
↑ ↑
type path

What file: means:

File-Based Database:
./data/ecommerce_db β†’ Relative to backend/ folder
β†’ Data persists across restarts
β†’ Can back up the file
β†’ Can copy to another machine

Contrast with in-memory:

# In-memory (data lost on restart)
spring.datasource.url=jdbc:h2:mem:testdb
↑
memory

File structure:

backend/
β”œβ”€β”€ data/
β”‚ β”œβ”€β”€ ecommerce_db.mv.db # Your data lives here!
β”‚ └── ecommerce_db.trace.db # Debug logs
β”œβ”€β”€ src/
└── pom.xml

What's in the .mv.db file?

  • All your tables (product, user, order, etc.)
  • All your data (every row you inserted)
  • Indexes, constraints, sequences
  • Everything needed to restore your database

Benefits:

  • βœ… Data persists across restarts
  • βœ… Can back up with simple file copy
  • βœ… Can version control (small files)
  • βœ… Easy to reset (just delete the file)

Concept 2: Auto Schema Management​

You write Java classes β†’ Hibernate creates tables
You add a field β†’ Hibernate adds a column
No manual SQL needed! ✨

Example workflow:

Day 1: Create Product Entity​

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private Double price;
}

Start app β†’ Hibernate runs:

CREATE TABLE product (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
price DOUBLE
);

Day 2: Add Description Field​

@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private Double price;

private String description; // πŸ†• NEW!
}

Start app β†’ Hibernate runs:

ALTER TABLE product ADD COLUMN description VARCHAR(255);

Your existing products? βœ… Still there! New column is NULL for existing rows.

Day 3: Add Category Entity​

@Entity
public class Category {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
}

Start app β†’ Hibernate runs:

CREATE TABLE category (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255)
);

Magic! ✨ No manual SQL needed!


Real-World Example: E-Commerce App Startup​

Let's see what happens when you start a real e-commerce application:

Your Java Entities​

// Product.java
@Entity
public class Product {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Double price;
private Integer stock;
}

// User.java
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String email;
private String password;
}

// Order.java
@Entity
@Table(name = "orders") // "order" is a reserved keyword
public class Order {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private LocalDateTime orderDate;
private String status;

@ManyToOne
private User user;
}

Complete Console Output​

$ mvn spring-boot:run

. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)

2024-10-18 10:30:15.123 INFO 12345 --- [main] c.example.ecommerce.EcommerceApplication
: Starting EcommerceApplication using Java 17

2024-10-18 10:30:15.456 INFO 12345 --- [main] com.zaxxer.hikari.HikariDataSource
: HikariPool-1 - Starting...
2024-10-18 10:30:15.789 INFO 12345 --- [main] com.zaxxer.hikari.HikariDataSource
: HikariPool-1 - Start completed.

Hibernate:
create table orders (
id bigint generated by default as identity,
order_date timestamp,
status varchar(255),
user_id bigint,
primary key (id)
)

Hibernate:
create table product (
id bigint generated by default as identity,
name varchar(255),
price double,
stock integer,
primary key (id)
)

Hibernate:
create table user (
id bigint generated by default as identity,
email varchar(255),
password varchar(255),
username varchar(255),
primary key (id)
)

Hibernate:
alter table if exists orders
add constraint FK_order_user
foreign key (user_id)
references user

2024-10-18 10:30:16.234 INFO 12345 --- [main] o.h.e.t.j.p.i.JtaPlatformInitiator
: HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]

2024-10-18 10:30:16.567 INFO 12345 --- [main] o.s.b.w.embedded.tomcat.TomcatWebServer
: Tomcat started on port(s): 8080 (http)

2024-10-18 10:30:16.890 INFO 12345 --- [main] c.example.ecommerce.EcommerceApplication
: Started EcommerceApplication in 2.345 seconds

βœ… Database: Connected to ./data/ecommerce_db.mv.db
βœ… Schema: 3 tables created
βœ… Server: Running on http://localhost:8080

The Created Database Structure​

-- You can verify with H2 Console: http://localhost:8080/h2-console

SHOW TABLES;
-- Result:
-- ORDERS
-- PRODUCT
-- USER

DESCRIBE product;
-- Result:
-- ID BIGINT PRIMARY KEY
-- NAME VARCHAR(255)
-- PRICE DOUBLE
-- STOCK INTEGER

DESCRIBE user;
-- Result:
-- ID BIGINT PRIMARY KEY
-- EMAIL VARCHAR(255)
-- PASSWORD VARCHAR(255)
-- USERNAME VARCHAR(255)

DESCRIBE orders;
-- Result:
-- ID BIGINT PRIMARY KEY
-- ORDER_DATE TIMESTAMP
-- STATUS VARCHAR(255)
-- USER_ID BIGINT FOREIGN KEY β†’ user(id)

Understanding the Data Flow​

From Java Class to Database Table​

@Entity
public class Product {
@Id
private Long id;
private String name;
private Double price;
}
↓
Hibernate reads annotations
↓
Generates DDL
↓
CREATE TABLE product (
id BIGINT PRIMARY KEY,
name VARCHAR(255),
price DOUBLE
);
↓
Stores in data/ecommerce_db.mv.db

From Repository Call to SQL​

// Your code
List<Product> products = productRepository.findAll();
↓
Spring Data JPA
↓
Hibernate
↓
H2 Driver (org.h2.Driver)
↓
SELECT * FROM product;
↓
data/ecommerce_db.mv.db
↓
Results mapped back to Java objects
↓
List<Product> [Product{id=1, name="Laptop", price=999.99}, ...]

Best Practices​

βœ… Do This​

1. Use different configurations for different environments

# application-dev.properties (Development)
spring.datasource.url=jdbc:h2:file:./data/dev_db
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

# application-prod.properties (Production)
spring.datasource.url=jdbc:postgresql://prod-server:5432/mydb
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false

2. Enable H2 console for debugging (dev only)

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

3. Use descriptive database names

# βœ… Good
spring.datasource.url=jdbc:h2:file:./data/ecommerce_db

# ❌ Bad
spring.datasource.url=jdbc:h2:file:./data/db

4. Format SQL for readability during development

spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true

❌ Avoid This​

1. Never use create in production

# ❌ DANGER: Deletes all data on restart!
spring.jpa.hibernate.ddl-auto=create

2. Don't commit database files to Git

# .gitignore
data/
*.mv.db
*.trace.db

3. Don't use empty passwords in production

# ❌ Development only
spring.datasource.password=

# βœ… Production
spring.datasource.password=${DB_PASSWORD}

Troubleshooting Common Issues​

Issue 1: Database File Not Created​

Problem:

App starts, but no data/ecommerce_db.mv.db file appears

Check:

# Is the URL correct?
spring.datasource.url=jdbc:h2:file:./data/ecommerce_db
↑
Must be "file:" not "mem:"

Solution:

# Check if file exists
ls -la data/

# Check Spring Boot logs for errors
tail -f logs/spring.log

Issue 2: Schema Not Updated​

Problem:

Added new field to @Entity, but column doesn't appear in database

Check:

# Is ddl-auto set correctly?
spring.jpa.hibernate.ddl-auto=update ← Must be "update"

Solution:

# Restart the app
mvn spring-boot:run

# Check console for Hibernate DDL
# Should see: ALTER TABLE ... ADD COLUMN ...

Issue 3: Connection Refused​

Problem:

org.h2.jdbc.JdbcSQLNonTransientConnectionException: Database may be already in use

Cause: Another instance of the app is running

Solution:

# Stop other instances
ps aux | grep java

# Or use a different database file
spring.datasource.url=jdbc:h2:file:./data/ecommerce_db_v2

Summary​

What Happens at Startup (Quick Version)​

1. Read application.properties
2. Load database driver (org.h2.Driver)
3. Create/open database file (data/ecommerce_db.mv.db)
4. Connect with credentials (sa / "")
5. Scan for @Entity classes
6. Update schema (ddl-auto=update)
7. Ready to serve requests! πŸŽ‰

The Two Configuration Patterns​

# How to connect
spring.datasource.*

# What to do when connected
spring.jpa.*

Key Concepts​

  1. File-Based Database

    • ./data/ecommerce_db β†’ Relative path
    • Data persists across restarts
    • Can back up with file copy
  2. Auto Schema Management

    • Write Java classes β†’ Hibernate creates tables
    • Add fields β†’ Hibernate adds columns
    • No manual SQL needed! ✨
  3. The Driver (Translator)

    • Converts Java β†’ Database protocol
    • org.h2.Driver for H2
    • Comes from Maven dependency

Mental Models​

Database Connection:

Java Code β†’ Driver (Translator) β†’ Database File

Schema Management:

@Entity Class β†’ Hibernate β†’ CREATE/ALTER TABLE β†’ Database

Next time you start your Spring Boot app, you'll know exactly what's happening behind the scenes! πŸš€

Tags: #spring-boot #jdbc #database #h2 #jpa #hibernate #configuration