Spring Boot - Notes
Spring Boot - Notes
aven is a build automation and dependency management tool used in Java projects,
M
including Spring Boot applications. It simplifies project setup, builds, and dependency
management.
● anages dependencies (e.g., Spring Boot libraries)
M
● Automates the build process
● Provides a standard project structure
● Handles plugins for testing, packaging, and deployment
●
● If not installed, download and install:
○ Download fromApache Maven
○ Set
MAVEN_HOMEand
PATHenvironment variables.
bash:
y-springboot-app/
m
-- src/main/java
│ # Application source code
-- src/main/resources #
│ Configuration files
(application.properties)
-- src/test/java
│ # Unit tests
-- pom.xml
│ # Maven configuration file
4. Understanding
pom.xml
The
pom.xml(Project Object Model) is the core filein Maven projects.
A basic Spring Boot
pom.xml:
xml:
<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>com.example</groupId>
<
<artifactId>my-springboot-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<dependencies>
<!-- Spring Boot Starter -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
● roupId– Unique identifier (like a package name)
g
● artifactId– Project name
● version– Project version
● dependencies– Required libraries
● build/plugins– Used for packaging and execution
un the application:
R
sh:
mvn spring-boot:run
dd a dependency:
A
Add in
<dependencies>section of
pom.xml
, then run:
sh:
mvn clean install
pdate dependencies:
U
sh:
mvn dependency:resolve
aven simplifies dependency management and project builds in Spring Boot. The
M pom.xml
file is crucial for managing dependencies, plugins, and configurations. Running Maven
commands helps manage project lifecycle efficiently.
Understanding
pom.xmlin Maven
he
T pom.xml(Project Object Model) is the configurationfile in a Maven project. It defines
project details, dependencies, plugins, and build instructions.
xml:
<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>com.example</groupId>
<
<artifactId>my-springboot-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
</project>
<groupId>
Unique identifier for the project (e.g.,
com.example).
<version>
Project version (e.g.,
1.0.0).
<packaging>
Defines the output type (
jaror
war).
3. Adding Dependencies
Dependencies are external libraries required for the project. They are added inside
<dependencies>.
xml:
<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>
</dependencies>
T
● he
<groupId>and<artifactId>specify the dependency.
● The version is automatically resolved from Spring Boot’s parent.
Plugins extend Maven’s functionality. The most common plugin in Spring Boot is:
xml:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
sh:
mvn spring-boot:run
5. Parent POM (Spring Boot Parent)
Instead of manually managing dependency versions, we use Spring Boot’s parent POM:
xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/> <!-- lookup from repository -->
</parent>
xml:
<properties>
<java.version>17</java.version>
</properties>
Usage:
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>${spring.boot.version}</version>
</dependency>
xml:
<profiles>
<profile>
<id>dev</id>
<properties>
<env>development</env>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env>production</env>
</properties>
</profile>
</profiles>
Activate with:
sh:
mvn package -Pdev
8. Final
pom.xmlExample for a Spring Boot App
xml:
<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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
groupId>com.example</groupId>
<
<artifactId>my-springboot-app</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Conclusion
● pom.xmlis the core file of Maven projects.
● It defines dependencies, plugins, and build configurations.
● Spring Boot usesspring-boot-starter-parentto simplify dependency
management.
● Maven profiles help manage different environments.
1. What are Beans in Spring?
Beanis an object managed by the Spring IoC container. It is created, configured, and
A
managed by Spring.
Example of a Bean:
java:
import org.springframework.stereotype.Component;
Component
@
public class MyBean {
public void showMessage() {
System.out.println("Hello from MyBean!");
}
}
B
● ean lifecycle management
● Dependency injection
● Internationalization, event handling, and more.
Here,
ApplicationContextloads beans from the
AppConfigclass.
(a)
@Component
java:
Component
@
public class MyService {
public String getService() {
return "Service called!";
}
}
(b)
@ComponentScan
java:
mport org.springframework.context.annotation.ComponentScan;
i
import org.springframework.context.annotation.Configuration;
@Configuration
ComponentScan(basePackages = "com.example")
@
public class AppConfig {
}
(c)
@SpringBootApplication
Example:
java:
mport org.springframework.boot.SpringApplication;
i
import org.springframework.boot.autoconfigure.SpringBootApplication;
SpringBootApplication
@
public class MyApp {
public static void main(String[] args) {
SpringApplication.run(MyApp.class, args);
}
}
Autowired
@
public Car(Engine engine) {
this.engine = engine;
}
}
etter Injection
S
java:
Component
@
public class Car {
private Engine engine;
Autowired
@
public void setEngine(Engine engine) {
this.engine = engine;
}
}
@Service
Marks a service class (business logic).
@Bean
Used inside
@Configurationto define beans manually.
Qualifie U
@ sed to select a specific bean when multiple implementations
r
exist.
@Primary
Marks a bean as the default choice.
Conclusion
S
● pring Beansare objects managed by the IoC Container.
● TheIoC Container(ApplicationContext) creates and injects dependencies.
● Spring Boot simplifies configuration usingannotationslike
@Component
,
@SpringBootApplication, and
@Autowired
.
● Dependency Injectionreduces coupling and improves testability.
Spring Boot REST API Basics
Spring Boot makes it easy to create REST APIs usingSpring Web. Let's go step by step.
● ET– Retrieve data
G
● POST– Create new data
● PUT– Update existing data
● DELETE– Remove data
2.
@RestControllerAnnotation
RestControlleris aspecialized versionof
@ @Controllerthat combines
@Controllerand@ResponseBody.
Example
java:
mport org.springframework.web.bind.annotation.GetMapping;
i
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
RestController
@
@RequestMapping("/api")
public class HelloController {
GetMapping("/hello")
@
public String sayHello() {
return "Hello, Avinash!";
}
}
@GetMapping
GET Retrieve data
@PostMapping POST
Create new data
@PutMapping
PUT pdate existing
U
data
DeleteMappi DELETE
@ Delete data
ng
4.
@GetMapping– Fetch Data
Used to retrieve resources.
RestController
@
@RequestMapping("/users")
public class UserController {
GetMapping
@
public List<String> getUsers() {
return List.of("Avinash", "John", "Alice");
}
}
● Calling
GET /usersreturns a JSON list.
5.
@PostMapping– Create Data
Used to send data to the server.
RestController
@
@RequestMapping("/users")
public class UserController {
PostMapping
@
public String addUser(@RequestBody String name) {
return "User " + name + " added successfully!";
}
}
6.
@PathVariable– Dynamic URL Parameters
Extractsvalues from the URL.
RestController
@
@RequestMapping("/users")
public class UserController {
GetMapping("/{id}")
@
public String getUser(@PathVariable int id) {
return "User with ID: " + id;
}
}
● GET
/users/101→"User with ID: 101"
7.
@PutMapping– Update Data
Updates existing data.
RestController
@
@RequestMapping("/users")
public class UserController {
PutMapping("/{id}")
@
public String updateUser(@PathVariable int id, @RequestBody
String name) {
return "User " + id + " updated to " + name;
}
}
● PUT
/users/101with"New Name"updates user 101.
8.
@DeleteMapping– Delete Data
Removes a resource.
RestController
@
@RequestMapping("/users")
public class UserController {
DeleteMapping("/{id}")
@
public String deleteUser(@PathVariable int id) {
return "User with ID " + id + " deleted";
}
}
● DELETE
/users/101→"User with ID 101 deleted"
9. Full REST API Example
java:
import org.springframework.web.bind.annotation.*;
mport java.util.ArrayList;
i
import java.util.List;
RestController
@
@RequestMapping("/users")
public class UserController {
GetMapping
@
public List<String> getUsers() {
return users;
}
GetMapping("/{id}")
@
public String getUser(@PathVariable int id) {
return users.get(id);
}
PostMapping
@
public String addUser(@RequestBody String name) {
users.add(name);
return "User " + name + " added successfully!";
}
PutMapping("/{id}")
@
public String updateUser(@PathVariable int id, @RequestBody
String name) {
users.set(id, name);
return "User " + id + " updated to " + name;
}
DeleteMapping("/{id}")
@
public String deleteUser(@PathVariable int id) {
users.remove(id);
return "User " + id + " deleted";
}
}
● GET
/users→ Returns all users.
● GET
/users/1→ Returns"John".
● POST
/userswith"Bob"→ Adds
"Bob"
.
● PUT
/users/1with"New
John"→ Updates
"John".
ELETE
D
● /users/1→ Removes
"John".
P
● ostman– GUI tool for API testing.
● cURL– Command-line tool.
● Swagger– API documentation.
ExamplecURL commands:
sh:
url
c -X GET http://localhost:8080/users
curl
-X POST http://localhost:8080/users -d "Bob"
curl
-X PUT http://localhost:8080/users/1 -d "New Bob"
curl
-X DELETE http://localhost:8080/users/1
Conclusion
REST API (Representational State Transfer API)is a web service that allows
A
communication between a client (frontend, mobile app, etc.) and a server usingHTTP
methods. It followsREST principles, meaning:
S
● tateless– No client data is stored on the server between requests.
● Client-Server Architecture– The frontend and backend are separate.
● Uses HTTP Methods– REST APIs commonly use:
○
GET→ Retrieve data
○ POST→ Create data
○ PUT→ Update data
○ DELETE→ Remove data
2.
@RestControllerAnnotation
@RestControlleris a combination of
@Controllerand
@ResponseBody
.
Example:
java:
import org.springframework.web.bind.annotation.*;
RestController
@
@RequestMapping("/api")
public class HelloController {
GetMapping("/hello")
@
public String sayHello() {
return "Hello, Avinash!";
}
}
GetMapping
@
public List<String> getUsers() {
return List.of("Avinash", "John", "Alice");
}
}
● CallingGET
/usersreturns a JSON list.
4.
@PostMapping– Create Data
Used to send data to the server.
PostMapping
@
public String addUser(@RequestBody String name) {
return "User " + name + " added successfully!";
}
}
5.
@PathVariable– Extract URL Data
Used to getdynamic values from the URL.
Example: Get user by ID
java:
RestController
@
@RequestMapping("/users")
public class UserController {
GetMapping("/{id}")
@
public String getUser(@PathVariable int id) {
return "User with ID: " + id;
}
}
● GET
/users/101→"User with ID: 101"
6.
@PutMapping– Update Data
Used to modify existing resources.
PutMapping("/{id}")
@
public String updateUser(@PathVariable int id, @RequestBody
String name) {
return "User " + id + " updated to " + name;
}
}
● PUT
/users/101with"New Name"updates user 101.
7.
@DeleteMapping– Remove Data
Used to delete resources.
DeleteMapping("/{id}")
@
public String deleteUser(@PathVariable int id) {
return "User with ID " + id + " deleted";
}
}
● DELETE
/users/101→"User with ID 101 deleted"
mport java.util.ArrayList;
i
import java.util.List;
RestController
@
@RequestMapping("/users")
public class UserController {
GetMapping
@
public List<String> getUsers() {
return users;
}
GetMapping("/{id}")
@
public String getUser(@PathVariable int id) {
return users.get(id);
}
PostMapping
@
public String addUser(@RequestBody String name) {
users.add(name);
return "User " + name + " added successfully!";
}
PutMapping("/{id}")
@
public String updateUser(@PathVariable int id, @RequestBody
String name) {
users.set(id, name);
return "User " + id + " updated to " + name;
}
DeleteMapping("/{id}")
@
public String deleteUser(@PathVariable int id) {
users.remove(id);
return "User " + id + " deleted";
}
}
● GET
/users→ Returns all users.
● GET
/users/1→ Returns"John".
● POST
/userswith"Bob"→ Adds
"Bob"
.
● PUT
/users/1with"New
John"→ Updates
"John".
ELETE
D
● /users/1→ Removes
"John".
Conclusion
H
● elps inreducing boilerplate code.
● Improvesdatabase independence.
● Examples of ORM frameworks:Hibernate, JPA, Spring Data JPA.
P
● rovidesannotationslike
@Entity,
@Id
,
@Table
, etc.
● DefinesEntityManagerto interact with the database.
● Can work withany persistence providerlike Hibernate.
Entity
@
@Table(name = "users")
public class User {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Column(nullable = false)
@
private String name;
U
● sesrepositoriesinstead of manually writing queries.
● SupportsQuery Method DSLandcustom queries.
H
● ibernate(most common for MySQL, PostgreSQL, etc.)
● EclipseLink
● OpenJPA
Repository
@
public class UserRepositoryCustom {
PersistenceContext
@
private EntityManager entityManager;
query.select(root).where(cb.equal(root.get("name"), name));
return entityManager.createQuery(query).getResultList();
}
}
C
● riteriaBuilderbuilds queries dynamically.
● Works withcomplex filteringconditions.
MongoDB (NoSQL) with Spring Boot
MongoDB is adocument-based NoSQL database.
java:
mport org.springframework.data.annotation.Id;
i
import org.springframework.data.mongodb.core.mapping.Document;
Document(collection = "users")
@
public class User {
Id
@
private String id;
private String name;
java:
mport org.springframework.data.mongodb.repository.MongoRepository;
i
import java.util.List;
ublic interface UserRepository extends MongoRepository<User,
p
String> {
W
● orks the same as
JpaRepository , but for MongoDB.
● No need for Hibernate(MongoDB does not use ORM).
M
● ongoTemplateis used for dynamic queries.
● Criteria.where("name").is(name)– Works like Hibernate'sCriteria API.
Conclusion
Feature MySQL (Relational DB) MongoDB (NoSQL)
rc/main/java/com/example/demo
s
── controller/
├ (REST APIs)
── service/
├ (Business logic)
── repository/
├ (Database interactions)
── entity/
├ (Database models)
── dao/
├ (Custom queries)
── config/
├ (Configuration classes)
── dto/
├ (Data Transfer Objects)
── exception/
├ (Exception handling)
── util/
├ (Utility classes)
── DemoApplication.java (Main class)
├
Explanation:
Entity
@
@Table(name = "users")
public class User {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Column(nullable = false)
@
private String name;
Document(collection = "users")
@
public class User {
Id
@
private String id;
private String name;
Service
@
public class UserServiceImpl implements UserService {
Override
@
public User saveUser(User user) {
return userRepository.save(user);
}
Override
@
public List<User> getAllUsers() {
return userRepository.findAll();
}
Override
@
public User getUserById(Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("User not
found"));
}
Override
@
public User updateUser(Long id, User user) {
User existingUser = getUserById(id);
existingUser.setName(user.getName());
return userRepository.save(existingUser);
}
Override
@
public void deleteUser(Long id) {
userRepository.deleteById(id);
}
}
RestController
@
@RequestMapping("/users")
public class UserController {
PostMapping
@
public User createUser(@RequestBody User user) {
return userService.saveUser(user);
}
GetMapping
@
public List<User> getAllUsers() {
return userService.getAllUsers();
}
GetMapping("/{id}")
@
public User getUserById(@PathVariable Long id) {
return userService.getUserById(id);
}
PutMapping("/{id}")
@
public User updateUser(@PathVariable Long id, @RequestBody User
user) {
return userService.updateUser(id, user);
}
DeleteMapping("/{id}")
@
public void deleteUser(@PathVariable Long id) {
userService.deleteUser(id);
}
}
{
"name": "Avinash"
}
Updating User
UT /users/1
P
Content-Type: application/json
{
"name": "Updated Name"
}
Deleting User
DELETE /users/1
These codes indicate that the request has been received and is being processed.
● 00 Continue– Server received the request, and the client should continue.
1
●
101 Switching Protocols– Protocol upgrade request is accepted.
● 00
2 OK– Request was successful.
● 201 Created– Resource successfully created (e.g., after
POSTrequest).
● 202 Accepted– Request accepted but processing is not complete.
●
204 No Content– Request successful, but no response body is returned.
These codes indicate that the client must take additional action to complete the request.
These codes indicate that there is an issue with the client’s request.
● 00
4 Bad Request– Invalid request syntax.
● 401
Unauthorized– Authentication is required.
● 403
Forbidden– Server understood but refuses to authorize.
● 404
Not Found– Requested resource does not exist.
● 405
Method Not Allowed– HTTP method is not supported for the requested
resource.
These codes indicate that the server encountered an error while processing the request.
. R
1 esponse Body(Data)
2. HTTP Status Code
3. Headers
Basic Usage
java:
mport org.springframework.http.ResponseEntity;
i
import org.springframework.web.bind.annotation.*;
RestController
@
@RequestMapping("/api")
public class DemoController {
GetMapping("/success")
@
public ResponseEntity<String> successResponse() {
return ResponseEntity.ok("Request was successful!");
}
}
GetMapping("/notfound")
@
public ResponseEntity<String> notFoundResponse() {
return new ResponseEntity<>("Resource not found",
HttpStatus.NOT_FOUND);
}
● Returns
404 Not Found
.
GetMapping("/custom-header")
@
public ResponseEntity<String> customHeaderResponse() {
HttpHeaders headers = new HttpHeaders();
headers.add("Custom-Header", "Value");
GetMapping("/users")
@
public ResponseEntity<List<User>> getUsers() {
List<User> users = List.of(new User(1L, "Avinash"), new User(2L,
"Kumar"));
return ResponseEntity.ok(users);
}
● Returns
200 OKwith a list of users.
Conclusion
✅
ResponseEntity<T>is used to customize responses in Spring Boot REST APIs.
✅
✅
It allows settingHTTP status codes, headers, andresponse bodies.
It is useful forerror handling, sending customheaders, and returning objects.
Lombok in Spring Boot
ombok is a Java library that helps reduce boilerplate code (like getters, setters,
L
constructors, etc.) by using annotations. It is widely used in Spring Boot projects to make the
code more concise and readable.
xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
Example:
java:
mport lombok.Getter;
i
import lombok.Setter;
Getter
@
@Setter
public class User {
private Long id;
private String name;
}
Equivalent Code Without Lombok:
java:
public class User {
private Long id;
private String name;
ublic
p Long getId() { return id; }
public
void setId(Long id) { this.id = id; }
public
String getName() { return name; }
public
void setName(String name) { this.name = name; }
}
B.
@ToString
Generates a
toString()method automatically.
Example:
java:
import lombok.ToString;
ToString
@
public class User {
private Long id;
private String name;
}
java:
Override
@
public String toString() {
return "User{id=" + id + ", name='" + name + "'}";
}
C.
@EqualsAndHashCode
Generates
equals()andhashCode()methods.
Example:
java:
import lombok.EqualsAndHashCode;
EqualsAndHashCode
@
public class User {
private Long id;
private String name;
}
java:
Override
@
public boolean equals(Object o) { /* Implementation */ }
Override
@
public int hashCode() { /* Implementation */ }
D.
@NoArgsConstructor
,
@AllArgsConstructor,
@RequiredArgsConstructor
● NoArgsConstructor→ Generates a no-argument constructor.
@
● @AllArgsConstructor→ Generates a constructor with all fields.
●
@RequiredArgsConstructor→ Generates a constructor for finalfields.
Example:
java:
mport lombok.NoArgsConstructor;
i
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
NoArgsConstructor
@
@AllArgsConstructor
@RequiredArgsConstructor
public class User {
private Long id;
private final String name;
}
E.
@Data
Ashortcut annotationthat includes:
● Getter
@
● @Setter
● @ToString
● @EqualsAndHashCode
● @RequiredArgsConstructor
Example:
java:
import lombok.Data;
Data
@
public class User {
private Long id;
private String name;
}
java:
public class User {
private Long id;
private String name;
ublic
p Long getId() { return id; }
public
void setId(Long id) { this.id = id; }
public
String getName() { return name; }
public
void setName(String name) { this.name = name; }
Override
@
public String toString() { /* Implementation */ }
Override
@
public boolean equals(Object o) { /* Implementation */ }
Override
@
public int hashCode() { /* Implementation */ }
}
F.
@Builder
Generates abuilder patternfor object creation.
Example:
java:
import lombok.Builder;
Builder
@
public class User {
private Long id;
private String name;
}
Usage:
java:
User user = User.builder().id(1L).name("Avinash").build();
G.
@Value
Createsimmutableclasses (like
@Databut with
finalfields and no setters).
Example:
java:
import lombok.Value;
Value
@
public class User {
Long id;
String name;
}
H.
@Slf4j
Generates a
Loggerinstance for logging.
Example:
java:
import lombok.extern.slf4j.Slf4j;
Slf4j
@
public class LoggerExample {
public void logMessage() {
log.info("Logging a message...");
}
}
java:
mport org.slf4j.Logger;
i
import org.slf4j.LoggerFactory;
Steps of Execution:
. L
1 ombok Plugin→ Installed in IDE to support annotations.
2. Annotation Processor→ During compilation, javacinvokesLombok's
processor.
3. AST Modification→ Lombok modifies theJava classbytecodebefore compiling.
4. Bytecode Generation→ The compiler then produces the final .classfile with the
necessary methods.
Example:
●
●
✅ Improvescode maintainability.
Conclusion
ombok simplifies Java development byautomatically generating getters, setters,
L
constructors, logging, and more.
MongoDB Annotations in Spring Boot
hen working withMongoDB in Spring Boot, we often need to create indexes for
W
performance optimization and define relationships between collections.
Two important annotations for this are
@Indexand
@DBRef.
1.
@IndexedAnnotation
@Indexedis used to create anindexon a field in a MongoDB collection.
It helps in improvingquery performance.
Example: Using
@Indexedin a MongoDB Document
java:
mport org.springframework.data.annotation.Id;
i
import org.springframework.data.mongodb.core.index.Indexed;
import org.springframework.data.mongodb.core.mapping.Document;
Document(collection = "users")
@
public class User {
Id
@
private String id;
Explanation:
U
● ser→ Stores user details.
● Orders→ Stores orders placed by users.
Document(collection = "users")
@
public class User {
Id
@
private String id;
Document(collection = "orders")
@
public class Order {
@Id
private String id;
3. How
@DBRefWorks Internally?
● T he @DBRefannotationstores only thereference (ID)of the related document in
MongoDB.
● When retrieving aUser, Spring Bootfetches relatedOrderdocuments lazily(i.e.,
only when required).
json:
/ User Collection
/
{
"_id": "user123",
"name": "Avinash",
"orders": [
{"$ref": "orders", "$id": "order456"},
{"$ref": "orders", "$id": "order789"}
]
}
/ Order Collection
/
{
"_id": "order456",
"productName": "Laptop",
"price": 800.0
}
{
"_id": "order789",
"productName": "Phone",
"price": 500.0
}
4. Querying Data with
@DBRef(Spring Boot Example)
Repository Interface (UserRepository.java)
java:
import org.springframework.data.mongodb.repository.MongoRepository;
Service
@
public class UserService {
Autowired
@
private UserRepository userRepository;
Autowired
@
private OrderRepository orderRepository;
orderRepository.saveAll(Arrays.asList(order1, order2));
Fetching Data
java:
ser user = userRepository.findByName("Avinash");
U
System.out.println(user.getOrders()); // Fetches orders linked to
user
Id
@
private String id;
❌
Avoid
@DBRefwhen:
Y
● ou havelarge collections→ Instead, usemanual referencing(storing only IDs).
● You needhigh-performance queries.
Conclusion
●Indexed→ Improves query performance by creating an index.
@
● @DBRef→ Establishes relationships between MongoDB collections (but can have
erformance drawbacks).
p
Manual Referencing(storing only IDs) is oftenfasterthan
● @DBRef
.
MySQL:
@Indexand Relationship Between Tables Using
@ManyToOne,
@OneToMany
InSpring Boot with MySQL, we useJPA and Hibernateto manage relational databases.
Instead of
@DBRef(used in MongoDB), we define relationshipsusingJPA annotationslike
@ManyToOneand@OneToMany.
1.
@Indexin MySQL
ySQL uses indexes tooptimize query performance. InSpring Data JPA, we can define
M
indexes using
@Indexin the@Tableannotation.
Example: Using
@Indexin MySQL
java:
import jakarta.persistence.*;
Entity
@
@Table(name = "users", indexes = {
@Index(name = "idx_email", columnList = "email", unique = true)
})
public class User {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Explanation:
Entity
@
@Table(name = "users")
public class User {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Entity
@
@Table(name = "orders")
public class Order {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
ManyToOne
@
@JoinColumn(name = "user_id") // Foreign Key
private User user;
Service
@
public class UserService {
Autowired
@
private UserRepository userRepository;
Autowired
@
private OrderRepository orderRepository;
user.setOrders(Arrays.asList(order1, order2));
userRepository.save(user);
}
}
java:
ManyToMany
@
@JoinTable(
name = "user_orders",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "order_id")
)
private List<Order> orders;
@Index
Improve search performance on frequently queried fields.
OneToManyand
@ If one entity can have multiple related records (e.g., one user
@ManyToOne
has multiple orders).
@ManyToMany
hen both entities can have multiple relationships (e.g., users
W
and courses).
Final Thoughts
✅
@Indeximproves MySQL query performance.
✅ UseJPA annotations(
@ManyToOne ,
@OneToMany ) toestablish relationships.
✅ Avoid
@ManyToManyunless required, as it createsaseparate join table.
✅
Spring Boot’sJPA repositoriesmake CRUD operations easy.
CID Principles and Implementing Transactions in
A
Spring Boot
1. Understanding ACID Principles
We will implement a scenario where a user transfers money from one account to another.
Entities: Account
java:
import jakarta.persistence.*;
Entity
@
@Table(name = "accounts")
public class Account {
Id
@
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
We use
@Transactionalto ensureatomicityin the money transfer.
java:
mport org.springframework.beans.factory.annotation.Autowired;
i
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Service
@
public class BankService {
Autowired
@
private AccountRepository accountRepository;
Transactional
@
public void transferMoney(String fromAccount, String toAccount,
double amount) {
Account sender =
accountRepository.findByAccountHolder(fromAccount);
Account receiver =
accountRepository.findByAccountHolder(toAccount);
accountRepository.save(sender);
accountRepository.save(receiver);
}
}
solation.READ_COMMITT P
I revents dirty reads but allows non-repeatable reads.
ED
(Default for most databases)
solation.REPEATABLE_R P
I revents dirty and non-repeatable reads, but allows
EAD
phantom reads.
REQUIRED
Uses the existing transaction or creates a new one.
(default)
REQUIRES_NEW
Suspends existing transaction and creates a new one.
MANDATORY
Requires an existing transaction, else throws an error.
SUPPORTS
uns within a transaction if one exists, else runs
R
without.
NEVER
Throws an exception if a transaction exists.
NESTED
Creates a nested transaction within an existing one.
Example: Using
REQUIRES_NEW
java:
Transactional(propagation = Propagation.REQUIRES_NEW)
@
public void logTransaction(String details) {
// Logs transaction details separately
}
Transactional(rollbackFor =
@ olls back for both checked and
R
Exception.class)
unchecked exceptions.
Transactional(noRollbackFor =
@ revents rollback for specific
P
NullPointerException.class)
exceptions.
Example: Rollback for Checked Exceptions
java:
Transactional(rollbackFor = Exception.class)
@
public void transferMoney(...) { ... }
java:
mport org.springframework.transaction.support.TransactionTemplate;
i
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
Service
@
public class BankService {
Autowired
@
private TransactionTemplate transactionTemplate;
sender.setBalance(sender.getBalance() - amount);
receiver.setBalance(receiver.getBalance() + amount);
accountRepository.save(sender);
accountRepository.save(receiver);
});
}
}
7. Transaction Management in MySQL vs MongoDB
Feature MySQL (RDBMS) MongoDB (NoSQL)
CID
A Fully ACID-compliant CID-compliant in multi-document
A
Compliance transactions
Indexing ses
U @Indexfor Uses
@Indexedannotation
optimization
java:
import org.springframework.transaction.annotation.Transactional;
Transactional
@
public void performMongoDBTransaction() {
// MongoDB operations inside a transaction
}
✅
Handlechecked exceptions carefullyto prevent unwantedrollbacks.
Use Propagation.REQUIRES_NEWforlogging transactions separately.
✅
Optimize database indexesto speed up transactionqueries.
Final Thoughts
A
● CID properties ensure data integrity.
● Spring Boot simplifies transaction management with @Transactional.
● Choosing the right isolation level and propagation type is critical for
performance.
1. Stateless Authentication (JWT)
y default, Spring Security usessession-based authentication, but for stateless
B
authentication (like JWT), wedisable sessionsandrely on tokens.
Key Differences
Feature ession-Based
S Stateless Authentication (JWT)
Authentication
State Server maintains user session No session; authentication info is in JWT
java:
Configuration
@
@EnableWebSecurity
public class SecurityConfig {
Bean
@
public SecurityFilterChain securityFilterChain(HttpSecurity
http) throws Exception {
http.authorizeHttpRequests(auth -> auth
.requestMatchers("/hello").permitAll() // Public
endpoint
.anyRequest().authenticated() // Secure all other
endpoints
)
.formLogin(withDefaults()) // Enable form-based login
.logout(withDefaults()); // Enable logout
return http.build();
}
}
3.2 antMatchers("/hello").permitAll()
Allowsunauthenticatedaccess to
/hello.
3.3 anyRequest().authenticated()
3.5 logout()
A
● ftersuccessful authentication, an HTTPsession iscreated.
● Your authentication details are stored in this session.
T
● he server sends aJSESSIONIDcookie to the browser.
● The browser sends thiscookie backin subsequent requests.
3. SecurityContext
● U
singJSESSIONID, Spring Securityretrieves authentication detailsfor every
request.
S
● essionsexpireafter a configured timeout (default is30 minutes).
● Users arelogged outafter inactivity.
A
● llows users to staylogged in even after sessiontimeout.
● Uses along-lived persistent cookie.
5. Stateless Authentication (JWT)
Instateless authentication, we don’t use sessions. Instead, we rely onJSON Web Tokens
(JWT).
Add dependencies:
xml:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
import java.io.IOException;
Override
@
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain chain)
throws ServletException, IOException {
chain.doFilter(request, response);
}
}
Strategy Behavior
ALWAYS
Always creates a session (default)
Final Summary
Feature Session-Based Authentication tateless (JWT)
S
Authentication
. S
1 tore roles in the database.
2. Assign roles to users.
3. Restrict accessbased on roles.
Entity
@
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
ManyToMany(fetch = FetchType.EAGER)
@
@JoinTable(
name = "user_roles",
joinColumns = @JoinColumn(name = "user_id"),
inverseJoinColumns = @JoinColumn(name = "role_id")
)
private Set<Role> roles;
Entity
@
@Table(name = "roles")
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
java:
mport org.springframework.security.core.userdetails.*;
i
import
org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.stream.Collectors;
@Service
ublic class CustomUserDetailsService implements UserDetailsService
p
{
private final UserRepository userRepository;
Override
@
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new
UsernameNotFoundException("User not found"));
return new
org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
user.getRoles().stream()
.map(role -> new
SimpleGrantedAuthority(role.getName()))
.collect(Collectors.toSet())
);
}
}
EnableWebSecurity
@
public class SecurityConfig {
Bean
@
public SecurityFilterChain securityFilterChain(HttpSecurity
http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests(auth -> auth
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/user/**").hasRole("USER")
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.logout(withDefaults());
return http.build();
}
Bean
@
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
admin/* ROLE_ADMIN
/
*
/user/** ROLE_USER
● asRole("ADMIN")→ Requires
h ROLE_ADMIN
● hasRole("USER")→ Requires
ROLE_USER
●
anyRequest().authenticated()→ All other URLs requireauthentication
{
"username": "admin",
"password": "admin123"
}
→ Response:200 OK
✅
Allowedif user has
ROLE_ADMIN.
✅
Allowedif user has
ROLE_USER.
8. Conclusion
✔
Users & Roles stored in DB
✔Spring Security restricts endpoints based on roles
✔JWT or Session can be used for authentication
omparison of
C application.properties ,
application.yml ,
Command-Line Properties, and Environment Variables in Spring Boot
pring Boot provides multiple ways to configure properties. Let's compare these
S
approaches:
.
1 application.properties(Traditional Key-Value
Format)
● Stored in:
src/main/resources/application.properties
● Format:
key=value
xample:
E
properties:
erver.port=8081
s
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret
●
✅
Pros:
S
● imple and widely used.
● Default format in Spring Boot.
❌
Cons:
N
● o hierarchy (difficult for nested properties).
● Can become lengthy for complex configurations.
2.
application.yml(YAML Format)
S
● tored in:
src/main/resources/application.yml
● Format:Hierarchical (indentation-based)
xample:
E
yaml:
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: secret
●
✅
Pros:
M
● ore readable (especially for complex configurations).
● Supportsnested structuresbetter than
.properties .
❌
Cons:
Y
● AML syntax is indentation-sensitive.
● Harder to debug if indentation errors occur.
●
✅
Pros:
O
● verridesboth
application.propertiesand application.yml.
● Useful for temporary changes without modifying files.
❌
Cons:
N
● ot persistent.
● Hard to manage for complex configurations.
●
✅
Pros:
O
● verridesall other configurations (
properties,
YAML
, etc.).
● Best forproduction environments(e.g., AWS, Kubernetes).
● Secure (sensitive values like passwords don’t get committed in code).
❌
Cons:
H
● arder to track changes.
● Not project-specific (affects all applications running in the environment).
🔹 Best Practices
.
1 se
U application.ymlfor structured configuration.
2. Override with environment variablesin production.
3. Use command-line argumentsfor temporary overrides.
4. Avoid hardcoding passwordsin propertiesor yml(use environment variables
instead).
Unit is a popular testing framework for Java, and in Spring Boot, we use JUnit along with
J
Spring's testing support for unit and integration testing.
1️⃣ JUnit Basics
JUnit provides annotations and assertions for writing test cases.
@Test
Marks a method as a test case.
@BeforeEach
Runs before each test case (like setup).
@AfterEach
Runs after each test case (like cleanup).
@BeforeAll
uns once before all test cases (static
R
method).
@AfterAll
Runs once after all test cases (static method).
DisplayName("Custom
@ Provides a readable name for test cases.
Name")
@Disabled
Skips a test method.
Assertions in JUnit
Assertion Purpose
assertEquals(expected, actual)
Checks if values are equal.
assertNotEquals(val1, val2)
hecks if values are not
C
equal.
assertTrue(condition)
Ensures condition is true.
assertFalse(condition)
Ensures condition is false.
assertNull(object)
Checks if an object is null.
assertNotNull(object)
Checks if an object is not null.
ssertThrows(Exception.class, () -> {
a Ensures exception is thrown.
code })
Test
@
void testAddition() {
int sum = 5 + 3;
assertEquals(8, sum, "Addition test failed");
}
Test
@
void testDivisionByZero() {
assertThrows(ArithmeticException.class, () -> {
int result = 10 / 0;
});
}
}
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
mport
i org.junit.jupiter.api.BeforeEach;
import
org.junit.jupiter.api.Test;
import
org.mockito.InjectMocks;
import
org.mockito.Mock;
import
org.mockito.MockitoAnnotations;
mport com.example.service.UserService;
i
import com.example.repository.UserRepository;
import com.example.model.User;
import java.util.Optional;
class UserServiceTest {
Mock
@
private UserRepository userRepository;
InjectMocks
@
private UserService userService;
BeforeEach
@
void setUp() {
MockitoAnnotations.openMocks(this);
}
Test
@
void testFindUserById() {
User user = new User(1L, "Avinash");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
mport org.junit.jupiter.api.Test;
i
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)
@
class UserControllerTest {
Autowired
@
private MockMvc mockMvc;
Test
@
void testGetUser() throws Exception {
mockMvc.perform(get("/users/1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.name").value("Avinash"));
}
}
✅
Key Takeaways:
● @WebMvcTestloads only the web layer (lightweight).
● M ockMvcallows us to test REST APIs.
●
jsonPath("$.name")checks JSON response fields.
SpringBootTest
@
class ApplicationTests {
Test
@
void contextLoads() {
assertThat(true).isTrue();
}
}
✅
@SpringBootTestloads the whole application for testing.
ockito is a powerful mocking framework used in unit testing to simulate dependencies. It
M
helps in testing components independently by replacing real dependencies with mock
objects.
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
@Mock
Creates a mock object.
@InjectMocks
Injects mock dependencies into the class under
test.
@Spy
Creates a spy (partial mock).
@Captor
Captures method arguments for verification.
java:
mport static org.mockito.Mockito.*;
i
import static org.junit.jupiter.api.Assertions.*;
mport
i org.junit.jupiter.api.BeforeEach;
import
org.junit.jupiter.api.Test;
import
org.mockito.InjectMocks;
import
org.mockito.Mock;
import
org.mockito.MockitoAnnotations;
import
java.util.Optional;
class UserServiceTest {
Mock
@
private UserRepository userRepository;
InjectMocks
@
private UserService userService;
BeforeEach
@
void setUp() {
MockitoAnnotations.openMocks(this);
}
Test
@
void testFindUserById() {
User user = new User(1L, "Avinash");
when(userRepository.findById(1L)).thenReturn(Optional.of(user));
👉
Returns a
Userobject whenfindById(1L)is called.
Verification Explanation
erify(mock,
v Ensures method was called once.
times(1))
erify(mock,
v nsures method was called at least
E
atLeast(2))
twice.
7️⃣ Throwing Exceptions withthenThrow()
ssertThrows(RuntimeException.class, () ->
a
userService.findUserById(999L));
👉
Ensures an exception is thrown for invalid user IDs.
Example: Using
@Spy
java:
Spy
@
private UserService userService;
Test
@
void testSpy() {
doReturn("Mocked Name").when(userService).getUserName();
assertEquals("Mocked Name", userService.getUserName());
}
Test
@
void testArgumentCaptor() {
userService.findUserById(1L);
verify(userRepository).findById(idCaptor.capture());
assertEquals(1L, idCaptor.getValue());
}
👉
Ensures the correct argument was passed.
👉
Temporarily overrides static method behavior.
pring Profiles allow us to define different configurations for different environments like
S
development (dev), testing (test), and production (prod). We can switch between these
profiles easily.
. a
1 pplication.properties / application.yml
2. Command-line arguments
3. Environment variables
application-dev.properties
properties:
erver.port=8081
s
spring.datasource.url=jdbc:mysql://localhost:3306/devdb
spring.datasource.username=dev_user
spring.datasource.password=dev_pass
application-test.properties
properties:
erver.port=8082
s
spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=test_user
spring.datasource.password=test_pass
application-prod.properties
properties:
erver.port=8080
s
spring.datasource.url=jdbc:mysql://prod-db-server:3306/proddb
spring.datasource.username=prod_user
spring.datasource.password=prod_pass
3️⃣ Activating a Profile
We can activate a profile in different ways:
A. In
application.properties
properties:
spring.profiles.active=dev
java:
Component
@
@Profile("dev")
public class DevConfig {
public DevConfig() {
System.out.println("Dev Configuration Loaded");
}
}
java:
Component
@
@Profile("prod")
public class ProdConfig {
public ProdConfig() {
System.out.println("Prod Configuration Loaded");
}
}
👉
When we run the app with
spring.profiles.active=prod, only the
ProdConfig
bean will be loaded.
java:
Value("${spring.datasource.url}")
@
private String dbUrl;
application-test.properties
properties:
pring.datasource.url=jdbc:h2:mem:testdb
s
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
👉
This allows running tests without an actual database.
pring Boot provides built-in support forLogback,Log4j2, andJava Util Logging (JUL).
S
By default,Logbackis used, but we can configuredifferent logging frameworks based on
our needs.
● RACE– Very detailed information, mostly for debugging.
T
● DEBUG– Detailed information for debugging.
● INFO– General application-level events.
● WARN– Potential issues or non-critical failures.
● ERROR– Serious problems that may prevent execution.
java:
mport org.slf4j.Logger;
i
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
Service
@
public class LoggingService {
private static final Logger logger =
LoggerFactory.getLogger(LoggingService.class);
properties:
Set the logging level
#
logging.level.root=WARN
logging.level.com.example=DEBUG
This means:
properties:
Log to a file
#
logging.file.name=logs/app.log
logging.file.path=logs
xml:
<configuration>
<fileNamePattern>logs/app-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>7</maxHistory>
</rollingPolicy>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level
%logger{36} - %msg%n</pattern>
</encoder>
</appender>
</configuration>
L
● ogs are printed to bothconsoleandfile(
logs/app.log).
● Log files arerotated dailyand stored for7 days.
5️⃣ Switching to Log4j2
To use Log4j2 instead of Logback:
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
<scope>runtime</scope>
</dependency>
2. Create
src/main/resources/log4j2.xml
:
xml:
<Configuration status="WARN">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} [%t]
%-5level %c{1} - %msg%n"/>
</Console>
<RollingFile name="FileLogger" fileName="logs/app.log"
filePattern="logs/app-%d{yyyy-MM-dd}.log">
<PatternLayout>
<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%t] %-5level %c{1}
- %msg%n</Pattern>
</PatternLayout>
<Policies>
<TimeBasedTriggeringPolicy />
</Policies>
</RollingFile>
</Appenders>
<Loggers>
<Root level="INFO">
<AppenderRef ref="Console"/>
<AppenderRef ref="FileLogger"/>
</Root>
</Loggers>
</Configuration>
6️⃣ Using Java Util Logging (JUL)
JUL is the default logging framework in Java but is rarely used in Spring Boot.
properties:
andlers=java.util.logging.ConsoleHandler
h
.level=INFO
ava.util.logging.ConsoleHandler.level=FINE
j
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleF
ormatter
✅
Store logs infilesinstead of just the consolefor troubleshooting.
✅
Uselog rotationto avoid excessive log file sizes.
✅
Don't log sensitive informationlike passwords.
UseMDC (Mapped Diagnostic Context)to track logs for each request in a distributed
system.
SonarQube, SonarLint, and SonarCloud
onarQube, SonarLint, and SonarCloud are tools used forstatic code analysisto improve
S
code quality,security, andmaintainabilityin software development.
1️⃣ SonarQube
SonarQube is aself-hostedcode quality analysis tool that scans code for:
● ugs
B
● Security vulnerabilities
● Code smells (bad coding practices)
● Code coverage
● Duplications
.
1 evelopers commit codeto a repository.
D
2. Sonar Scanneranalyzes the code.
3. SonarQube Serverprocesses the analysis results.
4. Developers review and fix issuesvia the SonarQubeDashboard.
○
○ Accesshttp://localhost:9000in a browser.
2. Add SonarQube Plugin to Spring Boot Project ( pom.xml)
xml:
<properties>
<sonar.projectKey>your-project-key</sonar.projectKey>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.sonarsource.scanner.maven</groupId>
<artifactId>sonar-maven-plugin</artifactId>
<version>3.9.1.2184</version>
</plugin>
</plugins>
</build>
un the Analysis
R
sh:
mvn clean verify sonar:sonar -Dsonar.host.url=http://localhost:9000
3.
○ Results are available inSonarQube Dashboard.
Key Features
🔹
SonarLint vs. SonarQube
eal-time
R ✅ Yes ❌ No (post-commit analysis)
Analysis
CI/CD Integration ❌
No ✅
Yes
3️⃣ SonarCloud
✔️
onarCloud isSonarQube’s cloud-based version. It provides:
S
✔️
Automatic code analysisin cloud-based CI/CD pipelines.
✔️
Supports GitHub, Bitbucket, GitLab, and Azure DevOps.
✔️
No need to install and manage servers(unlike SonarQube).
Free for open-source projectsbut requires a paidplan for private projects.
. C
1 reate an accountatSonarCloud.io.
2. Link your GitHub repository.
3. Add GitHub Actions workflow ( .github/workflows/sonarcloud.yml
):
yaml:
name: SonarCloud Analysis
on:
push:
branches:
- main
jobs:
sonar:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'
- name: Cache SonarCloud Packages
uses: actions/cache@v3
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
- name: Run SonarCloud Analysis
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: mvn clean verify sonar:sonar
-Dsonar.projectKey=your_project_key
-Dsonar.organization=your_organization
4️⃣
Comparison: SonarQube vs. SonarLint vs.
SonarCloud
Feature SonarQube SonarLint SonarCloud
(Self-hosted) (IDE) (Cloud-based)
eal-time
R ❌ No ✅ Yes ❌ No
Feedback
erver
S ✅
Yes ❌
No ❌
No (managed by
Maintenance Sonar)
Integrating external APIs in a Spring Boot application involves making HTTP requests to
✔️
external services. This can be done using:
RestTemplate(Deprecated, but still used in older projects)
✔️
WebClient(Recommended for modern applications)
Using
RestTemplate(Traditional Approach)
java:
mport org.springframework.stereotype.Service;
i
import org.springframework.web.client.RestTemplate;
import org.springframework.http.ResponseEntity;
Service
@
public class ExternalApiService {
private final RestTemplate restTemplate = new RestTemplate();
✔ getForEntity(url, responseType.class)→ Fetches the response along with
HTTP status.
✔
getForObject(url, responseType.class)→ Directlyfetches the response body.
Using
WebClient(Recommended)
java:
mport org.springframework.stereotype.Service;
i
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
@Service
public class ExternalApiService {
private final WebClient webClient = WebClient.create();
✔
retrieve()→ Fetches the response.
✔
bodyToMono(String.class)→ Converts response to
Mono<String>.
✔
.block()→ Converts reactive response to a synchronousone.
Using
RestTemplate
java:
mport
i org.springframework.http.HttpEntity;
import
org.springframework.http.HttpHeaders;
import
org.springframework.http.MediaType;
import
org.springframework.stereotype.Service;
import
org.springframework.web.client.RestTemplate;
mport java.util.HashMap;
i
import java.util.Map;
Service
@
public class ExternalApiService {
private final RestTemplate restTemplate = new RestTemplate();
✔ postForObject(url, request, responseType.class)→ Sends a POST
request and retrieves the response.
Using
WebClient
java:
mport
i org.springframework.stereotype.Service;
import
org.springframework.web.reactive.function.client.WebClient;
import
reactor.core.publisher.Mono;
import
java.util.Map;
Service
@
public class ExternalApiService {
private final WebClient webClient = WebClient.create();
✔
.bodyValue(requestBody)→ Sends request body as JSON.
✔
.retrieve()→ Fetches response.
RestController
@
@RequestMapping("/api")
public class ExternalApiController {
private final ExternalApiService apiService;
GetMapping("/fetch")
@
public String fetchExternalData() {
return apiService.fetchData();
}
PostMapping("/send")
@
public String sendExternalData() {
return apiService.sendData();
}
}
●
OST Request:
P
sh:
curl -X POST http://localhost:8080/api/send
●
5️⃣ Choosing BetweenRestTemplateandWebClient
Feature estTemplate
R WebClient (Recommended)
(Deprecated)
Synchronous ✅ Yes
✅ Yes (with.block())
eactive
R ❌
No ✅
Yes (supports Reactive
Support Streams)
Future Proof ❌
No (Deprecated) ✅ Yes
In Spring Boot, we can interact with MySQL usingSpring Data JPA. The two primary ways
to query data are:
. S
1 pring Data JPA Query Methods (Derived Queries)
2. Criteria API (For Dynamic Queries)
Entity
@
@Table(name = "employees")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Creating Repository
java:
mport org.springframework.data.jpa.repository.JpaRepository;
i
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
ublic interface EmployeeRepository extends JpaRepository<Employee,
p
Long> {
List<Employee> findByDepartment(String department);
List<Employee> findBySalaryGreaterThan(int salary);
Employee findByName(String name);
List<Employee> findByDepartmentAndSalaryGreaterThan(String
department, int salary);
}
Usage in Service
java:
mport org.springframework.stereotype.Service;
i
import java.util.List;
Service
@
public class EmployeeService {
private final EmployeeRepository employeeRepository;
RestController
@
@RequestMapping("/employees")
public class EmployeeController {
private final EmployeeService employeeService;
GetMapping("/by-department/{dept}")
@
public List<Employee> getByDepartment(@PathVariable String dept)
{
return employeeService.getEmployeesByDepartment(dept);
}
GetMapping("/high-salary/{salary}")
@
public List<Employee> getHighEarners(@PathVariable int salary) {
return employeeService.getHighEarningEmployees(salary);
}
}
Using
CriteriaBuilderto Build Dynamic Queries
java:
mport
i jakarta.persistence.criteria.*;
import
org.springframework.stereotype.Service;
import
jakarta.persistence.EntityManager;
import
jakarta.persistence.PersistenceContext;
import
java.util.List;
Service
@
public class EmployeeCriteriaService {
@PersistenceContext
private EntityManager entityManager;
query.select(root).where(cb.greaterThan(root.get("salary"),
salary));
return entityManager.createQuery(query).getResultList();
}
ublic List<Employee>
p
findEmployeesByDepartmentAndMinSalary(String department, int salary)
{
CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Employee> query =
cb.createQuery(Employee.class);
Root<Employee> root = query.from(Employee.class);
return entityManager.createQuery(query).getResultList();
}
}
RestController
@
@RequestMapping("/employees/criteria")
public class EmployeeCriteriaController {
private final EmployeeCriteriaService employeeCriteriaService;
ublic EmployeeCriteriaController(EmployeeCriteriaService
p
employeeCriteriaService) {
this.employeeCriteriaService = employeeCriteriaService;
}
GetMapping("/high-salary/{salary}")
@
public List<Employee> getBySalary(@PathVariable int salary) {
return
employeeCriteriaService.findEmployeesWithSalaryGreaterThan(salary);
}
GetMapping("/by-department/{dept}/min-salary/{salary}")
@
public List<Employee> getByDeptAndSalary(@PathVariable String
dept, @PathVariable int salary) {
return
employeeCriteriaService.findEmployeesByDepartmentAndMinSalary(dept,
salary);
}
}
4️⃣ Summary
Method Use Case
erived
D Simple queries like
findByName()or
findByDepartment()
Queries
JPQL (
@Query
) Custom queries using entity names (
SELECT e FROM Employee
e)
Native Queries When you need full control over SQL syntax (
SELECT
* FROM
employees
)
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
properties:
pring.mail.host=smtp.gmail.com
s
spring.mail.port=587
spring.mail.username=your_email@gmail.com
spring.mail.password=your_app_password
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
RestController
@
@RequestMapping("/email")
public class EmailController {
private final EmailService emailService;
OST http://localhost:8080/email/send
P
Params:
- `to`: recipient@example.com
- `subject`: Test Email
- `text`: Hello, this is a test email!
java:
mport org.springframework.core.io.FileSystemResource;
i
import java.io.File;
mailSender.send(message);
System.out.println("Email with attachment sent
successfully!");
} catch (MessagingException e) {
e.printStackTrace();
}
}
Conclusion
✅
his setup allows sending:
T
✅
Plain text emails
✅
HTML emails
Emails with attachments
Efficient Task Scheduling in Spring Boot Using Cron Jobs
pring Boot provides a built-in mechanism for scheduling tasks using the
S @Scheduled
annotation. You can schedule tasks usingfixed delays,fixed rates, or cron expressions.
xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
java:
mport org.springframework.boot.SpringApplication;
i
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
SpringBootApplication
@
@EnableScheduling // Enables scheduling tasks
public class SchedulerApplication {
public static void main(String[] args) {
SpringApplication.run(SchedulerApplication.class, args);
}
}
3️⃣ Creating Scheduled Tasks in Spring Boot
Spring Boot provides three scheduling types:
. F
1 ixed Delay(Runs after the previous execution completes)
2. Fixed Rate(Runs at a fixed interval, independentof execution time)
3. Cron Expressions(Runs at specified times)
java:
mport org.springframework.scheduling.annotation.Scheduled;
i
import org.springframework.stereotype.Component;
Component
@
public class FixedDelayTask {
@Scheduled(fixedDelay = 5000) // Runs 5 seconds after the
previous execution completes
public void executeTask() {
System.out.println("Fixed delay task executed at: " +
System.currentTimeMillis());
}
}
java:
Component
@
public class FixedRateTask {
@Scheduled(fixedRate = 5000) // Runs every 5 seconds,
regardless of previous execution
public void executeTask() {
System.out.println("Fixed rate task executed at: " +
System.currentTimeMillis());
}
}
C) Cron Expression Scheduling
java:
Component
@
public class CronTask {
@Scheduled(cron = "0 0 9 * * *") // Runs daily at 9 AM
public void executeTask() {
System.out.println("Cron task executed at: " +
System.currentTimeMillis());
}
}
"0 * * * * *"
Every minute
"0 0 * * * *"
Every hour
"0 0 9 * * *"
Every day at 9 AM
0 0 9 * *
" Every weekday at 9 AM
MON-FRI"
"0 30 9 * * *"
Every day at9:30 AM
"0 0 9 1 * *"
First dayof every month at 9 AM
"0 0 9 ? * 6"
EverySaturday at 9 AM
5️⃣ Scheduling Tasks Dynamically
ometimes, you need to change the schedulewithout restartingthe app. Store the cron
S
expression in
application.properties:
properties:
scheduler.cron=0 0 9 * * *
java:
mport org.springframework.beans.factory.annotation.Value;
i
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
Component
@
public class DynamicCronTask {
@Value("${scheduler.cron}")
private String cronExpression;
java:
mport java.util.concurrent.Executors;
i
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
SpringBootApplication
@
@EnableAsync // Enables async task execution
public class SchedulerApplication {
}
se
U @Asyncfor non-blocking execution:
java:
mport org.springframework.scheduling.annotation.Async;
i
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
Component
@
public class AsyncTaskScheduler {
@Async
Scheduled(fixedRate = 5000)
@
public void executeTask() throws InterruptedException {
System.out.println("Executing async task...");
Thread.sleep(7000); // Simulate long execution
System.out.println("Async task completed.");
}
}
✅UseScheduledExecutorServicefor high-performancescheduling
Conclusion
pring Boot makestask scheduling easyusing the
S @Scheduledannotation. Whether
usingfixed delay, fixed rate, or cron expressions,you can efficiently manage background
jobs in your application.
Signup, Login, and Logout using JWT Authentication in Spring Boot
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
2️⃣ Configure Database inapplication.properties
For demonstration, we use an in-memoryH2 database:
properties:
pring.datasource.url=jdbc:h2:mem:testdb
s
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.h2.console.enabled=true
java:
mport jakarta.persistence.*;
i
import lombok.*;
Getter
@
@Setter
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Column(nullable = false)
@
private String password;
Column(nullable = false)
@
private String role; // Example: ROLE_USER, ROLE_ADMIN
}
4️⃣ Create User Repository (UserRepository.java)
java:
mport org.springframework.data.jpa.repository.JpaRepository;
i
import java.util.Optional;
java:
mport
i io.jsonwebtoken.*;
import
io.jsonwebtoken.security.Keys;
import
org.springframework.stereotype.Component;
import
java.security.Key;
import
java.util.Date;
Component
@
public class JwtUtil {
private static final String SECRET_KEY =
"mysecretkeymysecretkeymysecretkey123456"; // Use 256-bit key
private static final long EXPIRATION_TIME = 86400000; // 1 day
in milliseconds
wts.parserBuilder().setSigningKey(key).build().parseClaimsJws(token
J
);
return true;
} catch (JwtException e) {
return false;
}
}
}
Service
@
public class AuthService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
private final JwtUtil jwtUtil;
7️⃣
Create Authentication Controller
(
A uthController.java
)
java:
import org.springframework.web.bind.annotation.*;
RestController
@
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
PostMapping("/signup")
@
public String signUp(@RequestParam String username,
@RequestParam String password, @RequestParam String role) {
return authService.signUp(username, password, role);
}
PostMapping("/login")
@
public String login(@RequestParam String username, @RequestParam
String password) {
return authService.login(username, password);
}
}
Configuration
@
public class SecurityConfig {
Bean
@
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
Bean
@
public AuthenticationManager
authenticationManager(AuthenticationConfiguration configuration)
throws Exception {
return configuration.getAuthenticationManager();
}
}
Request:
POST /auth/signup
json:
{
"username": "john",
"password": "password123",
"role": "ROLE_USER"
}
Response:
"User registered successfully!"
Request:
POST /auth/login
json:
{
"username": "john",
"password": "password123"
}
Response:
json:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
📌 Protecting Endpoints
java:
import org.springframework.web.bind.annotation.*;
RestController
@
@RequestMapping("/user")
public class UserController {
GetMapping("/profile")
@
public String getProfile() {
return "User Profile Data";
}
}
java:
mport
i
org.springframework.security.config.annotation.web.builders.HttpSecu
rity;
import
org.springframework.security.config.annotation.web.configuration.Ena
bleWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import
org.springframework.security.web.authentication.UsernamePasswordAuth
enticationFilter;
EnableWebSecurity
@
public class SecurityConfig {
Bean
@
public SecurityFilterChain securityFilterChain(HttpSecurity
http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.requestMatchers("/auth/signup",
"/auth/login").permitAll()
.anyRequest().authenticated();
return http.build();
}
}
🔚 Conclusion
✅ ImplementedJWT authentication
✅ Builtsignup, login, and logoutfunctionality
In astateless JWT authentication system, logging out is different from traditional
session-based authentication. Since JWT tokens areself-contained, the server does not
store them, so we cannot "invalidate" a token in a database directly. However, we have
several ways to implement logout effectively.
S
● tore the JWT token in adatabase (Redis or SQL) or an in-memory blacklist.
● Check if the token isblacklistedbefore processinga request.
● Remove the token from the list when it expires.
java:
mport org.springframework.stereotype.Service;
i
import java.util.HashSet;
import java.util.Set;
Service
@
public class TokenBlacklistService {
private final Set<String> blacklistedTokens = new HashSet<>();
public void blacklistToken(String token) {
blacklistedTokens.add(token);
}
RestController
@
@RequestMapping("/auth")
public class AuthController {
private final AuthService authService;
private final TokenBlacklistService tokenBlacklistService;
PostMapping("/logout")
@
public String logout(HttpServletRequest request) {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
tokenBlacklistService.blacklistToken(token);
return "Logout successful!";
}
return "Invalid token!";
}
}
Step 3: Check Blacklist in JWT Filter (
JwtFilter.java)
Modify theJWT filterto check if the token is blacklisted.
java:
mport io.jsonwebtoken.ExpiredJwtException;
i
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import
org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import
org.springframework.security.web.authentication.WebAuthenticationDet
ailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.GenericFilterBean;
import java.io.IOException;
Component
@
public class JwtFilter extends GenericFilterBean {
private final JwtUtil jwtUtil;
private final TokenBlacklistService tokenBlacklistService;
Override
@
public void doFilter(ServletRequest request, ServletResponse
response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest)
request;
String authHeader = httpRequest.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer "))
{
String token = authHeader.substring(7);
try {
String username = jwtUtil.extractUsername(token);
if (username != null &&
SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = new
CustomUserDetailsService().loadUserByUsername(username);
if (jwtUtil.validateToken(token)) {
UsernamePasswordAuthenticationToken
authentication =
new
UsernamePasswordAuthenticationToken(userDetails, null,
userDetails.getAuthorities());
authentication.setDetails(new
WebAuthenticationDetailsSource().buildDetails(httpRequest));
ecurityContextHolder.getContext().setAuthentication(authentication)
S
;
}
}
} catch (ExpiredJwtException e) {
SecurityContextHolder.clearContext();
}
}
chain.doFilter(request, response);
}
}
java:
mport org.springframework.context.annotation.Bean;
i
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.web.builders.HttpSecu
rity;
import org.springframework.security.web.SecurityFilterChain;
import
org.springframework.security.web.authentication.UsernamePasswordAuth
enticationFilter;
Configuration
@
public class SecurityConfig {
Bean
@
public SecurityFilterChain securityFilterChain(HttpSecurity
http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests()
.requestMatchers("/auth/signup",
"/auth/login").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(jwtFilter,
UsernamePasswordAuthenticationFilter.class);
return http.build();
}
}
3️⃣ Testing Logout API
🔹 Step 1: Sign Up a User
POST
/auth/signup
json:
{
"username": "john",
"password": "password123",
"role": "ROLE_USER"
}
✅
Response:
"User registered successfully!"
POST
/auth/login
json:
{
"username": "john",
"password": "password123"
}
✅
Response:
json:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
ET
G /user/profile
Header:
Authorization: Bearer <JWT_TOKEN>
✅
Response:
"User Profile Data"
🔹 Step 4: Logout
OST
P /auth/logout
Header:
Authorization: Bearer <JWT_TOKEN>
✅
Response:
"Logout successful!"
ET
G /user/profile
Header:
Authorization: Bearer <JWT_TOKEN>
❌
Response:
"Invalid Token!"(Token is blacklisted)
R
● emove the token from LocalStorage or Cookies.
● The next request will beunauthenticatedbecause notoken is sent.
javascript:
/ Frontend Logout
/
localStorage.removeItem("jwtToken");
🔚 Conclusion
✅
✅
Implemented secure JWT login and logout
✅
Blacklisted tokens on logout
Ensured API protection
Hiding API Keys Using
@Valuein Spring Boot
Toavoid hardcodingAPI keys in your Java code, we can store them in the
application.properties(orapplication.yml
) file andinject them using
@Value
.
properties:
api.key=your-secret-api-key
Alternatively, if using
application.yml:
yaml:
api:
key: your-secret-api-key
java:
mport org.springframework.beans.factory.annotation.Value;
i
import org.springframework.stereotype.Service;
Service
@
public class ApiService {
Value("${api.key}")
@
private String apiKey;
Linux/Mac (Terminal):
sh:
export API_KEY=your-secret-api-key
Modify
application.propertiesto reference theenvironment variable:
properties:
api.key=${API_KEY}
ow,
N @Value("${api.key}")will automatically fetchtheAPI key from the environment
variable, keeping it secure.
java:
mport
i
org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
Component
@
@ConfigurationProperties(prefix = "api")
public class ApiConfig {
private String key;
java:
import org.springframework.stereotype.Service;
Service
@
public class ApiService {
private final ApiConfig apiConfig;
🔹 Best Practices
✅
Never commit
application.propertieswith sensitive data(use
.gitignore
).
✅Use environment variables(API_KEY
✅Use Spring Cloud ConfigorSecrets Managerfor highly sensitive keys.
) in productionfor extra security.
nderstanding
U @PostConstructand Application Caching in Spring
Boot
1️⃣
@PostConstructAnnotation in Spring Boot
📌 What is@PostConstruct?
L
● oading cacheon application startup
● Initializing resources
● Logging application startup details
Component
@
public class DataInitializer {
PostConstruct
@
public void init() {
// Simulating fetching data from a database or API
countryCache.put(1, "India");
countryCache.put(2, "USA");
countryCache.put(3, "Germany");
System.out.println(" ✅
Country Cache Initialized: " +
countryCache);
}
● T he
init()method will run once when the application starts and pre-loads the
cache.
● The cache can then be usedwithout making a databasecall.
✅
✅
In-Memory Caching– Simple Java HashMap
Spring Cache Abstraction–
@Cacheable ,
@CacheEvict
✅
EhCache, Redis, Hazelcast– For distributed caching
Configuration
@
@EnableCaching
public class CacheConfig {
// Enables Spring's caching mechanism
}
Service
@
public class CountryService {
Cacheable("countries")
@
public String getCountryById(int id) {
System.out.println("Fetching country from DB...");
return switch (id) {
case 1 -> "India";
case 2 -> "USA";
case 3 -> "Germany";
default -> "Unknown";
};
}
}
T
● he first time
getCountryById(1)is called, itfetches from DB.
● Thenext time, thecached result is usedinstead of fetching again.
java:
mport org.springframework.cache.annotation.CacheEvict;
i
import org.springframework.stereotype.Service;
Service
@
public class CountryService {
import java.time.Duration;
Configuration
@
@EnableCaching
public class RedisConfig {
Bean
@
public RedisConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory();
}
Bean
@
public RedisCacheManager cacheManager(RedisConnectionFactory
redisConnectionFactory) {
RedisCacheConfiguration config =
RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(10)) // Cache expiry
time
.disableCachingNullValues()
serializeKeysWith(RedisSerializationContext.SerializationPair.fromS
.
erializer(new StringRedisSerializer()))
serializeValuesWith(RedisSerializationContext.SerializationPair.fro
.
mSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(redisConnectionFactory)
.cacheDefaults(config)
.build();
}
}
Service
@
public class CountryService {
PostConstru R
@ uns once after Spring initializes a
ct
Bean
@Cacheable
Enables method-level caching
@CacheEvict
Removes cache when data updates