Boot

Spring Boot with Lombok

Welcome readers, in this tutorial, we will explain Lombok. We will also create a simple Spring Boot application with Lombok and persist the records in the h2 database.

1. Introduction

Before going further in this tutorial, we will look at the common terminology such as introduction to Spring Boot, Lombok, and h2 database.

1.1 Spring Boot

  • Spring boot is a module that provides rapid application development feature to the spring framework including auto-configuration, standalone-code, and production-ready code
  • It creates applications that are packaged as jar and are directly started using embedded servlet container (such as Tomcat, Jetty or, Undertow). Thus, no need to deploy the war files
  • It simplifies the maven configuration by providing the starter template and helps to resolve the dependency conflicts. It automatically identifies the required dependencies and imports them in the application
  • It helps in removing the boilerplate code, extra annotations, and xml configurations
  • It provides a powerful batch processing and manages the rest endpoints
  • It provides an efficient jpa-starter library to effectively connect the application with the relational databases
  • It offers a Microservice architecture and cloud configuration that manages all the application related configuration properties in a centralized manner

1.2 Lombok

  • Lombok is nothing but a small library which reduces the amount of boilerplate Java code from the project
  • Automatically generates the getters and setters for the object by using the Lombok annotations
  • Hooks in via the Annotation processor API
  • Raw source code is passed to Lombok for code generation before the Java Compiler continues. Thus, produces properly compiled Java code in conjunction with the Java Compiler
  • Under the target/classes folder developers can view the compiled class files
  • Can be used with Maven, Gradle IDE, etc.

1.2.1 Lombok features

FeatureDetails
valLocal variables are declared as final
varMutable local variables
@Slf4JCreates an SLF4J logger
@CleanupWill call close() on the resource in the finally block
@GetterCreates getter methods for all properties
@SetterCreates setter for all non-final properties
@EqualsAndHashCode
  • Generates implementations of equals(Object other) and hashCode()
  • By default will use all non-static, non-transient properties
  • Can optionally exclude specific properties
@ToString
  • Generates String of class name, and each field separated by commas
  • Optional parameter to include field names
  • Optional parameter to include a call to the super toString method
@NoArgsConstructor
  • Generates no-args constructor
  • Will cause compiler error if there are final fields
  • Can optionally force, which will initialize final fields with 0/false/null var – mutable local variables
@RequiredArgsContructor
  • Generates a constructor for all fields that are final or marked @NonNull
  • The constructor will throw a NullPointerException if any @NonNull fields are null val – local variables are declared final
@AllArgsConstructor
  • Generates a constructor for all properties of the class
  • Any @NotNull properties will have null checks
@Data
  • Generates typical boilerplate code for POJOs
  • Combines – @Getter, @Setter, @ToString, @EqualsAndHashCode, @RequiredArgsConstructor
  • No constructor is generated if constructors have been explicitly declared
@Builder
  • Implements the Builder pattern for object creation
@Value
  • The immutable variant of @Data
  • All fields are made private and final by default

1.3 H2 Database

H2 databases are open-source compact relational databases written in Java language. They are popularly known as in-memory databases and are generally used for unit testing or proof-of-concept purposes. The data in-memory database only exists when the application works (i.e. the H2 database is created/initialized when an application starts) and is destroyed when the application shuts down. The H2 database provides developers with an administration window known as the H2 console.

2. Spring Boot and Lombok

Here is a systematic guide for implementing this tutorial but before going any further I’m assuming that readers are aware of the Spring boot.

2.1 Application Pre-requisite

To start with this tutorial, we are hoping that readers at present have the Lombok plugin installed in the IDE of their favorite choice. If someone needs to go through the Lombok installation on IntelliJ IDE, please watch this video. For installation on Eclipse IDE, please watch this video.

2.2 Tools Used and Project Structure

We are using Eclipse Kepler SR2, JDK 8, and Maven. In case you’re confused about where you should create the corresponding files or folder, let us review the project structure of the spring boot application.

Spring Boot Lombok - Project structure
Fig. 1: Project structure

Let us start building the application!

3. Creating a Spring Boot application

Below are the steps involved in developing the application.

3.1 Maven Dependency

Here, we specify the dependency for the Spring Boot, h2 database, Faker, and Lombok. Maven will automatically resolve the other dependencies. The updated file will have the following code.

pom.xml

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.springboot.lombok</groupId>
    <artifactId>SpringbootAndLombok</artifactId>
    <version>0.0.1-SNAPSHOT</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.3.RELEASE</version>
    </parent>
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
    <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>
        <!-- embedded database (h2) dependency. -->
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok dependency. -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <scope>provided</scope>
        </dependency>
        <!-- faker dependency to generate some random data. -->
        <dependency>
            <groupId>com.github.javafaker</groupId>
            <artifactId>javafaker</artifactId>
            <version>1.0.2</version>
        </dependency>
    </dependencies>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.2 Application Properties

Create a new properties file at the location: SpringbootAndLombok/src/main/resources/ and add the following code to it.

