KEMBAR78
Spring | PDF | Spring Framework | Model–View–Controller
0% found this document useful (0 votes)
29 views82 pages

Spring

Uploaded by

ksamutha027
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
29 views82 pages

Spring

Uploaded by

ksamutha027
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 82

SPRING & SPRING BOOT TEXTBOOK FOR BEGINNERS

(From Zero to Advanced)

---

Chapter 1: Introduction to Spring

1.1 What is Spring?


Spring is an open-source Java framework that helps in building enterprise-
level applications with minimal effort.

Lightweight and modular.

Focuses on Inversion of Control (IoC) and Dependency Injection (DI).

1.2 Key Features

IoC Container → Manages objects (beans) lifecycle.

Dependency Injection → Objects get dependencies automatically.

Modular → Use only what you need.

Testable → Encourages unit testing.

Integration → Works well with JDBC, Hibernate, JMS, etc.

1.3 Spring Modules

Core – IoC, DI.

Context – Bean management.

AOP – Aspect-Oriented Programming.

DAO / ORM – Database integration.

Web MVC – Web application support.

Security – Authentication & authorization.


---

Chapter 2: Inversion of Control (IoC) & Dependency Injection (DI)

2.1 IoC Concept


Normally, you create objects manually:

Car car = new Car(new Engine());

With IoC, the container creates and injects objects for you.

2.2 Dependency Injection Types

Constructor Injection

Setter Injection

Field Injection (@Autowired)

Example (Constructor Injection):

@Component
public class Engine {
public String start() { return "Engine started"; }
}

@Component
public class Car {
private Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println(engine.start());
}
}

---
Chapter 3: Spring Beans & Configuration

3.1 What is a Bean?

A bean is an object managed by Spring.

3.2 Bean Scopes

singleton → One instance (default).

prototype → New instance every time.

request → One per HTTP request.

session → One per HTTP session.

3.3 Configuration Styles

1. XML-based

2. Annotation-based (@Component, @Configuration, @Bean)

3. Java-based Configuration:

@Configuration
public class AppConfig {
@Bean
public Engine engine() {
return new Engine();
}
}

---

Chapter 4: Spring MVC


4.1 MVC Pattern

Model → Data.

View → UI (JSP, Thymeleaf).

Controller → Handles requests.

4.2 Flow

1. User sends request.

2. DispatcherServlet receives it.

3. Controller processes.

4. View renders response.

Example Controller:

@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Hello, Spring!";
}
}

---

Chapter 5: Introduction to Spring Boot

5.1 What is Spring Boot?

A Spring-based framework that removes boilerplate configuration.

Has starter dependencies, auto-configuration, and embedded server.


5.2 How It’s Different from Spring

Spring → More manual setup.

Spring Boot → Just run and go.

5.3 Setting up with Spring Initializr

1. Go to start.spring.io

2. Choose:

Maven Project

Java 17+

Dependencies: Spring Web, Spring Data JPA, MySQL Driver, DevTools.

3. Download & open in IDE.

---

Chapter 6: Spring Boot Basics

6.1 Main Class

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

6.2 application.properties Example


server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/demo
spring.datasource.username=root
spring.datasource.password=pass

---

Chapter 7: Spring Boot + Database (JPA)

7.1 Entity Example

@Entity
public class User {
@Id @GeneratedValue
private Long id;
private String name;
}

7.2 Repository

public interface UserRepository extends JpaRepository<User, Long> {}

7.3 Service & Controller

@Service
public class UserService {
@Autowired private UserRepository repo;
public List<User> getAll() { return repo.findAll(); }
}

@RestController
public class UserController {
@Autowired private UserService service;
@GetMapping("/users")
public List<User> list() { return service.getAll(); }
}

---

Chapter 8: Advanced Topics

Spring Boot Actuator → Health checks.


Spring Security → Authentication/Authorization.

Spring Boot Testing → Unit & integration tests.

Microservices with Spring Cloud → Service discovery, API Gateway.

---

Chapter 9: Practice Project Ideas

1. To-Do List App

2. Student Management System

3. Online Book Store API

4. Employee Payroll System

5. Simple E-commerce Backend

---

Chapter 10: Daily Practice Plan

DayTopic

1–2 IoC, DI
3–4 Beans, Scopes
5–6 MVC Basics
7–8 Spring Boot Setup
9–10 JPA + CRUD
11–12 Security Basics
13–15 Build Mini Project
---

If you want, I can now start Chapter 1 in full “textbook depth” — with theory,
real-world analogies, and diagrams in words — so it feels like reading a
printed Java Spring book from the first page.

Do you want me to continue in deep-dive style chapter-by-chapter? That will


make it truly like a professional textbook you can follow every day.
Alright 🚀
Let’s start Chapter 1 in full textbook style — detailed explanations, examples,
real-world analogies, and word-based diagrams so it feels like you’re reading a
printed guide.

---

📖 CHAPTER 1 — INTRODUCTION TO SPRING

---

1.1 Why Spring Exists

Before Spring, building enterprise-level Java applications was painful:

You had to write a lot of repetitive boilerplate code.

Configuring connections, security, and transactions was manual.

Applications were hard to test because objects were tightly coupled.

💡 Spring came to fix this by:

Reducing boilerplate code.

Making code loosely coupled.

Providing a ready-to-use infrastructure for enterprise applications.


---

1.2 What is Spring?

Spring is an open-source Java framework for building enterprise applications.


It provides tools to develop:

Web applications

REST APIs

Microservices

Desktop/Batch applications

Think of Spring as:

> 🛠 “A big toolbox for Java developers where you pick only the tools you
need.”

---

1.3 Core Principles of Spring

A. Inversion of Control (IoC)

Normally, your code controls object creation:

Car car = new Car(new Engine());

With IoC, you give control to Spring — it creates objects and gives them to
you when needed.

📌 Real-world analogy:
Imagine hiring a personal assistant (Spring) who brings you everything you
need instead of you going to get it yourself.

---
B. Dependency Injection (DI)

Instead of creating dependencies manually, Spring injects them into your


classes.

Example:

@Component
class Engine {}

@Component
class Car {
private Engine engine;
@Autowired
Car(Engine engine) {
this.engine = engine;
}
}

Here, you don’t new Engine(). Spring provides it.

---

C. Aspect-Oriented Programming (AOP)

Separates cross-cutting concerns (logging, security, transactions) from


business logic.

Means you don’t write the same logging/security code in every method.

📌 Example: Instead of adding System.out.println() in 50 places, you configure


it once and Spring applies it everywhere.

---

1.4 Spring Architecture Overview

+---------------------------------------------------+
| SPRING FRAMEWORK |
+---------------------------------------------------+
| Spring Core Container | IoC, DI |
| Spring Context | Bean management |
| Spring AOP | Cross-cutting concerns |
| Spring DAO / ORM | Database integration |
| Spring Web MVC | Web applications |
| Spring Security | Authentication/Access |
+---------------------------------------------------+

---

1.5 Spring Modules in Detail

Module Purpose

Core Basic IoC and DI features


ContextAccessing beans via ApplicationContext
AOP Aspect-oriented programming
Web MVC Building web apps
ORM Integration with Hibernate, JPA
Security Login, roles, encryption
Boot Rapid development with auto-configuration

---

1.6 Advantages of Spring

1. Lightweight – You use only what you need.

2. Testable – Mock dependencies easily.

3. Flexible – Integrates with almost any technology.

4. Non-Intrusive – Your code doesn’t depend heavily on Spring-specific


classes.

5. Community Support – Large global developer base.


---

1.7 Example: Without Spring vs With Spring

Without Spring

