Spring Boot Lombok Tutorial

Introduction

Lombok is a Java library that helps reduce boilerplate code by generating common methods like getters, setters, equals, hashCode, toString, and more at compile time. It uses annotations to achieve this, making the code cleaner and more readable. In this tutorial, we will demonstrate how to use various Lombok annotations in a Spring Boot application.

To learn more about Lombok Library, check out this guide: Lombok.

Benefits of Lombok Integration

1. Reduces Boilerplate Code

Lombok eliminates the need for manually writing common methods such as getters, setters, toString(), equals(), and hashCode(). This results in a cleaner and more concise codebase.

2. Improves Readability

By reducing boilerplate code, Lombok makes your classes easier to read and understand. This is particularly useful in large projects where maintaining clean code is crucial.

3. Simplifies Data Classes

Lombok is particularly useful for simplifying data classes, which often require numerous getters and setters. Annotations like @Data, @Getter, @Setter, @NoArgsConstructor, and @AllArgsConstructor automatically generate these methods.

4. Enhances Productivity

Lombok enhances developer productivity by reducing the amount of code that needs to be written and maintained. This allows developers to focus on writing business logic instead of repetitive code.

How Lombok Works

Lombok uses annotations to generate code at compile time. These annotations are processed by Lombok's annotation processor, which injects the necessary methods into your classes. Common Lombok annotations include:

  • @Getter and @Setter: Generates getter and setter methods for fields.
  • @ToString: Generates a toString() method.
  • @EqualsAndHashCode: Generates equals() and hashCode() methods.
  • @NoArgsConstructor: Generates a no-argument constructor.
  • @AllArgsConstructor: Generates a constructor with all fields as parameters.
  • @Data: Combines @Getter, @Setter, @ToString, @EqualsAndHashCode, and @RequiredArgsConstructor into a single annotation.

Integrating Lombok with Spring Boot

To integrate Lombok with Spring Boot, you need to:

  1. Add the Lombok Dependency: Include Lombok in your project's dependencies.
  2. Enable Annotation Processing: Ensure that your IDE is configured to support annotation processing for Lombok.
  3. Use Lombok Annotations: Annotate your classes with Lombok annotations to automatically generate the desired methods.

Prerequisites

  1. Java Development Kit (JDK) 17 or later
  2. Apache Maven installed
  3. An IDE like IntelliJ IDEA or Eclipse with Lombok plugin installed

Step 1: Create a Spring Boot Project

You can create a Spring Boot project using Spring Initializr or your IDE.

Using Spring Initializr

  1. Go to Spring Initializr.
  2. Select the following options:
    • Project: Maven Project
    • Language: Java
    • Spring Boot: 3.0.0 or later
    • Group: com.example
    • Artifact: lombok-demo
    • Name: lombok-demo
    • Package name: com.example.lombokdemo
    • Packaging: Jar
    • Java: 17 or later
  3. Add the following dependencies:
    • Spring Web
    • Spring Data JPA
    • H2 Database
    • Spring Boot Starter Test
  4. Click "Generate" to download the project zip file.
  5. Extract the zip file and open the project in your IDE.

Step 2: Add Lombok Dependency

Add the following dependency to your pom.xml file:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- H2 Database -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- Spring Boot Starter Test -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
        <exclusions>
            <exclusion>
                <groupId>org.junit.vintage</groupId>
                <artifactId>junit-vintage-engine</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.24</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

Step 3: Configure Application Properties

Add the following properties to src/main/resources/application.properties:

spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

Step 4: Create Entity Classes Using Lombok

Create the Department Entity

Create a new Java class named Department in the com.example.lombokdemo package:

package com.example.lombokdemo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

import java.util.List;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;

    @OneToMany(mappedBy = "department")
    private List<Employee> employees;
}

Explanation:

  • @Data: Generates getters, setters, toString, equals, and hashCode methods.
  • @NoArgsConstructor: Generates a no-arguments constructor.
  • @AllArgsConstructor: Generates an all-arguments constructor.

Create the Employee Entity

Create a new Java class named Employee in the com.example.lombokdemo package:

package com.example.lombokdemo;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;

@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String email;

    @ManyToOne
    private Department department;
}

Step 5: Create Repository Interfaces

DepartmentRepository

Create a new Java interface named DepartmentRepository in the com.example.lombokdemo package:

package com.example.lombokdemo;

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

public interface DepartmentRepository extends JpaRepository<Department, Long> {
}

EmployeeRepository

Create a new Java interface named EmployeeRepository in the com.example.lombokdemo package:

package com.example.lombokdemo;

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

public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}

Step 6: Create Service Classes

DepartmentService

Create a new Java class named DepartmentService in the com.example.lombokdemo package:

package com.example.lombokdemo;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class DepartmentService {

    private final DepartmentRepository departmentRepository;

    public List<Department> findAll() {
        return departmentRepository.findAll();
    }

    public Optional<Department> findById(Long id) {
        return departmentRepository.findById(id);
    }

    public Department save(Department department) {
        return departmentRepository.save(department);
    }

    public void deleteById(Long id) {
        departmentRepository.deleteById(id);
    }
}