application.properties

01
02
03
04
05
06
07
08
09
10
11
12
13
14
server.port=10093
spring.application.name=springboot-and-lombok
# h2 database settings
spring.datasource.username=book_admin
spring.datasource.password=book_admin@1234!
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
# db-creation settings
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.show_sql=true
## browser url for h2 console - http://localhost:10093/h2-console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console

3.3 Java Classes

Let us write all the java classes involved in this application.

3.3.1 Implementation/Main class

Add the following code to the main class to bootstrap the application from the main method. Always remember, the entry point of the spring boot application is the class containing @SpringBootApplication annotation and the static main method.

SpringbootAndLombok.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
package com.springboot.lombok;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
 
import lombok.extern.slf4j.Slf4j;
 
@SpringBootApplication
// Causes Lombok to generate a logger field.
@Slf4j
public class SpringbootAndLombok {
 
    public static void main(String[] args) {
        SpringApplication.run(SpringbootAndLombok.class, args);
        log.info("Springboot and lombok application started successfully.");
    }
}

3.3.2 Model class

Add the following code to the Book model class.

Book.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.springboot.lombok.model;
 
import java.time.LocalDateTime;
 
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
 
import org.springframework.stereotype.Component;
 
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
 
@Entity
@Table(name = "book")
// Lombok annotations
// Causes lombok to generate toString(), equals(), hashCode(), getter() & setter(), and Required arguments constructor in one go.
@Data
// Causes Lombok to implement the Builder design pattern for the Pojo class.
// Usage can be seen in DefaultBookLoader.java -> createNewBook() method.
@Builder
// Causes Lombok to generate a constructor with no parameters.
@NoArgsConstructor
// // Causes Lombok to generate a constructor with 1 parameter for each field in your class.
@AllArgsConstructor
// Spring framework annotation
@Component
public class Book {
 
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    int id;
    @Column(name = "author", nullable = false)
    String author;
    @Column(name = "genre")
    String genre;
    @Column(name = "publisher", nullable = false)
    String publisher;
    @Column(name = "title", nullable = false)
    String title;
    @Column(name = "quantity")
    int quantity;
    @Column(name = "published_on")
    LocalDateTime publishedOn;
}

3.3.3 Data-Access-Object interface

Add the following code to the Dao interface that extends the Crud repository to automatically handle the SQL queries.

BookRepository.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
package com.springboot.lombok.repository;
 
import java.util.List;
 
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
 
import com.springboot.lombok.model.Book;
 
@Repository
public interface BookRepository extends CrudRepository<Book, Integer> {
 
    List<Book> findBookByGenre(String genre);
 
    List<Book> findBookByQuantityGreaterThanEqual(int quantity);
}

3.3.4 Service class

Add the following code to the service class where we will call the methods of the Dao interface to handle the SQL operations.

BookService.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.springboot.lombok.service;
 
import java.util.List;
import java.util.Optional;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
import com.springboot.lombok.model.Book;
import com.springboot.lombok.repository.BookRepository;
 
@Service
public class BookService {
 
    @Autowired
    BookRepository bookRepository;
 
    public void save(final Book book) {
        bookRepository.save(book);
    }
 
    public long getBooksCount() {
        return bookRepository.count();
    }
 
    public Iterable<Book> getAllBooks() {
        return bookRepository.findAll();
    }
 
    public Optional<Book> getBookById(final int bookId) {
        return bookRepository.findById(bookId);
    }
 
    public List<Book> getAllBooksByGenre(final String genre) {
        return bookRepository.findBookByGenre(genre);
    }
 
    public List<Book> getAllBooksByQuantityGreaterThanEqual(final int quantity) {
        return bookRepository.findBookByQuantityGreaterThanEqual(quantity);
    }
}

3.3.5 Boostrapping class

Add the following class to the bootstrapping class designed to load default data into the database on application startup.

DefaultBookLoader.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package com.springboot.lombok.bootstrap;
 
import java.time.LocalDateTime;
import java.time.Period;
import java.util.Random;
 
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
 
import com.github.javafaker.Faker;
import com.springboot.lombok.model.Book;
import com.springboot.lombok.service.BookService;
 
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
 
// Causes Lombok to generate a logger field.
@Slf4j
// Causes Lombok to generate a constructor with 1 parameter for each field that requires special handling.
@RequiredArgsConstructor
@Component
public class DefaultBookLoader implements CommandLineRunner {
 
    private final BookService bookService;
    private final Faker faker;
 
    @Override
    public void run(String... args) {
        loadBooksData();
    }
 
    private void loadBooksData() {
        if (bookService.getBooksCount() == 0) {
            log.info("Saving the default books into the database.");
            for (int x = 0; x < 5; x++) {
                bookService.save(createNewBook());
            }
        } else {
            log.info("Default books are already present in the database.");
        }
    }
 
