Spring Boot Hello World Tutorial with Lombok and H2 Database – Quick Start for Beginners
Learn how to create a Hello World Spring Boot application using Lombok for cleaner code and the H2 in-memory database for rapid development. This step-by-step guide includes annotations, project setup, REST API, H2 console access, and more to kickstart your Spring Boot journey.

Table of Contents
Below is a deep-dive into each of the four Java classes in your “Hello World” Spring Boot project, with an eye toward SOLID design, layering, and best practices.
🔧 pom.xml - Maven dependencies
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>dev.ggorantala</groupId>
<artifactId>hello-lombok-h2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.4</version>
<relativePath/>
</parent>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Web starter for REST endpoints -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- JPA + Hibernate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- In-memory H2 database -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Lombok for getters/setters/etc. -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Enables annotation-processing for Lombok -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.28</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
</project>
📁 Project Structure
hello-world/
├── src/main/java/com/example/helloworld/
│ ├── HelloWorldApplication.java
│ ├── controller/HelloController.java
│ ├── model/User.java
│ ├── repository/UserRepository.java
│ └── data/DataLoader.java
└── src/main/resources/
└── application.properties
🏁 Main Class
package dev.ggorantala.hellolombokh2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloLombokH2Application {
public static void main(String[] args) {
SpringApplication.run(HelloLombokH2Application.class, args);
}
}
Entry point of the application (the “composition root”). Lives in the bootstrap layer: it brings together all other beans and starts the Spring context.
Key Annotations
@SpringBootApplication
=@Configuration
+@EnableAutoConfiguration
+@ComponentScan
.@Configuration
: lets you declare extra@Bean
definitions if needed.@EnableAutoConfiguration
: tells Spring Boot to configure your app based on classpath dependencies (e.g., JPA, Web, H2).@ComponentScan
: scans your package (and subpackages) for Spring stereotypes (@Service
,@Repository
,@Controller
, etc.).
SOLID & Patterns
- Single Responsibility: only boots and wires up the context.
- Open/Closed: adding new beans (services, controllers) doesn’t require modifying this class.
- Dependency Injection: empowers the rest of your code to rely on abstractions rather than manually instantiating objects.
🌐 Simple REST Controller
package dev.ggorantala.hellolombokh2.controller;
import dev.ggorantala.hellolombokh2.model.Greeting;
import dev.ggorantala.hellolombokh2.repository.GreetingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private final GreetingRepository repo;
@Autowired
public GreetingController(GreetingRepository repo) {
this.repo = repo;
}
@GetMapping("/hello")
public String hello() {
// save a new Greeting to H2 and return its message
Greeting g = repo.save(new Greeting(null, "Hello, World!"));
return g.getMessage();
}
}
Web / API layer: handles HTTP requests and returns responses.
Key Annotations
@RestController
: shorthand for@Controller
+@ResponseBody
—methods return data directly in the HTTP body (no view resolution).@GetMapping("/hello")
: maps GET requests for/hello
to this method.@Autowired
on the constructor:- Constructor injection is preferred (over field/setter) for immutability and easier testing.
Method Logic
- Creates and saves a new
Greeting
entity with the text “Hello, World!”. - Returns the persisted
message
(so you see the exact string).
SOLID & Patterns
- Single Responsibility: only handles HTTP mapping and delegates persistence.
- Open/Closed: to add more endpoints, create new methods—no need to touch existing mappings.
- Dependency Inversion: depends on the
GreetingRepository
interface. - Stateless controller: no mutable fields beyond the injected repository, making it thread-safe.
🧍♂️Entity Class with Lombok
package dev.ggorantala.hellolombokh2.model;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Greeting {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String message;
}
Domain model / JPA entity in the persistence layer. Maps to a table (by default named greeting
) in H2.
Key Annotations
@Entity
: marks this class as a managed JPA entity.@Id
+@GeneratedValue
: designatesid
as the primary key with auto-generated values.- Lombok
@Data
: generates getters/setters,equals()
,hashCode()
, and a nicetoString()
.@NoArgsConstructor
/@AllArgsConstructor
: boilerplate constructors for JPA and convenience.
SOLID & Patterns
- Single Responsibility: encapsulates only the persistence fields and behavior of a “Greeting.”
- Encapsulation: fields are private, access via methods generated by Lombok.
- No business logic here—kept lean to respect the Layered Architecture (separate domain/persistence from service/controller).
🗃️ Repository Interface
package dev.ggorantala.hellolombokh2.repository;
import dev.ggorantala.hellolombokh2.model.Greeting;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface GreetingRepository extends JpaRepository<Greeting, Long> { }
Data access abstraction in the repository layer. Hides JPA/Hibernate boilerplate behind well-known CRUD methods.
Key Annotations & Interfaces
@Repository
:- Marks it as a Spring bean for component scanning.
- Translates JPA exceptions into Spring’s
DataAccessException
hierarchy.
- Extends
JpaRepository<Greeting, Long>
: provides- CRUD methods (
save()
,findAll()
,findById()
,delete()
, etc.). - Pagination and sorting out of the box.
- CRUD methods (
SOLID & Patterns
- Implements the Repository Pattern, keeping data operations isolated from business logic.
- Dependency Inversion: higher layers (services/controllers) depend on this abstraction, not on concrete JPA code.
Annotation & Configuration Breakdown
Annotation / Property | What it does |
---|---|
@SpringBootApplication | Combines @Configuration , @EnableAutoConfiguration , and @ComponentScan . Boots the app. |
@Entity | Marks a class as a JPA entity to be persisted in the database. |
@Id | Designates the primary-key field of the entity. |
@GeneratedValue | Configures automatic key generation strategy for the entity. |
@Data (Lombok) | Generates getters/setters, toString() , equals() & hashCode() . |
@NoArgsConstructor (Lombok) | Generates a no-argument constructor. |
@AllArgsConstructor (Lombok) | Generates a constructor with 1 parameter per field. |
@Repository | Indicates a Spring Data repository; exception translation, etc. |
@RestController | Combines @Controller + @ResponseBody . Defines a REST endpoint. |
@GetMapping("/hello") | Maps HTTP GET /hello to this method. |
@Autowired | Injects the GreetingRepository bean into the controller. |
⚙️ application.properties
# H2 in-memory datasource
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
# Show SQL in console (optional)
spring.jpa.show-sql=true
# Enable H2 console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
🔗 H2 Console
- URL: http://localhost:8080/h2-console
- JDBC URL:
jdbc:h2:mem:testdb
- User:
sa
- Password: (leave blank)
Once you start the app and navigate to that URL, you can inspect the GREETING
table and any data you’ve persisted.
How They Fit Together
- Startup: Spring Boot scans your package, finds
@SpringBootApplication
, brings up the context, and auto-configures H2, JPA, Web, and Lombok. - Request: A GET to
http://localhost:8080/hello
hitsGreetingController.hello()
. - Persistence: The controller uses
GreetingRepository
to save a newGreeting
to H2. - Response: Controller returns the persisted message back to the client.
- Inspect DB: You can browse the
GREETING
table via the H2 console at/h2-console
.
Each class plays its own role, keeps responsibilities narrow, and relies on Spring’s inversion-of-control to wire everything neatly—demonstrating strong adherence to SOLID and common design patterns.
Gopi Gorantala Newsletter
Join the newsletter to receive the latest updates in your inbox.