Spring Boot Backend Development Course, Trying Out Methods Provided by Spring Data JPA

Hello! In this tutorial, we will cover backend development using Spring Boot and Spring Data JPA.
Spring Data JPA is a powerful and flexible framework for object-relational mapping (ORM),
making interactions with databases easier. In particular, Spring Data JPA provides various methods
to help developers perform CRUD (Create, Read, Update, Delete) operations more easily.

1. What is Spring Data JPA?

Spring Data JPA is a library for managing the persistence of data based on the Spring Framework and JPA (Java Persistence API).
JPA allows Java objects to be mapped to database tables, enabling the management of database data as Java objects.
This makes interactions with the database more intuitive and straightforward.

2. Setting Up a Spring Boot Project

The process of setting up a project using Spring Boot is very simple. You can select the required dependencies through
start.spring.io and download a ZIP file to create your project. In this example, we will add Spring Web,
Spring Data JPA, and H2 Database.

2.1 Gradle or Maven Configuration

Add the following dependencies to the build.gradle or pom.xml file of the downloaded project.

dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-web'
        runtimeOnly 'com.h2database:h2'
    }

3. Creating an Entity Class

To use Spring Data JPA, you first need to define an Entity class that will be mapped to a database table.
For example, let’s create a simple `User` class to store user information.

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String username;
    private String email;

    // Getters and Setters
}

4. Implementing the Repository Interface

Spring Data JPA introduces the concept of Repository to simplify database access.
Create a Repository interface and define the necessary methods.

import org.springframework.data.jpa.repository.JpaRepository;

public interface UserRepository extends JpaRepository {
    User findByUsername(String username);
}

5. Creating a Service Class

The service class will implement the actual business logic and perform CRUD operations by injecting the Repository.
For example, let’s create the `UserService` class.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public User save(User user) {
        return userRepository.save(user);
    }

    public List findAll() {
        return userRepository.findAll();
    }

    public User findById(Long id) {
        return userRepository.findById(id).orElse(null);
    }

    public void delete(Long id) {
        userRepository.deleteById(id);
    }
}

6. Creating a Controller Class

The controller class handles HTTP requests and manages interactions with the client. To implement a RESTful API,
we will write the `UserController` class.

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.save(user);
    }

    @GetMapping
    public List getAllUsers() {
        return userService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity getUserById(@PathVariable Long id) {
        User user = userService.findById(id);
        return user != null ? ResponseEntity.ok(user) : ResponseEntity.notFound().build();
    }

    @DeleteMapping("/{id}")
    public ResponseEntity deleteUser(@PathVariable Long id) {
        userService.delete(id);
        return ResponseEntity.noContent().build();
    }
}

7. Using Methods Provided by Spring Data JPA

Spring Data JPA helps you handle many tasks easily through its built-in methods.
Here, we will look at some key methods.

7.1 findAll

The findAll() method is used to retrieve all records from the database.
This method returns all entities in the form of a List.

7.2 findById

The findById(Long id) method retrieves the entity corresponding to a specific ID.
The return value is of type Optional, which returns Optional.empty() if there is no result.

7.3 save

The save(User user) method saves a new entity or updates an existing entity.
This method helps implement business logic simply.

7.4 deleteById

The deleteById(Long id) method deletes the entity corresponding to the given ID.
The specified entity is deleted from the database.

7.5 Query Methods

Spring Data JPA allows you to define complex queries using query methods. For example, the
findByUsername(String username) method retrieves user information that matches the entered username.
Query Methods automatically generate queries based on the method name.

8. Using JPA Queries

Spring Data JPA supports JPQL (Java Persistence Query Language) and Native Query.
In some cases, complex queries may be necessary, and in such cases, queries can be written as follows.

8.1 Using JPQL

@Query("SELECT u FROM User u WHERE u.username = ?1")
    User findByUsername(String username);

8.2 Using Native Query

@Query(value = "SELECT * FROM users WHERE username = ?1", nativeQuery = true)
    User findByUsernameNative(String username);

9. Data Validation and Exception Handling

Data integrity can be maintained through validation. For this, Bean Validation can be used.
In Spring, you can conveniently handle validation of request body data using the @Valid annotation.

import javax.validation.Valid;

@PostMapping
public User createUser(@Valid @RequestBody User user) {
    return userService.save(user);
}

10. Writing Test Code

Every application should be validated through unit tests and integration tests.
In Spring Boot, it is easy to write test code.

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;

@WebMvcTest(UserController.class)
public class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void createUser_ShouldReturnUser() throws Exception {
        String newUserJson = "{\"username\":\"testuser\",\"email\":\"test@example.com\"}";

        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(newUserJson))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.username").value("testuser"));
    }
}

Conclusion

In this tutorial, we learned about backend development using Spring Boot and the methods provided by Spring Data JPA.
Spring Data JPA is a powerful tool that simplifies interactions with the database.
We found that rapid and efficient backend development is possible with various methods and querying capabilities.
We hope you will continue to utilize Spring Boot for development through diverse functions and use cases.