public class Car {


Engine engine = new Engine();
}

Tight coupling (Car is stuck with Engine implementation).

With Spring

@Component
class Engine {}

@Component
class Car {
private Engine engine;
@Autowired
Car(Engine engine) { this.engine = engine; }
}

Loose coupling (Engine can be replaced easily).

---

1.8 When to Use Spring

When you’re building a large, complex application.

When you want easy testing & maintainability.

When you need integration with multiple technologies (DB, Messaging, Cloud).

When building REST APIs or Microservices.


---

1.9 Summary

Spring is a flexible, modular Java framework.

Based on IoC & DI.

Divided into modules (Core, MVC, Security, etc.).

Works well for small apps and massive enterprise projects.

---

✅ Exercise 1

1. In your own words, explain IoC.

2. List any 3 Spring modules and their purpose.

3. Give a real-world example of Dependency Injection.

---

📌 Next Chapter (2) → We’ll dive deep into IoC & DI — the heart of Spring —
with diagrams, multiple injection types, and a real mini-project example.

---

Do you want me to continue with Chapter 2 now so you get the IoC & DI
details immediately before moving to Spring Boot later? That way, the flow
will be crystal clear.

Alright — let’s go into Chapter 2 in full textbook depth.


This chapter is extremely important because Inversion of Control (IoC) and
Dependency Injection (DI) are the foundation of both Spring and Spring Boot.

---

📖 CHAPTER 2 — Inversion of Control (IoC) & Dependency Injection (DI)

---

2.1 What is Inversion of Control (IoC)?

Traditional Programming Flow


Normally, in Java, you control object creation:

Engine engine = new Engine();


Car car = new Car(engine);

Here:

Car is responsible for creating or holding Engine.

You decide when and how objects are created.

This makes your code tightly coupled (harder to change).

Inversion of Control
With IoC:

You give control to a container (Spring).

The container creates objects (called beans) and wires them together.

Your job: just declare what you need.

The container decides when, how, and which objects to create.

📌 Analogy:
In a restaurant, you don’t go to the kitchen and cook your food.
You just place an order (declare requirement) → Chef (IoC container) prepares
and delivers the dish.
---

2.2 IoC Container in Spring

Spring provides two main IoC containers:

1. BeanFactory – Basic container.

2. ApplicationContext – Advanced container (most used).

Adds event propagation, bean creation, and internationalization.

---

2.3 What is Dependency Injection (DI)?

Dependency Injection is a way of implementing IoC.


It means: Instead of creating dependencies manually, you inject them into a
class.

---

Why DI is Important?

Removes tight coupling between classes.

Makes code testable.

Makes it easy to swap implementations.

---

2.4 Types of Dependency Injection in Spring

1. Constructor Injection
Dependencies are passed via the constructor.

@Component
class Engine {
public String start() { return "Engine started"; }
}

@Component
class Car {
private Engine engine;
@Autowired
public Car(Engine engine) { // Injection here
this.engine = engine;
}
public void drive() {
System.out.println(engine.start());
}
}

✅ Best for: Required dependencies, immutability.

---

2. Setter Injection

Dependencies are passed via setter methods.

@Component
class Car {
private Engine engine;
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
}

✅ Best for: Optional dependencies.

---

3. Field Injection
Dependencies are injected directly into fields.

@Component
class Car {
@Autowired
private Engine engine;
}

✅ Best for: Quick demos, but not recommended in large projects (harder to
test).

---

2.5 Bean Scopes in Spring

Bean scope decides how many instances of a bean Spring creates.

Scope Description Example Usage

singleton One instance per container (default) Services


prototype New instance each time requested State-full beans
request One instance per HTTP request Web apps
session One per HTTP session User sessions

Example:

@Component
@Scope("prototype")
class Engine {}

---

2.6 How Spring Knows What to Inject

Spring identifies and injects dependencies using:

Annotations:

@Component → Marks a class as a Spring bean.


@Autowired → Injects a dependency.

@Qualifier → Chooses between multiple beans of same type.

Configuration Classes:

@Configuration and @Bean methods.

XML Config (older style).

---

2.7 Real-World Example of IoC & DI

Without Spring:

public class Restaurant {


private Chef chef = new Chef(); // Direct creation
}

If you want a different chef, you must modify Restaurant code.

With Spring:

@Component
class Chef {
public String cook() { return "Cooking pasta"; }
}

@Component
class Restaurant {
private Chef chef;
@Autowired
public Restaurant(Chef chef) {
this.chef = chef;
}
}

You can replace Chef with ItalianChef, FrenchChef easily without touching
Restaurant.

---

2.8 Mini Project: IoC & DI Demo

Step 1 — Create Beans

@Component
class Engine {
public String start() { return "Engine started"; }
}

Step 2 — Inject Bean

@Component
class Car {
private Engine engine;
@Autowired
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println(engine.start() + " — Car is moving!");
}
}

Step 3 — Main Class

@SpringBootApplication
public class SpringDemoApp {
public static void main(String[] args) {
ApplicationContext context =
SpringApplication.run(SpringDemoApp.class, args);
Car car = context.getBean(Car.class);
car.drive();
}
}

Output:

Engine started — Car is moving!


---

2.9 Advantages of IoC & DI

Loosely coupled code.

Easy to maintain and test.

Reusable components.

Flexible architecture.

---

✅ Exercise 2

1. Explain the difference between IoC and DI in your own words.

2. Write an example of Setter Injection for a Mobile class that depends on a


Battery class.

3. List all Bean Scopes and give one use case for each.

---

📌 Next Chapter (3) → We’ll cover Spring Beans & Configuration in depth —
including:

How Spring creates & manages beans

Java-based, Annotation-based, XML-based configurations

Bean lifecycle methods


---

If you want, I can make Chapter 3 even more practical by adding bean
lifecycle diagrams and a live example with init/destroy methods so you can
see how Spring manages objects.

Do you want me to continue with Chapter 3 like that?

Alright, let’s dive into Chapter 3 — we’ll go deep into Spring Beans &
Configuration, and I’ll include lifecycle diagrams (in text form) + real coding
examples so it’s clear how Spring manages objects from birth to destruction.

---

📖 CHAPTER 3 — Spring Beans & Configuration

---

3.1 What is a Bean in Spring?

In Spring, a Bean is simply an object that is created, managed, and destroyed


by the Spring IoC Container.

When we mark a class with @Component or define it in @Bean, Spring treats


it as a bean and controls its lifecycle.

📌 Analogy:
Think of Spring as a nursery that takes care of your plants (beans) — planting
(creating), watering (configuring), pruning (managing), and removing
(destroying) them when they are no longer needed.

---

3.2 How Does Spring Create Beans?

Spring has 3 main ways to define beans:

1. XML-based configuration (old style)


2. Annotation-based configuration (@Component, @Autowired)

3. Java-based configuration (@Configuration, @Bean)

---

A. XML-based Configuration

<beans xmlns="http://www.springframework.org/schema/beans">
<bean id="engine" class="com.example.Engine" />
</beans>

You define beans in an XML file.

Rarely used in modern Spring Boot projects.

---

B. Annotation-based Configuration

@Component
public class Engine {}

Spring scans packages for @Component classes and creates beans


automatically.

---

C. Java-based Configuration

@Configuration
public class AppConfig {
@Bean
public Engine engine() {
return new Engine();
}
}

You manually declare beans in a configuration class.

---

3.3 Bean Scopes

Bean scope determines how many instances of a bean Spring creates.

Scope Description When to Use

singleton (default) One instance per container Most services


prototype New instance every time Stateful objects
request One instance per HTTP request Web request handling
session One instance per HTTP session Web session data

