We will implement REST APIs for performing CRUD operations:
- Create a user (POST)
- Update a user (PUT)
- Get a user by ID (GET)
- Get all users (GET)
- Delete a user (DELETE)
We will use DTOs (records) to transfer data between client and server and implement exception handling for better error responses.
What You Will Learn
- How to set up a Spring Boot 3 project with Hibernate 6.
- How to configure One-to-One mapping between entities.
- How to use DTOs for data transfer.
- How to implement proper exception handling.
- How to build REST APIs for performing CRUD operations:
- How to test APIs using Postman.
Step 1: Setting Up the Spring Boot Project
Create a Spring Boot 3 project using Spring Initializr. Select the following dependencies:
- Spring Web (for REST API development)
- Spring Data JPA (for ORM and Hibernate support)
- MySQL Driver (for database connectivity)
- Validation (for input validation)
Adding Dependencies to pom.xml
Add the required dependencies to your pom.xml
file:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>9.2.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
</dependencies>
Step 2: Configuring the application.properties
We need to configure our MySQL database settings in src/main/resources/application.properties
.
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
Replace root
with your actual MySQL username and password. ddl-auto=update
allows Hibernate to create and update tables automatically.
Step 3: Creating Entity Classes
We will create two entities: User
and Address
. Each User
will have one Address
, forming a One-to-One relationship.
User Entity
import jakarta.persistence.*;
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String name;
@Column(nullable = false, unique = true)
private String email;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
public User() {}
public User(String name, String email, Address address) {
this.name = name;
this.email = email;
this.address = address;
}
// Getters and Setters
}
@Entity
: Marks the class as a Hibernate entity.@Table(name = "users")
: Maps this entity to theusers
table.@Id
: Declares the primary key.@GeneratedValue(strategy = GenerationType.IDENTITY)
: Auto-generates the ID.@Column(nullable = false)
: Ensures the field is required.@OneToOne
: Defines a One-to-One relationship withAddress
.@JoinColumn(name = "address_id")
: Specifies the foreign key column.cascade = CascadeType.ALL, orphanRemoval = true
: Ensures changes in the parent affect the child entity.
Address Entity
import jakarta.persistence.*;
@Entity
@Table(name = "addresses")
public class Address {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String street;
@Column(nullable = false)
private String city;
@Column(nullable = false)
private String state;
@Column(nullable = false)
private String zipCode;
public Address() {}
public Address(String street, String city, String state, String zipCode) {
this.street = street;
this.city = city;
this.state = state;
this.zipCode = zipCode;
}
// Getters and Setters
}
Step 4: Creating the Repository Layer
We need to create repositories to handle database operations.
User Repository
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.User;
public interface UserRepository extends JpaRepository<User, Long> {
}
Address Repository
import org.springframework.data.jpa.repository.JpaRepository;
import com.example.demo.entity.Address;
public interface AddressRepository extends JpaRepository<Address, Long> {
}
Step 5: Creating DTO Classes
We will use DTOs (records) to transfer data between client and server.
User DTO
public record UserDTO(Long id, String name, String email, AddressDTO address) {}
Address DTO
public record AddressDTO(Long id, String street, String city, String state, String zipCode) {}
Step 6: Creating the Service Layer
UserService
Interface
The service layer acts as an intermediary between the controller and the repository. It contains business logic and ensures proper data transformation.
import java.util.List;
import com.example.demo.dto.UserDTO;
public interface UserService {
UserDTO createUser(UserDTO userDTO);
UserDTO updateUser(Long userId, UserDTO userDTO);
UserDTO getUserById(Long userId);
List<UserDTO> getAllUsers();
void deleteUser(Long userId);
}
UserServiceImpl
Implementation
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.demo.dto.UserDTO;
import com.example.demo.dto.AddressDTO;
import com.example.demo.entity.User;
import com.example.demo.entity.Address;
import com.example.demo.exception.ResourceNotFoundException;
import com.example.demo.repository.UserRepository;
@Service
@Transactional
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDTO createUser(UserDTO userDTO) {
Address address = new Address(userDTO.address().street(), userDTO.address().city(), userDTO.address().state(), userDTO.address().zipCode());
User user = new User(userDTO.name(), userDTO.email(), address);
user = userRepository.save(user);
return mapToDTO(user);
}
@Override
public UserDTO updateUser(Long userId, UserDTO userDTO) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + userId));
user.setName(userDTO.name());
user.setEmail(userDTO.email());
Address address = user.getAddress();
address.setStreet(userDTO.address().street());
address.setCity(userDTO.address().city());
address.setState(userDTO.address().state());
address.setZipCode(userDTO.address().zipCode());
user = userRepository.save(user);
return mapToDTO(user);
}
@Override
public UserDTO getUserById(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + userId));
return mapToDTO(user);
}
@Override
public List<UserDTO> getAllUsers() {
return userRepository.findAll().stream().map(this::mapToDTO).collect(Collectors.toList());
}
@Override
public void deleteUser(Long userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new ResourceNotFoundException("User not found with ID: " + userId));
userRepository.delete(user);
}
private UserDTO mapToDTO(User user) {
AddressDTO addressDTO = new AddressDTO(user.getAddress().getId(), user.getAddress().getStreet(), user.getAddress().getCity(), user.getAddress().getState(), user.getAddress().getZipCode());
return new UserDTO(user.getId(), user.getName(), user.getEmail(), addressDTO);
}
}
Explanation of annotations and methods:
@Service
: Marks the class as a Spring service component.@Transactional
: Ensures that database operations within the service methods are executed within a transaction.- createUser: Converts DTO to an entity, saves the user, and returns a DTO.
- updateUser: Finds the user, updates details, and returns the modified DTO.
- getUserById: Fetches a user from the database using their ID.
- getAllUsers: Retrieves all users and maps them to DTOs.
- deleteUser: Deletes a user by ID after checking existence.
Step 7: Creating the Controller
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import com.example.demo.dto.UserDTO;
import com.example.demo.service.UserService;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping
public ResponseEntity<UserDTO> createUser(@RequestBody UserDTO userDTO) {
UserDTO createdUser = userService.createUser(userDTO);
return new ResponseEntity<>(createdUser, HttpStatus.CREATED);
}
@PutMapping("/{id}")
public ResponseEntity<UserDTO> updateUser(@PathVariable Long id, @RequestBody UserDTO userDTO) {
UserDTO updatedUser = userService.updateUser(id, userDTO);
return ResponseEntity.ok(updatedUser);
}
@GetMapping("/{id}")
public ResponseEntity<UserDTO> getUserById(@PathVariable Long id) {
UserDTO userDTO = userService.getUserById(id);
return ResponseEntity.ok(userDTO);
}
@GetMapping
public ResponseEntity<List<UserDTO>> getAllUsers() {
return ResponseEntity.ok(userService.getAllUsers());
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
return ResponseEntity.noContent().build();
}
}
Explanation of Controller Methods
@RestController
: Marks the class as a REST API controller.@RequestMapping("/api/users")
: Sets the base path for all endpoints in this controller.@PostMapping
: Handles HTTP POST requests.@PutMapping
: Handles HTTP PUT requests.@GetMapping
: Handles HTTP GET requests.@DeleteMapping
: Handles HTTP DELETE requests.ResponseEntity
: Used to send proper HTTP status codes.
Step 8: Testing REST APIs with Sample Data (Using Postman)
Now that we have implemented the REST APIs. Next, let's test them with sample data using Postman.
1. Creating a User (POST Request)
Endpoint: POST http://localhost:8080/api/users
Request Body:
{
"name": "Rajesh Kumar",
"email": "rajesh.kumar@example.com",
"address": {
"street": "MG Road",
"city": "Mumbai",
"state": "Maharashtra",
"zipCode": "400001"
}
}
Response: 201 Created
{
"id": 1,
"name": "Rajesh Kumar",
"email": "rajesh.kumar@example.com",
"address": {
"id": 1,
"street": "MG Road",
"city": "Mumbai",
"state": "Maharashtra",
"zipCode": "400001"
}
}
2. Updating a User (PUT Request)
Endpoint: PUT http://localhost:8080/api/users/1
Request Body:
{
"name": "Rajesh Sharma",
"email": "rajesh.sharma@example.com",
"address": {
"street": "Brigade Road",
"city": "Bangalore",
"state": "Karnataka",
"zipCode": "560001"
}
}
Response: 200 OK
{
"id": 1,
"name": "Rajesh Sharma",
"email": "rajesh.sharma@example.com",
"address": {
"id": 1,
"street": "Brigade Road",
"city": "Bangalore",
"state": "Karnataka",
"zipCode": "560001"
}
}
3. Getting a User by ID (GET Request)
Endpoint: GET http://localhost:8080/api/users/1
Response: 200 OK
{
"id": 1,
"name": "Rajesh Sharma",
"email": "rajesh.sharma@example.com",
"address": {
"id": 1,
"street": "Brigade Road",
"city": "Bangalore",
"state": "Karnataka",
"zipCode": "560001"
}
}
4. Getting All Users (GET Request)
Endpoint: GET http://localhost:8080/api/users
Response: 200 OK
[
{
"id": 1,
"name": "Rajesh Sharma",
"email": "rajesh.sharma@example.com",
"address": {
"id": 1,
"street": "Brigade Road",
"city": "Bangalore",
"state": "Karnataka",
"zipCode": "560001"
}
},
{
"id": 2,
"name": "Priya Singh",
"email": "priya.singh@example.com",
"address": {
"id": 2,
"street": "Connaught Place",
"city": "Delhi",
"state": "Delhi",
"zipCode": "110001"
}
}
]
5. Deleting a User (DELETE Request)
Endpoint: DELETE http://localhost:8080/api/users/1
Response: 204 No Content
Conclusion
We have successfully implemented and tested a One-to-One Mapping REST API using Spring Boot 3, Hibernate 6, MySQL, and DTOs. The REST APIs include CRUD operations and proper error handling, and we tested them with sample data using Indian names.
This concludes the tutorial. Happy coding!
Comments
Post a Comment
Leave Comment