Spring Boot with PostgreSQL
PostgreSQL is the most popular production database for Spring Boot. This chapter covers Docker setup, configuration, JSONB, and connection pooling.
PostgreSQL with Docker
docker run -d \
--name bookstore-postgres \
-e POSTGRES_DB=bookstore \
-e POSTGRES_USER=admin \
-e POSTGRES_PASSWORD=secret \
-p 5432:5432 \
postgres:16
Or with Docker Compose:
# docker-compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: bookstore
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret
ports:
- "5432:5432"
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
docker compose up -d
Spring Boot Configuration
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
spring:
datasource:
url: jdbc:postgresql://localhost:5432/bookstore
username: admin
password: secret
driver-class-name: org.postgresql.Driver
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 300000
connection-timeout: 20000
max-lifetime: 1200000
jpa:
hibernate:
ddl-auto: validate
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
format_sql: true
show-sql: true
HikariCP Connection Pool
Spring Boot uses HikariCP by default — the fastest Java connection pool:
spring:
datasource:
hikari:
maximum-pool-size: 20 # max connections
minimum-idle: 5 # min idle connections
idle-timeout: 300000 # 5 min — close idle connections
connection-timeout: 20000 # 20 sec — wait for connection
max-lifetime: 1200000 # 20 min — max connection age
pool-name: BookstorePool
Rule of thumb:
pool-size = (core_count * 2) + effective_spindle_count. For most apps, 10–20 connections is sufficient.
PostgreSQL JSONB Columns
@Entity
@Table(name = "books")
public class Book {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
@Column(columnDefinition = "jsonb")
@JdbcTypeCode(SqlTypes.JSON)
private Map<String, Object> metadata;
}
Migration:
ALTER TABLE books ADD COLUMN metadata JSONB DEFAULT '{}';
CREATE INDEX idx_books_metadata ON books USING GIN (metadata);
Query JSONB:
@Query(value = "SELECT * FROM books WHERE metadata->>'language' = :lang", nativeQuery = true)
List<Book> findByMetadataLanguage(@Param("lang") String language);
Profile-Based DB Config
# application-dev.yml — H2 for development
spring:
datasource:
url: jdbc:h2:mem:devdb
jpa:
hibernate:
ddl-auto: create-drop
# application-prod.yml — PostgreSQL for production
spring:
datasource:
url: ${DATABASE_URL}
username: ${DATABASE_USERNAME}
password: ${DATABASE_PASSWORD}
jpa:
hibernate:
ddl-auto: validate
Key Takeaways
- Use Docker for local PostgreSQL — reproducible, disposable.
- HikariCP is auto-configured — tune pool size for your workload.
- PostgreSQL JSONB stores semi-structured data with indexing.
- Use profiles — H2 for dev, PostgreSQL for prod.
- Never hardcode credentials — use environment variables.
What’s Next?
Next: Spring Boot with MySQL — differences from PostgreSQL setup.