Example:

@Component
@Scope("prototype")
public class Engine {}

---

3.4 Bean Lifecycle in Spring

The lifecycle has 5 key phases:

[1] Instantiate → [2] Populate Properties → [3] Call init() → [4] Bean Ready
→ [5] Destroy

1. Instantiate

Spring creates the object (using new or factory method).

2. Populate Properties

Dependencies are injected (DI happens here).


3. Call init()

Any initialization logic is run.

4. Bean Ready

Bean is available to be used.

5. Destroy

When the container shuts down, beans are destroyed (if singleton).

---

3.5 Declaring Init and Destroy Methods

Using Annotations

@Component
public class Engine {

@PostConstruct
public void init() {
System.out.println("Engine is being initialized...");
}

@PreDestroy
public void cleanup() {
System.out.println("Engine is being destroyed...");
}
}

---

Using Java Config

@Configuration
public class AppConfig {
@Bean(initMethod = "init", destroyMethod = "cleanup")
public Engine engine() {
return new Engine();
}
}

---

Using XML Config

<bean id="engine" class="com.example.Engine"


init-method="init"
destroy-method="cleanup" />

---

3.6 Example: Bean Lifecycle in Action

@Component
class Engine {
@PostConstruct
public void start() {
System.out.println("🚗 Engine started");
}

@PreDestroy
public void stop() {
System.out.println("🛑 Engine stopped");
}
}

@SpringBootApplication
public class BeanLifecycleDemo {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(BeanLifecycleDemo.class, args);
context.close(); // Trigger destroy phase
}
}

Output:

🚗 Engine started
🛑 Engine stopped
---

3.7 Choosing the Right Configuration Method

Style Pros Cons Best For

XML Clear separation from code Verbose, outdated Legacy projects


Annotations Less config, easy to read Harder to change without touching
code Modern projects
Java Config Full control in code Slightly more verbose than annotations
Complex projects needing fine-tuning

---

3.8 Key Takeaways

Bean = Object managed by Spring.

Bean scope = Controls how many instances exist.

Bean lifecycle = Creation → Dependency Injection → Initialization → Ready →


Destruction.

Config styles = XML, Annotations, Java Config.

---

✅ Exercise 3

1. Write a bean using Java-based configuration with custom init and destroy
methods.

2. Change a bean scope from singleton to prototype and check if a new


instance is created every time.

3. Explain why @PostConstruct and @PreDestroy are useful.


---

📌 Next Chapter (4) → We’ll move into Spring MVC — learning the Model-View-
Controller pattern, how Spring handles HTTP requests, and building your first
controller with @RestController.

---

If you want, I can make Chapter 4 not just theory but a hands-on Spring Boot
mini web app so you immediately see MVC in action while learning.

Do you want me to make Chapter 4 like that?

Alright 👍
Let’s make Chapter 4 both theoretical and practical — you’ll understand
Spring MVC and also build a small working Spring Boot web app so the
concepts stick.

---

📖 CHAPTER 4 — Spring MVC (Model-View-Controller)

---

4.1 What is Spring MVC?

Spring MVC is a web framework within Spring for building web applications
and REST APIs.
It follows the MVC Pattern:

Component Responsibility

Model Represents data (Java objects, database entities).


View Presents data (HTML, JSP, Thymeleaf, JSON).
Controller Handles requests, processes data, returns view or JSON.

📌 Analogy:
Model = Kitchen ingredients (data).

View = Plated dish (UI).

Controller = Chef (decides what to cook & how).

---

4.2 Spring MVC Request Flow

[Browser] → [DispatcherServlet] → [Controller] → [Service] → [Model] →


[ViewResolver] → [HTML/JSON Response]

Step-by-step:

1. User sends a request (e.g., /hello).

2. DispatcherServlet (front controller) catches it.

3. Finds the right Controller method.

4. Method returns data (Model) and view name (or JSON).

5. ViewResolver renders final output.

---

4.3 Key Annotations in Spring MVC

@Controller – Handles HTTP requests and returns views.

@RestController – Handles HTTP requests and returns JSON.

@RequestMapping – Maps URLs to methods.


@GetMapping, @PostMapping – Shortcuts for HTTP GET/POST.

@PathVariable – Extracts values from URL.

@RequestParam – Extracts query parameters.

---

4.4 Hands-On Project: Hello Spring Web App

---

Step 1 — Create Project

Go to start.spring.io

Project: Maven, Java 17+

Dependencies:

Spring Web

Spring Boot DevTools

Download and open in your IDE.

---

Step 2 — Main Application Class

@SpringBootApplication
public class HelloMvcApp {
public static void main(String[] args) {
SpringApplication.run(HelloMvcApp.class, args);
}
}
---

Step 3 — Create Controller

@RestController
public class HelloController {

@GetMapping("/hello")
public String sayHello() {
return "Hello, Spring MVC!";
}
}

---

Step 4 — Run the App

Run HelloMvcApp from IDE.

Open browser → http://localhost:8080/hello

Output:

Hello, Spring MVC!

---

4.5 Example with PathVariable & RequestParam

@RestController
@RequestMapping("/api")
public class GreetingController {

// Example: /api/greet/John
@GetMapping("/greet/{name}")
public String greetByName(@PathVariable String name) {
return "Hello, " + name + "!";
}

// Example: /api/hi?name=John&age=25
@GetMapping("/hi")
public String greetWithParams(@RequestParam String name,
@RequestParam int age) {
return "Hi " + name + ", you are " + age + " years old!";
}
}

---

4.6 Returning JSON Data (Model Example)

public class User {


private String name;
private int age;
// constructor, getters, setters
}

@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/one")
public User getUser() {
return new User("Alice", 30);
}
}

Output JSON:

{
"name": "Alice",
"age": 30
}

---

4.7 MVC with View (HTML) using Thymeleaf

Add Thymeleaf dependency in pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

Create Controller:

@Controller
public class WebController {

@GetMapping("/welcome")
public String welcomePage(Model model) {
model.addAttribute("name", "Tharani");
return "welcome";
}
}

Create HTML in src/main/resources/templates/welcome.html:

<!DOCTYPE html>
<html>
<head><title>Welcome</title></head>
<body>
<h1>Welcome, <span th:text="${name}"></span>!</h1>
</body>
</html>

Visit http://localhost:8080/welcome

---

4.8 Key Takeaways

Spring MVC handles both web views and REST APIs.

@RestController = JSON output; @Controller = HTML view.

The request flow always passes through DispatcherServlet.

Thymeleaf is great for rendering HTML in MVC apps.

---
✅ Exercise 4

1. Create a /bye endpoint that returns "Goodbye, Spring!".

2. Create a /sum endpoint that takes two query parameters (a, b) and returns
their sum in JSON.

3. Modify the Thymeleaf example to also show today’s date.

---

📌 Next Chapter (5) → We’ll introduce Spring Boot — why it’s faster than plain
Spring, how to set it up, and how to use Starters & Auto-Configuration.
We’ll also upgrade our current app into a Spring Boot + Database project.

---

Do you want me to make Chapter 5 a mix of theory + a real Spring Boot +


MySQL CRUD example so you’ll have your first database project ready? That
way you learn Boot + JPA in one go.

Perfect 👍
Then Chapter 5 will be about Spring Boot + MySQL CRUD so you learn Boot,
JPA, and Database integration in one go.

---

📖 CHAPTER 5 — Spring Boot with MySQL (CRUD Example)

---

5.1 What is Spring Boot?

Spring Boot is like Spring, but with:


Auto-configuration → You don't write XML config.

