PostgreSQL
Fundamentals of PostgreSQL Database in Spring Boot Applications
By Utility Zone · 2025-11-02T13:01:41.320315
Overview and Setup
PostgreSQL is a powerful, open-source relational database that integrates seamlessly with Spring Boot applications through JDBC connectivity and ORM frameworks like Hibernate. The integration leverages Spring Boot's auto-configuration capabilities to minimize boilerplate code while providing robust database interaction mechanisms.
Step 1: Adding Required Dependencies
To connect a Spring Boot application to PostgreSQL, include the PostgreSQL JDBC driver in your project's pom.xml (for Maven):1
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.6.0</version>
</dependency>
For Spring Data JPA integration, add:2
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
Step 2: Database Configuration
Configure the PostgreSQL connection in your application.properties file:1
spring.datasource.url=jdbc:postgresql://localhost:5432/mydatabase
spring.datasource.username=postgres
spring.datasource.password=yourpassword
spring.datasource.driver-class-name=org.postgresql.Driver
Or in application.yml format:3
spring:
datasource:
url: jdbc:postgresql://localhost:5432/mydatabase
username: postgres
password: yourpassword
driver-class-name: org.postgresql.Driver
jpa:
properties:
hibernate:
dialect: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
show-sql: true
The JDBC URL follows the pattern jdbc:postgresql://[host]:[port]/[database], where the default port for PostgreSQL is 5432.4
Step 3: Entity Definition with JPA Annotations
Entities are Java classes that map to database tables. Define them using JPA annotations:5
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "username", nullable = false, length = 100)
private String username;
@Column(name = "email", nullable = false, unique = true)
private String email;
@Column(name = "created_at")
private LocalDateTime createdAt;
// Getters and setters
}
- @Entity: Marks the class as a JPA entity representing a database table
- @Table(name = "users"): Specifies the table name; defaults to the class name if omitted
- @Id: Designates the primary key field
- @GeneratedValue: Defines primary key generation strategy (IDENTITY for auto-increment)
- @Column: Customizes column properties such as name, nullability, length, and uniqueness constraints
Step 4: Connection Pooling with HikariCP
Spring Boot uses HikariCP by default for connection pooling, which improves performance by reusing database connections:7
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.max-lifetime=1800000
These settings control:
- maximum-pool-size: Maximum number of active connections (default: 10)
- minimum-idle: Minimum idle connections maintained (default: 5)
- idle-timeout: Connection idle timeout in milliseconds (default: 30 seconds)
- max-lifetime: Maximum connection lifetime in milliseconds (default: 30 minutes)
Step 5: Creating Repository Interfaces
Repositories provide an abstraction layer for database operations. Use Spring Data JPA's CrudRepository or JpaRepository:8
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByEmail(String email);
List<User> findByUsernameContaining(String username);
}
The @Repository annotation is optional when extending JpaRepository, as Spring automatically detects and creates bean implementations. However, it enables exception translation, converting database-specific exceptions into Spring's DataAccessException hierarchy.910
Step 6: Implementing CRUD Operations
With repositories configured, implement CRUD operations in service classes:118
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Optional;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// Create
public User createUser(User user) {
return userRepository.save(user);
}
// Read
public List<User> getAllUsers() {
return userRepository.findAll();
}
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}
// Update
public User updateUser(Long id, User userDetails) {
User user = userRepository.findById(id)
.orElseThrow(() -> new ResourceNotFoundException("User not found"));
user.setUsername(userDetails.getUsername());
user.setEmail(userDetails.getEmail());
return userRepository.save(user);
}
// Delete
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
Step 7: Custom Query Methods
For complex queries, use Spring Data JPA's @Query annotation:12
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
@Query("SELECT u FROM User u WHERE u.email = ?1")
User findByEmailAddress(String emailAddress);
@Query(value = "SELECT * FROM users WHERE username LIKE ?1%", nativeQuery = true)
List<User> findUsersStartingWith(String prefix);
}
Set nativeQuery = true to execute raw SQL queries instead of JPQL (Java Persistence Query Language).13
Step 8: Transaction Management
Use the @Transactional annotation to manage database transactions:14
@Service
public class UserService {
@Transactional
public void transferData() {
// All database operations here are part of a single transaction
// If any exception occurs, all changes are rolled back
}
}
The @Transactional annotation:
- Automatically begins a transaction when the method is called
- Commits if the method completes successfully
- Rolls back if a RuntimeException occurs14
- Is automatically configured when using Spring Boot's data-jpa starter
Step 9: Schema Initialization
Control automatic schema creation with spring.jpa.hibernate.ddl-auto:1516
spring.jpa.hibernate.ddl-auto=update
Supported values:15
- validate: Validates the schema without modifications
- update: Updates the schema if necessary (recommended for development)
- create: Creates a new schema, destroying existing data
- create-drop: Creates schema on startup and drops it on shutdown (useful for testing)
- none: Takes no action (recommended for production)
For custom SQL initialization, create a schema.sql or import.sql file in the src/main/resources folder. Hibernate automatically executes these files during startup.15
Step 10: Data Type Mapping
PostgreSQL data types map to Java types through Hibernate:17
| PostgreSQL Type | Java Type | JDBC Type |
|---|---|---|
| INTEGER | int, Integer | NUMERIC |
| VARCHAR | String | VARCHAR |
| BOOLEAN | boolean | BOOLEAN |
| TIMESTAMP | LocalDateTime | TIMESTAMP |
| BIGINT | long, Long | NUMERIC |
| DECIMAL | BigDecimal | NUMERIC |
| TEXT | String | LONGVARCHAR |
| DATE | LocalDate | DATE |
Step 11: Lazy vs. Eager Loading
Control how related entities are fetched using @OneToMany, @ManyToOne, and @OneToOne annotations:1819
@Entity
@Table(name = "departments")
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(mappedBy = "department", fetch = FetchType.LAZY)
private List<Employee> employees;
}
@Entity
@Table(name = "employees")
public class Employee {
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "department_id")
private Department department;
}
- FetchType.LAZY (default for @OneToMany and @ManyToMany): Related data loads only when accessed, reducing memory consumption
- FetchType.EAGER (default for @ManyToOne and @OneToOne): Related data loads immediately with the parent entity
Lazy loading is generally recommended for better performance with large datasets.18
Step 12: Exception Handling
Spring translates database exceptions into DataAccessException, a hierarchy of unchecked exceptions:20
import org.springframework.dao.DataAccessException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DataAccessException.class)
public ResponseEntity<?> handleDataAccessException(DataAccessException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
.body("Database error: " + e.getMessage());
}
}
Common subclasses include DataIntegrityViolationException (constraint violations) and BadSqlGrammarException (SQL syntax errors).21
Step 13: Advanced Features - JSONB Support
PostgreSQL's JSONB data type stores JSON data efficiently. Enable support by adding the Hibernate Types dependency:22
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-52</artifactId>
<version>2.3.4</version>
</dependency>
Then define JSONB columns:23
@Entity
@Table(name = "products")
@TypeDef(name = "jsonb", typeClass = JsonBinaryType.class)
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Type(type = "jsonb")
@Column(columnDefinition = "jsonb")
private Map<String, Object> attributes;
}
Best Practices
Implement these practices for robust PostgreSQL integration:
- Always use prepared statements to prevent SQL injection when executing native queries
- Define proper indexes on frequently queried columns for performance optimization
- Use connection pooling (HikariCP) to manage database connections efficiently
- Apply @Transactional at the service layer to maintain data consistency
- Set appropriate DDL auto values - use
validateornonein production environments - Implement proper exception handling using Spring's DataAccessException hierarchy
- Use lazy loading for large collections to minimize memory and query overhead
- Validate input data before persisting to ensure data integrity
- Configure logging with
spring.jpa.show-sql=trueandlogging.level.org.hibernate.SQL=DEBUGduring development for debugging SQL statements - Use versioning tools like Flyway or Liquibase for production database migrations instead of relying on Hibernate's DDL auto generation
This comprehensive foundation enables you to build scalable, maintainable Spring Boot applications with PostgreSQL databases using modern Java practices and proven enterprise patterns. <span style="display:none">2425262728293031323334353637383940414243444546474849</span>
<div align="center">⁂</div>
Footnotes
-
https://qavi.tech/a-guide-to-seamlessly-connecting-postgresql-with-spring-boot/ ↩ ↩2
-
https://aws.amazon.com/blogs/opensource/using-a-database-from-a-spring-boot-application/ ↩
-
https://www.w3resource.com/PostgreSQL/snippets/postgresql-spring-boot.php ↩
-
https://www.linkedin.com/pulse/entity-annotation-spring-boot-venura-pavan-zesde ↩ ↩2
-
https://www.geeksforgeeks.org/advance-java/spring-data-jpa-table-annotation/ ↩
-
https://www.linkedin.com/pulse/connection-pooling-optimizing-database-access-java-spring-nayak-q2gcf ↩
-
https://www.codecademy.com/learn/spring-apis-data-with-jpa/modules/spring-data-and-jpa/cheatsheet ↩ ↩2
-
https://stackoverflow.com/questions/42691697/using-repository-annotation-when-implementing-jparepostiory-in-spring ↩
-
https://howtodoinjava.com/spring-boot/repository-annotation/ ↩
-
https://blog.stackademic.com/working-with-spring-data-jpa-crud-operations-and-beyond-9acc3fec32f2?gi=a32e07a01d8c ↩
-
https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html ↩
-
https://thorben-janssen.com/spring-data-jpa-query-annotation/ ↩
-
https://www.geeksforgeeks.org/springboot/spring-boot-transaction-management-using-transactional-annotation/ ↩ ↩2
-
https://docs.spring.io/spring-boot/docs/2.1.x/reference/html/howto-database-initialization.html ↩ ↩2 ↩3
-
https://docs.spring.io/spring-boot/how-to/data-initialization.html ↩
-
https://www.instaclustr.com/blog/postgresql-data-types-mappings-to-sql-jdbc-and-java-data-types/ ↩
-
https://backendhance.com/en/blog/2023/jpa-fetching-strategies/ ↩ ↩2
-
https://www.geeksforgeeks.org/software-testing/lazy-loading-vs-eager-loading/ ↩
-
https://www.logicbig.com/tutorials/spring-framework/spring-data-access-with-jdbc/data-access-exception.html ↩
-
https://blog.csdn.net/qq_27756951/article/details/149114919 ↩
-
https://aws.amazon.com/blogs/database/support-json-data-using-amazon-rds-for-postgresql-or-amazon-aurora-postgresql-and-java-spring-boot-on-aws/ ↩
-
https://aurigait.com/blog/add-custom-java-objects-using-jsonb-in-spring-boot/ ↩
-
https://dev.to/codereacher_20b8a/getting-started-with-spring-boot-and-postgresql-a-beginner-friendly-guide-2mhb ↩
-
https://www.cdata.com/kb/tech/postgresql-jdbc-spring-boot.rst ↩
-
https://www.bezkoder.com/spring-boot-jdbctemplate-postgresql-example/ ↩
-
https://dzone.com/articles/bounty-spring-boot-and-postgresql-database ↩
-
https://docs.spring.io/spring-framework/reference/data-access/orm/introduction.html ↩
-
https://stackoverflow.com/questions/50213381/how-do-i-configure-hikaricp-for-postgresql ↩
-
https://stackoverflow.com/questions/69408767/can-i-use-spring-data-jpa-with-postgresql ↩
-
https://www.geeksforgeeks.org/advance-java/configuring-a-hikari-connection-pool-with-spring-boot/ ↩
-
https://docs.spring.io/spring-data/jpa/reference/repositories/definition.html ↩
-
https://www.javaguides.net/2019/01/springboot-postgresql-jpa-hibernate-crud-restful-api-tutorial.html ↩
-
https://www.geeksforgeeks.org/advance-java/spring-data-jpa-column-annotation/ ↩
-
https://www.geeksforgeeks.org/advance-java/storing-postgresql-jsonb-using-spring-boot-and-jpa/ ↩
-
https://www.javaguides.net/2023/07/jpa-column-annotation.html ↩
-
https://www.marcobehler.com/guides/spring-transaction-management-transactional-in-depth ↩
-
https://stackoverflow.com/questions/70263946/spring-boot-exception-handling-custom-exceptions-for-database-errors ↩
-
https://www.sivalabs.in/spring-boot-database-transaction-management-tutorial/ ↩
-
https://github.com/Martin-Hogge/spring-boot-postgresql-transactional-example ↩
-
https://www.masterspringboot.com/data-access/jpa-applications/how-to-get-your-tables-automatically-created-with-spring-boot/ ↩
-
https://stackoverflow.com/questions/49493360/how-to-auto-create-postgresql-schema-other-than-the-default-public-one-using ↩
-
https://stackoverflow.com/questions/79612439/spring-boot-no-difference-between-lazy-and-eager-loading ↩
-
https://stackoverflow.com/questions/45739379/creating-custom-user-types-for-jsonb-columns-in-hibernate-postgresql ↩