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
Feature | Details |
---|---|
val | Local variables are declared as final |
var | Mutable local variables |
@Slf4J | Creates an SLF4J logger |
@Cleanup | Will call close() on the resource in the finally block |
@Getter | Creates getter methods for all properties |
@Setter | Creates setter for all non-final properties |
@EqualsAndHashCode |
|
@ToString |
|
@NoArgsConstructor |
|
@RequiredArgsContructor |
|
@AllArgsConstructor |
|
@Data |
|
@Builder |
|
@Value |
|
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.
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 | < project xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xmlns = "http://maven.apache.org/POM/4.0.0" xsi:schemaLocation = "http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" > < 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; // URL - http://localhost:10093/api/books @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); } // URL - http://localhost:10093/api/book/id/1 @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); } // URL - http://localhost:10093/api/book/save // 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
.
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 | // Create a new book // Get all books // Get the book by id // Get the book by genre // Get the book by quantity |
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.
You can download the full source code of this example here: Spring Boot with Lombok
where is BeanConfiguration class program
you don’t need a bean class here as we have used the lombok annotation. so it will be handled automatically.