Starters → Pre-built dependency packs.

Embedded server → No need to deploy to Tomcat manually.

Production-ready → Health checks, metrics, etc.

📌 Why beginners love it?


Because you can build a complete REST API in minutes without boilerplate.

---

5.2 Key Concepts in Spring Boot

Concept Meaning

Starter Dependency bundles (e.g., spring-boot-starter-web).


Auto Configuration Spring Boot configures beans automatically.
Application.properties One file to set DB URL, username, etc.
DevTools Auto-reload during development.

---

5.3 Our Project: Student Management API

We will:

1. Create a Student entity.

2. Connect to MySQL.

3. Build CRUD REST APIs:

Create Student

Get All Students


Get Student by ID

Update Student

Delete Student

---

5.4 Step 1 — Create Spring Boot Project

Go to start.spring.io

Project: Maven, Java 17+

Dependencies:

Spring Web

Spring Boot DevTools

Spring Data JPA

MySQL Driver

---

5.5 Step 2 — Configure MySQL in application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/schooldb
spring.datasource.username=root
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

💡 Make sure you have MySQL running and schooldb database created.
---

5.6 Step 3 — Create Entity

import jakarta.persistence.*;

@Entity
public class Student {

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

private String name;


private String email;
private int age;

// Getters, setters, constructor


}

---

5.7 Step 4 — Create Repository

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

public interface StudentRepository extends JpaRepository<Student, Long> {


}

---

5.8 Step 5 — Create Service

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class StudentService {

private final StudentRepository repo;


public StudentService(StudentRepository repo) {
this.repo = repo;
}

public Student save(Student student) {


return repo.save(student);
}

public List<Student> getAll() {


return repo.findAll();
}

public Student getById(Long id) {


return repo.findById(id).orElse(null);
}

public Student update(Long id, Student student) {


Student existing = getById(id);
if (existing != null) {
existing.setName(student.getName());
existing.setEmail(student.getEmail());
existing.setAge(student.getAge());
return repo.save(existing);
}
return null;
}

public void delete(Long id) {


repo.deleteById(id);
}
}

---

5.9 Step 6 — Create Controller

import org.springframework.web.bind.annotation.*;
import java.util.List;

@RestController
@RequestMapping("/students")
public class StudentController {
private final StudentService service;

public StudentController(StudentService service) {


this.service = service;
}

@PostMapping
public Student create(@RequestBody Student student) {
return service.save(student);
}

@GetMapping
public List<Student> getAll() {
return service.getAll();
}

@GetMapping("/{id}")
public Student getById(@PathVariable Long id) {
return service.getById(id);
}

@PutMapping("/{id}")
public Student update(@PathVariable Long id, @RequestBody Student
student) {
return service.update(id, student);
}

@DeleteMapping("/{id}")
public String delete(@PathVariable Long id) {
service.delete(id);
return "Deleted Successfully";
}
}

---

5.10 Step 7 — Run & Test

1. Run the project.

2. Test APIs using Postman:


POST /students → Create a student

{
"name": "Tharani",
"email": "tharani@example.com",
"age": 23
}

GET /students → Get all

GET /students/1 → Get by ID

PUT /students/1 → Update

DELETE /students/1 → Delete

---

5.11 Key Takeaways

Spring Boot eliminates XML config.

JPA + MySQL makes CRUD easy.

We used JpaRepository so we didn’t write SQL manually.

This same API can be connected to React/Angular later.

---

✅ Exercise 5

1. Add a field course to Student.

2. Create an endpoint /students/findByCourse/{course}.


3. Return all students for that course.

---

If you want, I can make Chapter 6 about


Spring Boot + Thymeleaf + MySQL (Full Web UI) so you’ll see frontend +
backend integration in Spring.
This way, instead of only Postman JSON, you’ll have a real working web page
for CRUD.

Do you want me to make Chapter 6 like that? That would give you full-stack
Spring experience.

Alright 🚀
Let’s build Chapter 6 — Spring Boot + Thymeleaf + MySQL so you can create
a complete full-stack web app without needing React or Angular yet.

---

📖 CHAPTER 6 — Spring Boot + Thymeleaf + MySQL CRUD Web App

---

6.1 What is Thymeleaf?

Thymeleaf is a server-side Java template engine that:

Works with HTML + Spring Boot

Lets you embed Java variables in HTML

Is perfect for building dynamic web pages

📌 Why we use it here?

Simple for beginners

No JavaScript needed for basic UI


Works perfectly with Spring MVC

---

6.2 Project Goal

We will build a Student Management Web App:

View all students in a table

Add a student (form)

Edit student details

Delete student

---

6.3 Step 1 — Create Spring Boot Project

Go to start.spring.io

Project: Maven, Java 17+

Dependencies:

Spring Web

Spring Data JPA

MySQL Driver

Thymeleaf

Spring Boot DevTools


---

6.4 Step 2 — Configure MySQL

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/schooldb
spring.datasource.username=root
spring.datasource.password=your_password
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect

---

6.5 Step 3 — Create Entity

import jakarta.persistence.*;

@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;


private String email;
private int age;

// Getters & Setters


}

---

6.6 Step 4 — Repository

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

public interface StudentRepository extends JpaRepository<Student, Long> {


}
---

6.7 Step 5 — Service

import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class StudentService {
private final StudentRepository repo;

public StudentService(StudentRepository repo) {


this.repo = repo;
}

public List<Student> getAllStudents() {


return repo.findAll();
}

public void saveStudent(Student student) {


repo.save(student);
}

public Student getStudentById(Long id) {


return repo.findById(id).orElse(null);
}

public void deleteStudent(Long id) {


repo.deleteById(id);
}
}

---

6.8 Step 6 — Controller

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Controller
public class StudentController {
private final StudentService service;
public StudentController(StudentService service) {
this.service = service;
}

@GetMapping("/")
public String viewHomePage(Model model) {
model.addAttribute("listStudents", service.getAllStudents());
return "index";
}

@GetMapping("/showNewStudentForm")
public String showNewStudentForm(Model model) {
model.addAttribute("student", new Student());
return "new_student";
}

@PostMapping("/saveStudent")
public String saveStudent(@ModelAttribute("student") Student student) {
service.saveStudent(student);
return "redirect:/";
}

@GetMapping("/showFormForUpdate/{id}")
public String showFormForUpdate(@PathVariable Long id, Model model) {
model.addAttribute("student", service.getStudentById(id));
return "update_student";
}

@GetMapping("/deleteStudent/{id}")
public String deleteStudent(@PathVariable Long id) {
service.deleteStudent(id);
return "redirect:/";
}
}

---

6.9 Step 7 — Thymeleaf HTML Templates

📂 Folder Structure → src/main/resources/templates/


---

index.html — Show all students

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Student Management</title>
</head>
<body>
<h2>Student List</h2>
<a href="/showNewStudentForm">Add New Student</a>
<table border="1">
<tr>
<th>ID</th><th>Name</th><th>Email</th><th>Age</th><th>Actio
ns</th>
</tr>
<tr th:each="student : ${listStudents}">
<td th:text="${student.id}"></td>
<td th:text="${student.name}"></td>
<td th:text="${student.email}"></td>
<td th:text="${student.age}"></td>
<td>
<a th:href="@{/showFormForUpdate/{id}(id=$
{student.id})}">Edit</a>
<a th:href="@{/deleteStudent/{id}(id=${student.id})}">Delete</a>
</td>
</tr>
</table>
</body>
</html>

---