    private Book createNewBook() {
        final int randomNumber = new Random().nextInt(10 - 5 + 1) + 5;
        return Book.builder()
                .author(faker.book().author())
                .title(faker.book().title())
                .publisher(faker.book().publisher())
                .genre(faker.book().genre())
                .quantity(faker.number().numberBetween(50, 100))
                .publishedOn(LocalDateTime.now().minusHours(randomNumber)
                        .minus(Period.ofWeeks(randomNumber)))
                .build();
    }
}

3.3.6 Exception class

Add the following code to the exception class to throw the not found exception if the book is not found in the database.

BookNotFoundException.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
package com.springboot.lombok.exception;
 
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
 
@ResponseStatus(code = HttpStatus.NOT_FOUND)
public class BookNotFoundException extends RuntimeException {
 
    private static final long serialVersionUID = 1L;
 
    public BookNotFoundException(final String message) {
        super(message);
    }
}

3.3.7 Controller class

Add the following code to the controller class designed to handle the incoming requests. The class is annotated with the @RestController annotation where every method returns a domain object as a JSON response instead of a view.

BookController.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.springboot.lombok.controller;
 
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import com.springboot.lombok.exception.BookNotFoundException;
import com.springboot.lombok.model.Book;
import com.springboot.lombok.service.BookService;
 
import lombok.extern.slf4j.Slf4j;
 
// NOTE - We have left the implementation of the HATEOAS principle for simplicity.
 
// Causes Lombok to generate a logger field.
@Slf4j
@RestController
@RequestMapping(value = "/api/")
public class BookController {
 
    @Autowired
    BookService bookService;
 
    @GetMapping(value = "books")
    public ResponseEntity<List<Book>> getBooks() {
        log.info("Getting all books from the dB.");
        final Iterable<Book> bookIterable = bookService.getAllBooks();
        final List<Book> books = StreamSupport.stream(bookIterable.spliterator(), false).collect(Collectors.toList());
        return new ResponseEntity<>(books, HttpStatus.OK);
    }
 
    @GetMapping(value = "book/id/{id}")
    public ResponseEntity<Book> getBookById(@PathVariable(name = "id") final int bookId) {
        log.info("Getting book with book-id= {} from the dB.", bookId);
        final Book book = bookService.getBookById(bookId)
                .orElseThrow(() -> new BookNotFoundException("Book with id= " + bookId + "not found in the dB."));
        return new ResponseEntity<>(book, HttpStatus.OK);
    }
 
    @GetMapping(value = "book/genre/{genre}")
    public ResponseEntity<List<Book>> getBooksByGenre(@PathVariable(name = "genre") final String genre) {
        log.info("Getting book(s) for genre= {} from the dB.", genre);
        final List<Book> books = bookService.getAllBooksByGenre(genre);
        return new ResponseEntity<>(books, HttpStatus.OK);
    }
 
    @GetMapping(value = "book/quantity/{quantity}")
    public ResponseEntity<List<Book>> getBooksByQuantityGreaterThanEqual(
            @PathVariable(name = "quantity") final int quantity) {
        log.info("Getting book(s) from the dB where quantity is greater-than or equal to= {}.", quantity);
        final List<Book> books = bookService.getAllBooksByQuantityGreaterThanEqual(quantity);
        return new ResponseEntity<>(books, HttpStatus.OK);
    }
 
    // Sample HTTP POST request body.
    /*
    {
        "author": "Vasdev Mohi",
        "genre": "Ghazals",
        "publisher": "Central Sahitya Akademi",
        "title": "Cheque book",
        "quantity": 1,
        "publishedOn": "2020-09-11T11:11:36Z"
    }
    */
    @PostMapping(value = "book/save")
    public ResponseEntity<Void> save(@RequestBody final Book book) {
        log.info("Saving book with details= {} in the dB.", book.toString());
        bookService.save(book);
        return new ResponseEntity<>(HttpStatus.CREATED);
    }
}

4. Run the Application

To execute the application, right-click on the SpringbootAndLombok.java class, Run As -> Java Application.

Spring Boot Lombok - Run the Application
Fig. 2: Run the Application

5. Project Demo

Open the Postman tool and hit the following URLs to display the data in the JSON format.

01
02
03
04
05
06
07
08
09
10
11
12
13
14

That is all for this tutorial and I hope the article served you whatever you were looking for. Happy Learning and do not forget to share!

6. Summary

In this section, we learned:

  • Lombok and its features
  • Using Lombok with a Spring Boot application

You can download the sample application as an Eclipse project in the Downloads section.

7. Download the Eclipse Project

This was an example of Lombok and it’s integration with Spring Boot.

Download
You can download the full source code of this example here: Spring Boot with Lombok

Yatin

An experience full-stack engineer well versed with Core Java, Spring/Springboot, MVC, Security, AOP, Frontend (Angular & React), and cloud technologies (such as AWS, GCP, Jenkins, Docker, K8).
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

2 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Amara
Amara
2 years ago

where is BeanConfiguration class program

Last edited 2 years ago by Amara
Back to top button