Explanation:

  • @RequiredArgsConstructor: Generates a constructor with required arguments (final fields).

EmployeeService

Create a new Java class named EmployeeService in the com.example.lombokdemo package:

package com.example.lombokdemo;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
@RequiredArgsConstructor
public class EmployeeService {

    private final EmployeeRepository employeeRepository;

    public List<Employee> findAll() {
        return employeeRepository.findAll();
    }

    public Optional<Employee> findById(Long id) {
        return employeeRepository.findById(id);
    }

    public Employee save(Employee employee) {
        return employeeRepository.save(employee);
    }

    public void deleteById(Long id) {
        employeeRepository.deleteById(id);
    }
}

Step 7: Create Controller Classes

DepartmentController

Create a new Java class named DepartmentController in the com.example.lombokdemo package:

package com.example.lombokdemo;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/departments")
@RequiredArgsConstructor
public class DepartmentController {

    private final DepartmentService departmentService;

    @GetMapping
    public List<Department> getAllDepartments() {
        return departmentService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Department> getDepartmentById(@PathVariable Long id) {
        return departmentService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public Department createDepartment(@RequestBody Department department) {
        return departmentService.save(department);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Department> updateDepartment(@PathVariable Long id, @RequestBody Department department) {
        return departmentService.findById(id)
                .map(existingDepartment -> {
                    department.setId(existingDepartment.getId());
                    return ResponseEntity.ok(departmentService.save(department));
                })
                .orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteDepartment(@PathVariable Long id) {
        return departmentService.findById(id)
                .map(department -> {
                    departmentService.deleteById(id);
                    return ResponseEntity.noContent().build();
                })
                .orElse(ResponseEntity.notFound().build());
    }
}

EmployeeController

Create a new Java class named EmployeeController in the com.example.lombokdemo package:

package com.example.lombokdemo;

import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/employees")
@RequiredArgsConstructor
public class EmployeeController {

    private final EmployeeService employeeService;

    @GetMapping
    public List<Employee> getAllEmployees() {
        return employeeService.findAll();
    }

    @GetMapping("/{id}")
    public ResponseEntity<Employee> getEmployeeById(@PathVariable Long id) {
        return employeeService.findById(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    @PostMapping
    public Employee createEmployee(@RequestBody Employee employee) {
        return employeeService.save(employee);
    }

    @PutMapping("/{id}")
    public ResponseEntity<Employee> updateEmployee(@PathVariable Long id, @RequestBody Employee employee) {
        return employeeService.findById(id)
                .map(existingEmployee -> {
                    employee.setId(existingEmployee.getId());
                    return ResponseEntity.ok(employeeService.save(employee));
                })
                .orElse(ResponseEntity.notFound().build());
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteEmployee(@PathVariable Long id) {
        return employeeService.findById(id)
                .map(employee -> {
                    employeeService.deleteById(id);
                    return ResponseEntity.noContent().build();
                })
                .orElse(ResponseEntity.notFound().build());
    }
}

Step 8: Create the Main Application Class

Create a main application class named LombokDemoApplication in the com.example.lombokdemo package:

package com.example.lombokdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LombokDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(LombokDemoApplication.class, args);
    }
}

Explanation: The LombokDemoApplication class contains the main method, which is the entry point of the Spring Boot application. The @SpringBootApplication annotation is a convenience annotation that adds all the following:

  • @Configuration: Tags the class as a source of bean definitions for the application context.
  • @EnableAutoConfiguration: Tells Spring Boot to start adding beans based on classpath settings, other beans, and various property settings.
  • @ComponentScan: Tells Spring to look for other components, configurations, and services in the specified package.

Step 9: Test the Application

Start your Spring Boot application and use tools like Postman or curl to test the CRUD operations for both Department and Employee entities.

Create a Department

  • Method: POST
  • URL: http://localhost:8080/departments
  • Body:
    {
        "name": "IT Department"
    }
    

Get All Departments

  • Method: GET
  • URL: http://localhost:8080/departments

Create an Employee

  • Method: POST
  • URL: http://localhost:8080/employees
  • Body:
    {
        "name": "Amit Sharma",
        "email": "amit.sharma@example.com",
        "department": {
            "id": 1,
            "name": "IT Department"
        }
    }
    

Get All Employees

  • Method: GET
  • URL: http://localhost:8080/employees

Get an Employee by ID

  • Method: GET
  • URL: http://localhost:8080/employees/{id}

Update an Employee

  • Method: PUT
  • URL: http://localhost:8080/employees/{id}
  • Body:
    {
        "name": "Rohit Sharma",
        "email": "rohit.sharma@example.com",
        "department": {
            "id": 1,
            "name": "IT Department"
        }
    }
    

Delete an Employee

  • Method: DELETE
  • URL: http://localhost:8080/employees/{id}

Conclusion

In this tutorial, we demonstrated how to use Lombok to reduce boilerplate code in a Spring Boot application. We covered the creation of entities, repositories, services, and controllers, and showed how to use various Lombok annotations to simplify the code. 

By following these steps, you can efficiently use Lombok to make your code more readable and maintainable. This approach ensures that your code is clean and reduces the boilerplate code required for common operations.

Comments