new_student.html — Add student form

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Add Student</title>
</head>
<body>
<h2>Add Student</h2>
<form th:action="@{/saveStudent}" th:object="${student}"
method="post">
Name: <input type="text" th:field="*{name}"><br>
Email: <input type="text" th:field="*{email}"><br>
Age: <input type="number" th:field="*{age}"><br>
<button type="submit">Save</button>
</form>
</body>
</html>

---

update_student.html — Edit student form

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Update Student</title>
</head>
<body>
<h2>Update Student</h2>
<form th:action="@{/saveStudent}" th:object="${student}"
method="post">
<input type="hidden" th:field="*{id}">
Name: <input type="text" th:field="*{name}"><br>
Email: <input type="text" th:field="*{email}"><br>
Age: <input type="number" th:field="*{age}"><br>
<button type="submit">Update</button>
</form>
</body>
</html>

---

6.10 Step 8 — Run & Test

Run Spring Boot

Open browser → http://localhost:8080

You can now:


Add students via form

Edit details

Delete students

See all in a table

---

✅ Key Learning Points

Thymeleaf integrates directly with Spring MVC

You used Model to send data to HTML

CRUD works both in REST API and Web UI

---

If you want, the next Chapter 7 could be


Spring Boot Security (Login + Role-based Access) so you can protect this
student management app with authentication.

Do you want me to prepare Chapter 7 like that? That will make your app a
real-world secure application.

Alright — Chapter 7 will be the Spring Boot Security + Login System part of
your tutorial book.
We’ll add authentication and role-based access control to your Student
Management App from Chapter 6.

---

📖 CHAPTER 7 — Spring Boot Security (Login + Role-based Access)

---
7.1 What is Spring Security?

Spring Security is a powerful framework for authentication & authorization in


Java applications.

Key Concepts:

Authentication → Who are you? (Login)

Authorization → What can you do? (Roles/Permissions)

---

7.2 Features We’ll Implement

User login with username/password

Two roles:

ADMIN → Can add, edit, delete students

USER → Can only view students

Logout button

---

7.3 Step 1 — Add Dependency

In your pom.xml:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

---
7.4 Step 2 — Create User Entity

We’ll store users in the database.

import jakarta.persistence.*;

@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String username;


private String password;
private String role; // ROLE_ADMIN or ROLE_USER

// Getters and Setters


}

---

7.5 Step 3 — User Repository

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

public interface UserRepository extends JpaRepository<User, Long> {


User findByUsername(String username);
}

---

7.6 Step 4 — Security Configuration

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {

@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/", "/login").permitAll()
.requestMatchers("/showNewStudentForm", "/saveStudent",
"/showFormForUpdate/**", "/deleteStudent/**")
.hasRole("ADMIN")
.anyRequest().authenticated()
)
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/", true)
.permitAll()
)
.logout(logout -> logout.permitAll());

return http.build();
}
}

---

7.7 Step 5 — Custom UserDetailsService

import org.springframework.security.core.userdetails.*;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Service;
import java.util.Collections;

@Service
public class CustomUserDetailsService implements UserDetailsService {

private final UserRepository userRepo;


public CustomUserDetailsService(UserRepository userRepo) {
this.userRepo = userRepo;
}

@Override
public UserDetails loadUserByUsername(String username) throws
UsernameNotFoundException {
User user = userRepo.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User not found");
}
return new org.springframework.security.core.userdetails.User(
user.getUsername(),
user.getPassword(),
Collections.singleton(new SimpleGrantedAuthority(user.getRole()))
);
}
}

---

7.8 Step 6 — Create Login Page

📂 src/main/resources/templates/login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Login</title>
</head>
<body>
<h2>Login</h2>
<form th:action="@{/login}" method="post">
Username: <input type="text" name="username"><br>
Password: <input type="password" name="password"><br>
<button type="submit">Login</button>
</form>
</body>
</html>

---
7.9 Step 7 — Insert Users into DB

You can insert users manually via MySQL or at application startup.

Example SQL:

INSERT INTO users (username, password, role) VALUES


('admin',
'$2a$10$eB9x1yUL7nT9R0g6jqvPpOY7F4s1GqS3OaRzA1FtnV/JUYYomTgqC',
'ROLE_ADMIN'),
('user',
'$2a$10$eB9x1yUL7nT9R0g6jqvPpOY7F4s1GqS3OaRzA1FtnV/JUYYomTgqC',
'ROLE_USER');

(The password here is "1234" encrypted with BCrypt.)

---

7.10 Step 8 — Add Logout Button

Modify index.html:

<a href="/logout">Logout</a>

---

7.11 Step 9 — Run & Test

1. Start app

2. Visit http://localhost:8080/login

3. Login as:

admin (full CRUD access)

user (view-only access)


4. Try accessing restricted pages — unauthorized users will be blocked.

---

✅ What You Learned

How Spring Security works

Adding authentication with DB-backed users

Role-based access control

Secure login & logout

---

If you’re ready, Chapter 8 can be Deploying Spring Boot App to the Cloud
(Heroku / Railway / AWS) so you can put your app online.
That will make your Student Management System live for anyone to use.

Do you want me to prepare Chapter 8 — Deployment Guide next?

Alright — Chapter 8 will be Deploying Your Spring Boot App to the Cloud so
your Student Management System can be accessed from anywhere.
We’ll use Heroku (free tier) because it’s beginner-friendly, and then I’ll briefly
explain how to move to AWS or Railway later.

---

📖 CHAPTER 8 — Deploying Spring Boot Application to the Cloud (Heroku)

---

8.1 Why Deploy?

Right now, your app runs only on your local machine (localhost:8080).
Deployment lets you:

Share it with anyone via a link

Test in real-world environments

Show it in your portfolio/resume

---

8.2 Requirements

Before starting:

Java 17+ installed

Maven installed

Git installed

Heroku account → https://signup.heroku.com

Heroku CLI → https://devcenter.heroku.com/articles/heroku-cli

---

8.3 Step 1 — Prepare Your App for Deployment

Spring Boot must run on the port Heroku assigns (not 8080).

📂 src/main/resources/application.properties:

spring.datasource.url=jdbc:mysql://your-db-host/db_name
spring.datasource.username=your_db_user
spring.datasource.password=your_db_password
spring.jpa.hibernate.ddl-auto=update

# Heroku sets port dynamically


server.port=${PORT:8080}
---

8.4 Step 2 — Use PostgreSQL Instead of MySQL (Optional)

Heroku has a free PostgreSQL database.


You can either:

Keep using MySQL (needs a cloud provider like ClearDB)

Or switch to PostgreSQL for free hosting

For PostgreSQL in pom.xml:

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

---

8.5 Step 3 — Create Procfile

📂 Create a file named Procfile (no extension) in your root directory:

web: java -Dserver.port=$PORT -jar target/your-app-name-0.0.1-SNAPSHOT.jar

---

8.6 Step 4 — Build JAR File

In your project folder:

mvn clean package -DskipTests

This will create a .jar file in target/ folder.

---
8.7 Step 5 — Deploy to Heroku

# Login to Heroku
heroku login

# Create new Heroku app


heroku create your-app-name

# Push code to Heroku


git init
git add .
git commit -m "Initial commit"
heroku git:remote -a your-app-name
git push heroku master # or main

---

8.8 Step 6 — Add Database (If Needed)

For PostgreSQL:

heroku addons:create heroku-postgresql:hobby-dev

Then update your application.properties with:

spring.datasource.url=${JDBC_DATABASE_URL}
spring.datasource.username=${JDBC_DATABASE_USERNAME}
spring.datasource.password=${JDBC_DATABASE_PASSWORD}

---

8.9 Step 7 — Open Your App

heroku open

Your app will open in the browser with a public URL like:

