Date: January 21, 2025
Welcome back! So far, you’ve set up your Spring Boot project and designed your REST APIs with DTOs and controllers. Now, it’s time to connect your application to a real database using JPA and Hibernate for persistence.
What You’ll Learn in This Part
- Configuring Spring Boot with JPA and Hibernate
- Creating entity classes and repositories
- Mapping between DTOs and entities
- Performing CRUD operations backed by the database
- Simple configuration of an in-memory database (H2) for testing
Step 1: Add JPA and Database Dependencies
Update your pom.xml
or build.gradle
to include:
<!-- Maven example -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- H2 in-memory database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Step 2: Configure the Database
In src/main/resources/application.properties
, add:
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
spring.h2.console.enabled=true
This sets up an in-memory H2 database with an accessible web console at /h2-console
.
Step 3: Define the Entity
Replace the previous simple User model with a JPA entity that maps to a table.
package com.example.demoapp.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
// Constructors, getters and setters
}
Step 4: Create the Repository Interface
Spring Data JPA will generate implementation at runtime.
package com.example.demoapp.repository;
import com.example.demoapp.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
// Optional: Custom queries can go here
}
Step 5: Service Layer to Handle Business Logic
Create a service to decouple controller from repository:
package com.example.demoapp.service;
import com.example.demoapp.dto.UserDTO;
import com.example.demoapp.model.User;
import com.example.demoapp.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
// Convert entity to DTO
private UserDTO convertToDTO(User user) {
UserDTO dto = new UserDTO();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setEmail(user.getEmail());
return dto;
}
// Convert DTO to entity
private User convertToEntity(UserDTO dto) {
User user = new User();
user.setId(dto.getId());
user.setName(dto.getName());
user.setEmail(dto.getEmail());
return user;
}
public UserDTO createUser(UserDTO dto) {
User user = convertToEntity(dto);
User saved = userRepository.save(user);
return convertToDTO(saved);
}
public UserDTO getUserById(Long id) {
return userRepository.findById(id)
.map(this::convertToDTO)
.orElse(null);
}
public List<UserDTO> getAllUsers() {
return userRepository.findAll().stream()
.map(this::convertToDTO)
.collect(Collectors.toList());
}
public UserDTO updateUser(Long id, UserDTO dto) {
return userRepository.findById(id).map(user -> {
user.setName(dto.getName());
user.setEmail(dto.getEmail());
User updated = userRepository.save(user);
return convertToDTO(updated);
}).orElse(null);
}
public boolean deleteUser(Long id) {
if (!userRepository.existsById(id)) {
return false;
}
userRepository.deleteById(id);
return true;
}
}
Step 6: Update the Controller to Use the Service
package com.example.demoapp.controller;
import com.example.demoapp.dto.UserDTO;
import com.example.demoapp.service.UserService;
import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
@Autowired
private UserService userService;
@PostMapping
public ResponseEntity<UserDTO> createUser(@Valid @RequestBody UserDTO userDto) {
UserDTO created = userService.createUser(userDto);
return new ResponseEntity<>(created, HttpStatus.CREATED);
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUser(@PathVariable Long id) {
UserDTO user = userService.getUserById(id);
if (user == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(user);
}
@GetMapping
public List<UserDTO> getAllUsers() {
return userService.getAllUsers();
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @Valid @RequestBody UserDTO userDto) {
UserDTO updated = userService.updateUser(id, userDto);
if (updated == null) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(updated);
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean deleted = userService.deleteUser(id);
if (!deleted) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.noContent().build();
}
}
Step 7: Run and Test
- Start your app
- Access the H2 console at http://localhost:8080/h2-console
- Test your endpoints with Postman or curl as before
The data will persist in-memory during runtime, and Hibernate will automatically create and update tables.
Summary
In this part, we have:
- Configured Spring Boot for JPA with Hibernate
- Created a database-backed User entity
- Built a repository and service layer for clean separation of concerns
- Updated controller to use persistence services
Next Up
In Part 4, we will secure the app using Spring Security to protect your APIs and manage authentication/authorization.