https://your-app-name.herokuapp.com

---
8.10 Step 8 — Free Hosting Alternatives

If Heroku’s free tier isn’t enough:

Railway → https://railway.app (easy MySQL/PostgreSQL hosting)

Render → https://render.com (good for static + backend)

AWS Elastic Beanstalk → scalable cloud option

Azure App Service → good for Java + Microsoft integration

---

✅ What You Learned

How to prepare Spring Boot app for cloud deployment

How to deploy on Heroku

How to connect cloud database

Alternatives to Heroku

---

If you want, I can now make Chapter 9 — Real-World Project: Complete E-


Commerce Application with Spring Boot + Security + Cloud Deployment so
you can practice full-stack development from scratch.
It would be like building Amazon (mini version) step-by-step.

Do you want me to start that?

Alright — Chapter 10 will be our Advanced Features for ShopEase so it feels


like a professional, portfolio-ready e-commerce application.

---

📖 CHAPTER 10 — Advanced Features for ShopEase


10.1 Overview

We’ll enhance ShopEase with:

1. Payment Gateway Integration (Razorpay for India / Stripe for international)

2. Product Image Upload & Storage

3. Cart Persistence (saved in DB per user)

4. Order History (view past purchases)

---

10.2 Payment Gateway Integration (Razorpay)

Razorpay Docs → https://razorpay.com/docs/

Step 1 — Create Razorpay Account

Sign up at Razorpay

Create Test Mode API keys

You’ll get:

key_id = rzp_test_abc123
key_secret = xyz456

Step 2 — Add Razorpay SDK 📄 pom.xml

<dependency>
<groupId>com.razorpay</groupId>
<artifactId>razorpay-java</artifactId>
<version>1.4.4</version>
</dependency>
Step 3 — Payment Service 📄 PaymentService.java

@Service
public class PaymentService {

private static final String KEY = "rzp_test_abc123";


private static final String SECRET = "xyz456";

public String createOrder(double amount) throws RazorpayException {


RazorpayClient client = new RazorpayClient(KEY, SECRET);
JSONObject orderRequest = new JSONObject();
orderRequest.put("amount", (int)(amount * 100)); // amount in paise
orderRequest.put("currency", "INR");
orderRequest.put("receipt", "txn_123456");

Order order = client.Orders.create(orderRequest);


return order.toString();
}
}

Step 4 — Frontend Payment Button 📄 checkout.html

<script src="https://checkout.razorpay.com/v1/checkout.js"></script>
<button id="rzp-button1">Pay Now</button>
<script>
var options = {
"key": "rzp_test_abc123",
"amount": "50000", // example
"currency": "INR",
"name": "ShopEase",
"description": "Test Transaction",
"handler": function (response){
alert("Payment Successful: " + response.razorpay_payment_id);
}
};
var rzp1 = new Razorpay(options);
document.getElementById('rzp-button1').onclick = function(e){
rzp1.open();
e.preventDefault();
}
</script>
---

10.3 Product Image Upload

We’ll allow admin to upload an image file instead of typing the URL.

Step 1 — Enable File Upload in Spring Boot 📄 application.properties

spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=5MB

Step 2 — File Upload Controller

@PostMapping("/admin/products")
public String saveProduct(@RequestParam("file") MultipartFile file,
@ModelAttribute Product product) throws IOException {
String uploadDir = "src/main/resources/static/images/";
Path path = Paths.get(uploadDir + file.getOriginalFilename());
Files.write(path, file.getBytes());

product.setImageUrl("/images/" + file.getOriginalFilename());
productRepo.save(product);
return "redirect:/admin/products";
}

Step 3 — HTML Form

<form action="/admin/products" method="post" enctype="multipart/form-


data">
<input type="text" name="name" placeholder="Product Name" required>
<input type="text" name="description" placeholder="Description"
required>
<input type="number" name="price" placeholder="Price" required>
<input type="file" name="file" required>
<button type="submit">Save</button>
</form>

---

10.4 Cart Persistence

Right now, our cart is temporary. We’ll link it to the logged-in user so it’s
saved in the database.
Cart Entity

@Entity
@Data
public class CartItem {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
private User user;

@ManyToOne
private Product product;

private int quantity;


}

Repository

@Repository
public interface CartItemRepository extends JpaRepository<CartItem, Long> {
List<CartItem> findByUser(User user);
}

---

10.5 Order History

When a user checks out, we’ll move cart items to an Order table.

Order Entity

@Entity
@Data
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@ManyToOne
private User user;
private LocalDateTime orderDate;
private double totalAmount;
}

---

10.6 Next Steps

With Chapter 10 done, ShopEase will have:

Real payments

File upload for product images

Saved carts

Order history page

---

If you want, in Chapter 11 I can make


💡 "Complete API Documentation for ShopEase using Swagger + Deploy to
Cloud with DB Migration"
so it’s job-interview ready.

Do you want me to make Chapter 11 now?

Awesome — let’s make your project interview-ready with clean API docs and
safe, repeatable DB changes.

---

📖 CHAPTER 11 — Swagger/OpenAPI Documentation + DB Migrations (Flyway)


+ Deploy Tips

This chapter adds:

1. Interactive API docs (Swagger UI, OpenAPI 3)


2. Versioned DB migrations (Flyway)

3. Prod-ready setup (profiles, security, deploy notes)

---

11.1 Add Swagger / OpenAPI (springdoc)

11.1.1 Dependency

pom.xml

<!-- API docs -->


<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>

11.1.2 Where to find it (default)

OpenAPI JSON: /v3/api-docs

Swagger UI: /swagger-ui.html

11.1.3 Basic metadata

Create OpenApiConfig.java

package com.shopease.config;

import io.swagger.v3.oas.models.info.*;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.servers.Server;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;
@Configuration
public class OpenApiConfig {

@Bean
public OpenAPI shopEaseOpenAPI() {
return new OpenAPI()
.info(new Info()
.title("ShopEase API")
.description("Mini e-commerce API for products, carts, orders and
auth")
.version("v1.0")
.contact(new Contact().name("ShopEase
Team").email("support@shopease.example")))
.servers(List.of(
new Server().url("http://localhost:8080").description("Local Dev")
));
}
}

11.1.4 Annotate your endpoints

Example — ProductController

import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.*;

@Tag(name = "Products", description = "Product catalog operations")


@RestController
@RequestMapping("/api/products")
public class ProductController {

private final ProductService service;


public ProductController(ProductService service) { this.service = service; }

@Operation(summary = "List all products")


@GetMapping
public List<ProductDto> findAll() { return service.findAll(); }

@Operation(summary = "Find product by id")


@ApiResponses({
@ApiResponse(responseCode = "200", description = "Product found"),
@ApiResponse(responseCode = "404", description = "Product not found")
})
@GetMapping("/{id}")
public ProductDto findOne(@Parameter(description = "Product ID")
@PathVariable Long id) {
return service.findOne(id);
}

@Operation(summary = "Create a new product")


@PostMapping
public ProductDto create(@RequestBody CreateProductRequest req) {
return service.create(req);
}

@Operation(summary = "Update a product")


@PutMapping("/{id}")
public ProductDto update(@PathVariable Long id, @RequestBody
UpdateProductRequest req) {
return service.update(id, req);
}

@Operation(summary = "Delete a product")


@DeleteMapping("/{id}")
public void delete(@PathVariable Long id) {
service.delete(id);
}
}

> Tip: Use DTOs (ProductDto, CreateProductRequest, UpdateProductRequest)


so your API models are clean and stable.

11.1.5 Securing Swagger (only in dev)

Add a Spring profile called dev where Swagger UI is open.

In prod, restrict to admins or disable entirely.

application.yml

spring:
profiles:
active: dev

SecurityConfig.java (allow Swagger paths in dev)

@Bean
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers(
"/v3/api-docs/**", "/swagger-ui.html", "/swagger-ui/**" // open docs in dev
).permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.requestMatchers("/cart/**", "/checkout").hasRole("USER")
.anyRequest().permitAll()
)
.formLogin(login -> login.loginPage("/login").permitAll())
.logout(logout -> logout.permitAll());
return http.build();
}

In prod, either:

Move these doc paths behind hasRole("ADMIN"), or

Disable by profile (see below).

Disable springdoc in prod profile: application-prod.yml

springdoc:
api-docs:
enabled: false
swagger-ui:
enabled: false

---

11.2 Database Migrations with Flyway

Why Flyway?

Versioned SQL scripts you can track in Git


Auto-runs on startup → no manual DB drift

Works great locally and in cloud

11.2.1 Dependency

pom.xml

<!-- DB migrations -->


<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>

11.2.2 Folder structure

Place SQL files here:

src/main/resources/db/migration/

Files are ordered by version: V1__init.sql, V2__seed_data.sql, etc.

11.2.3 Example migrations

V1 — Schema V1__init_schema.sql

CREATE TABLE IF NOT EXISTS product (


id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(200) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
image_url VARCHAR(500)
);

CREATE TABLE IF NOT EXISTS users (


id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(200) NOT NULL,
role VARCHAR(50) NOT NULL
);

CREATE TABLE IF NOT EXISTS cart_item (


id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
product_id BIGINT NOT NULL,
quantity INT NOT NULL,
CONSTRAINT fk_cart_user FOREIGN KEY (user_id) REFERENCES users(id),
CONSTRAINT fk_cart_product FOREIGN KEY (product_id) REFERENCES
product(id)
);

CREATE TABLE IF NOT EXISTS orders (


id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT NOT NULL,
order_date TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
total_amount DECIMAL(10,2) NOT NULL,
CONSTRAINT fk_order_user FOREIGN KEY (user_id) REFERENCES users(id)
);

V2 — Seed data (admin + sample products) V2__seed_data.sql

-- password: 1234 (BCrypt, replace with your hash)


INSERT INTO users (username, password, role) VALUES
('admin',
'$2a$10$eB9x1yUL7nT9R0g6jqvPpOY7F4s1GqS3OaRzA1FtnV/JUYYomTgqC',
'ROLE_ADMIN'),
('user',
'$2a$10$eB9x1yUL7nT9R0g6jqvPpOY7F4s1GqS3OaRzA1FtnV/JUYYomTgqC',
'ROLE_USER');

INSERT INTO product (name, description, price, image_url) VALUES


('Phone X', 'Great phone', 49999.00, '/images/phone-x.jpg'),
('Laptop Z', 'Lightweight laptop', 89999.00, '/images/laptop-z.jpg');

> If you use PostgreSQL, replace AUTO_INCREMENT with GENERATED BY


DEFAULT AS IDENTITY and adjust types.

11.2.4 Configure Flyway

application.yml

spring:
flyway:
enabled: true
locations: classpath:db/migration
jpa:
hibernate:
ddl-auto: validate # Let Flyway manage schema; prevent Hibernate from
altering it

> In earlier chapters we used ddl-auto=update for speed.


Switch to validate once Flyway is in place to keep schema changes controlled.

11.2.5 Change management workflow

1. Edit code (new field/entity)

2. Write a new migration: V3__add_discount_to_product.sql

ALTER TABLE product ADD COLUMN discount DECIMAL(5,2) DEFAULT 0.00;

3. Commit both code + migration together

4. Run app → Flyway applies V3

5. Rollback? Create V4__revert_discount.sql (Flyway prefers forward-only; for


true rollback, write a new script that reverses changes)

---

11.3 Dev vs Prod Profiles (DB + Docs + Security)

11.3.1 Profile-specific configs

application-dev.yml (local)

spring:
datasource:
url: jdbc:mysql://localhost:3306/shopease
username: root
password: root
jpa:
show-sql: true

springdoc:
swagger-ui:
enabled: true
api-docs:
enabled: true

application-prod.yml (cloud)

spring:
datasource:
url: ${JDBC_URL}
username: ${JDBC_USER}
password: ${JDBC_PASSWORD}
jpa:
show-sql: false

springdoc:
swagger-ui:
enabled: false
api-docs:
enabled: false

Run with a profile:

# Dev (default we set earlier)


mvn spring-boot:run

# Prod
mvn spring-boot:run -Dspring-boot.run.profiles=prod

---

11.4 Deployment notes (Heroku/Railway/Render)

Ensure server.port=${PORT:8080} is set (Chapter 8).

Export DB creds as environment variables:

JDBC_URL, JDBC_USER, JDBC_PASSWORD


Keep Flyway enabled in prod so new versions auto-apply on release.

Consider a readiness check endpoint via Actuator:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

application-prod.yml

management:
endpoints:
web:
exposure:
include: health,info

Health: /actuator/health

---

11.5 Optional: JWT-auth in Swagger (if you move to token auth later)

Add a security scheme so Swagger UI can try authorized calls.

@Bean
public OpenAPI shopEaseOpenAPI() {
return new OpenAPI()
.components(new Components().addSecuritySchemes("bearerAuth",
new SecurityScheme()
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")))
.addSecurityItem(new SecurityRequirement().addList("bearerAuth"))
.info(new Info().title("ShopEase API").version("v1.0"));
}

In Swagger UI, click Authorize, paste a Bearer <token> when your login flow
returns JWT.
---

11.6 Quick Test Checklist

Start app (dev profile) → open /swagger-ui.html

Try:

GET /api/products — see schema + sample

POST /api/products — create a product

Stop app, run with prod profile → confirm Swagger disabled

Add V3 migration → restart → ensure Flyway applies it cleanly

---

11.7 What you’ve achieved

Clear, interactive API docs for devs, QA, and interviewers

Repeatable DB changes across local & cloud

Safe prod posture (profiles, limited docs, no schema drift)

---

✅ Exercise 11

1. Create V3__add_stock_to_product.sql adding stock INT DEFAULT 0 and


expose it in ProductDto.

2. Add Swagger annotations for Cart and Order controllers (tags, summaries,
responses).
3. Lock Swagger UI to ROLE_ADMIN in prod (or disable entirely) and test both
profiles.

---

If you’d like, next I can add Chapter 12 — Testing Strategy (Unit, Slice, and
Integration tests with JUnit 5 & Testcontainers) so your app has real test
coverage that also impresses interviewers. Would you like me to continue?

Awesome — let’s make your project testable and interview-ready. This chapter
gives you a complete testing stack for Spring Boot: unit, slice, and full
integration tests with JUnit 5, Mockito/MockMvc, and Testcontainers (real
database in Docker).

---

📖 CHAPTER 12 — Testing Strategy (JUnit 5, Mockito, Slices, Testcontainers)

12.1 Goals

Catch bugs early with fast unit tests.

Verify wiring and Spring config with slice tests.

Prove end-to-end behavior against a real DB via Testcontainers.

Run all of it in CI later without flakiness.

---

12.2 Add test dependencies (pom.xml)

<dependencies>
<!-- Spring Boot test bundle: JUnit 5, AssertJ, Mockito, MockMvc, JSONassert
-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- Testcontainers (choose your DB: mysql or postgresql) -->


<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId> <!-- or postgresql -->
<scope>test</scope>
</dependency>
</dependencies>

<!-- (Optional) Split unit/integration phases -->


<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<configuration>
<useModulePath>false</useModulePath>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.2.5</version>
<executions>
<execution>
<goals><goal>integration-test</goal><goal>verify</goal></goals>
<configuration>
<includes>
<include>**/*IT.java</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
> Keep Flyway enabled (from Chapter 11). Tests will apply migrations
automatically to the container DB.

---

12.3 Unit tests (pure Java, no Spring context)

What: Test a class in isolation with mocks; super fast (<10ms).


When: Services with business rules, mappers, utilities.

Example — ProductServiceTest.java

@ExtendWith(MockitoExtension.class)
class ProductServiceTest {

@Mock private ProductRepository repo;


@InjectMocks private ProductService service;

@Test
void createsProduct_withDefaultStock() {
CreateProductRequest req = new CreateProductRequest("Phone X", "Great",
49999.0, null);
Product saved = new Product(1L, "Phone X", "Great", 49999.0, null);
when(repo.save(any(Product.class))).thenReturn(saved);

ProductDto result = service.create(req);

assertThat(result.getId()).isEqualTo(1L);
verify(repo).save(argThat(p -> p.getName().equals("Phone X")));
}
}

Tips

Prefer constructor injection in your code → easier to unit test (no reflection).

Use AssertJ’s fluent assertions (assertThat).

Only mock your direct dependencies.


---

12.4 Slice tests (Spring “slices” load)

What: Load just the layer you’re testing with Spring auto-config for that layer.
Types: @DataJpaTest, @WebMvcTest, @JsonTest, etc.

A) Repository slice — @DataJpaTest

Loads JPA + Spring Data; uses embedded DB by default.

For realistic SQL, we’ll point it to Testcontainers (Section 12.6) or keep it


embedded for speed.

@DataJpaTest
class ProductRepositoryTest {

@Autowired private TestEntityManager em;


@Autowired private ProductRepository repo;

@Test
void findsById_afterPersist() {
Product p = new Product(null, "Book", "Nice", 499.0, null);
Long id = em.persistAndGetId(p, Long.class);
em.flush();

Optional<Product> found = repo.findById(id);


assertThat(found).isPresent();
assertThat(found.get().getName()).isEqualTo("Book");
}
}

B) Controller slice — @WebMvcTest

Loads MVC infra + your controller, not services or DB (you mock them).

@WebMvcTest(ProductController.class)
class ProductControllerTest {

@Autowired private MockMvc mvc;


@MockBean private ProductService service;

@Test
void listsProducts_asJson() throws Exception {
when(service.findAll()).thenReturn(List.of(new ProductDto(1L,"Phone",
"Great",49999.0,null,0)));
mvc.perform(get("/api/products"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].name").value("Phone"));
}
}

---

12.5 Full integration tests (@SpringBootTest)

What: Boot up the whole app with an actual DB (Testcontainers), real HTTP via
MockMvc or WebTestClient.
Purpose: Confidence that configuration, security, MVC, JPA, and Flyway all
work together.

@SpringBootTest
@AutoConfigureMockMvc
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
class ShopEaseIT {

@Autowired MockMvc mvc;

@Test @Order(1)
void healthEndpoint_works() throws Exception {
mvc.perform(get("/actuator/health"))
.andExpect(status().isOk());
}
}

We’ll wire this to a real DB next.

---

12.6 Testcontainers for real DB

Run tests against MySQL or PostgreSQL in Docker. Migrations (V1, V2, …)


apply automatically.

Reusable base class — AbstractContainerIT.java

@Testcontainers
public abstract class AbstractContainerIT {

// Choose one:
static MySQLContainer<?> DB = new MySQLContainer<>("mysql:8.4.0");
// static PostgreSQLContainer<?> DB = new
PostgreSQLContainer<>("postgres:16");

@DynamicPropertySource
static void registerProps(DynamicPropertyRegistry r) {
DB.start(); // starts once per JVM
r.add("spring.datasource.url", DB::getJdbcUrl);
r.add("spring.datasource.username", DB::getUsername);
r.add("spring.datasource.password", DB::getPassword);
r.add("spring.jpa.hibernate.ddl-auto", () -> "validate"); // Flyway manages
schema
}
}

End-to-end test using real DB — ProductApiIT.java

@SpringBootTest
@AutoConfigureMockMvc
class ProductApiIT extends AbstractContainerIT {

@Autowired MockMvc mvc;

@Test
void createAndFetchProduct() throws Exception {
String body = """
{"name":"Phone Z","description":"Solid","price":29999.0,"imageUrl":null}
""";

// Create
mvc.perform(post("/api/products")
.contentType(MediaType.APPLICATION_JSON)
.content(body))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").isNumber());
// List
mvc.perform(get("/api/products"))
.andExpect(status().isOk())
.andExpect(jsonPath("$
[*].name").value(org.hamcrest.Matchers.hasItem("Phone Z")));
}
}

Notes

The first test run pulls the container image (slower). Subsequent runs are fast.

For CI, Docker must be available (GitHub Actions supports it).

---

12.7 Security tests (login/roles)

Use @WithMockUser to simulate authenticated users.

@WebMvcTest(AdminController.class)
class AdminSecurityTest {

@Autowired MockMvc mvc;


@MockBean ProductService service;

@Test
@WithMockUser(roles = "ADMIN")
void adminCanAccess() throws Exception {
mvc.perform(get("/admin/products"))
.andExpect(status().isOk());
}

@Test
@WithMockUser(roles = "USER")
void userForbiddenOnAdmin() throws Exception {
mvc.perform(get("/admin/products"))
.andExpect(status().isForbidden());
}
}
---

12.8 Testing with Flyway data

Seed test data via a test-only migration or data.sql under test resources.

Option A — test migrations

src/test/resources/db/migration/
V999__test_seed.sql

Option B — data.sql

src/test/resources/data.sql
INSERT INTO product(name, price) VALUES ('Seed Item', 1000.00);

> Flyway runs main migrations first, then Spring runs data.sql.

---

12.9 Fast feedback vs. deep confidence

Unit & slices: run on every save (super quick).

Integration (IT): run on push/PR or nightly (use Failsafe with *IT.java).

Run locally:

# Unit + slice
mvn -q -Dtest='*Test' test

# Full integration suite (spins up containers)


mvn -q -DskipTests=false verify

---

12.10 Optional: GitHub Actions CI (sketch)

name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
services:
mysql:
image: mysql:8.4
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: shopease
ports: ["3306:3306"]
options: >-
--health-cmd="mysqladmin ping -proot"
--health-interval=10s --health-timeout=5s --health-retries=5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 21
- name: Build & test
run: mvn -B verify

(Or use Testcontainers with Docker enabled; you don’t need the services:
block then.)

---

12.11 Coverage & quality tips

Focus on core business rules (service) and critical flows (login, checkout).

Use parameterized tests for varied inputs.

Keep tests independent (no hidden state).

Fail fast: meaningful assertion messages.

---

✅ Exercise 12
1. Add a service unit test for cart total calculation, covering: empty cart,
single item, multi-item, discount.

2. Add a @DataJpaTest verifying a custom query (e.g.,


findByNameContainingIgnoreCase).

3. Create a Testcontainers integration test for the checkout flow: seed a user
+ product, POST /checkout, assert an orders row is created and cart becomes
empty.

4. Add 2 security tests proving ROLE_USER cannot access /admin/**.

---

With this chapter, your app has a professional testing setup.


Want me to add Chapter 13 — Performance & Caching (Spring Cache, Redis,
pagination, N+1 fixes) or Chapter 13 — Observability (Actuator metrics, logs,
tracing) next?

You might also like