<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gianfranco Coppola</title>
    <description>The latest articles on DEV Community by Gianfranco Coppola (@gianfcop98).</description>
    <link>https://dev.to/gianfcop98</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3454723%2F559258c7-dc46-4181-9b57-320bb8cafcc8.jpg</url>
      <title>DEV Community: Gianfranco Coppola</title>
      <link>https://dev.to/gianfcop98</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gianfcop98"/>
    <language>en</language>
    <item>
      <title>Transactions in Spring Boot: What `@Transactional` Really Does (and Why It Matters)</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Sun, 01 Feb 2026 21:47:54 +0000</pubDate>
      <link>https://dev.to/gianfcop98/transactions-in-spring-boot-what-transactional-really-does-and-why-it-matters-56a6</link>
      <guid>https://dev.to/gianfcop98/transactions-in-spring-boot-what-transactional-really-does-and-why-it-matters-56a6</guid>
      <description>&lt;h2&gt;
  
  
  1. Introduction: Why Transactions Matter More Than You Think
&lt;/h2&gt;

&lt;p&gt;If you’ve been working with Spring Boot for a while, chances are you’ve already used &lt;code&gt;@Transactional&lt;/code&gt;.&lt;br&gt;&lt;br&gt;
Maybe you added it because “that’s what everyone does”, or because a tutorial told you so.&lt;/p&gt;

&lt;p&gt;And most of the time… things &lt;em&gt;seem&lt;/em&gt; to work.&lt;/p&gt;

&lt;p&gt;Until one day:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a payment is charged but the order is not created&lt;/li&gt;
&lt;li&gt;a user is created, but their profile is missing&lt;/li&gt;
&lt;li&gt;a retry suddenly creates duplicate data&lt;/li&gt;
&lt;li&gt;or worse: production data ends up in an inconsistent state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s usually the moment when you realize that &lt;strong&gt;transactions are not just a database feature — they are a core business concept&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In backend development, especially in stateful systems, &lt;strong&gt;partial success is often worse than total failure&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
A transaction is what allows you to say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Either everything succeeds, or nothing does.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Spring Boot makes transactions &lt;em&gt;easy to use&lt;/em&gt;, but also &lt;em&gt;easy to misunderstand&lt;/em&gt;.&lt;br&gt;&lt;br&gt;
And misunderstanding them can lead to subtle bugs that are extremely hard to debug.&lt;/p&gt;

&lt;p&gt;In this article, we’ll start from the basics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what a transaction really is&lt;/li&gt;
&lt;li&gt;why it is fundamental in backend systems&lt;/li&gt;
&lt;li&gt;and how this concept maps to Spring Boot and &lt;code&gt;@Transactional&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before touching any annotation, we need to get the mental model right.&lt;/p&gt;


&lt;h2&gt;
  
  
  2. What Is a Transaction? (The Basics, Done Right)
&lt;/h2&gt;

&lt;p&gt;At its core, a &lt;strong&gt;transaction&lt;/strong&gt; is a logical unit of work.&lt;/p&gt;

&lt;p&gt;It groups multiple operations into a &lt;strong&gt;single, indivisible action&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think about a very common backend use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an order&lt;/li&gt;
&lt;li&gt;decrease product stock&lt;/li&gt;
&lt;li&gt;save a payment record&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If one of these steps fails, the system should not be left in a half-completed state.&lt;/p&gt;

&lt;p&gt;Without transactions, your system might end up like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✔ Order created
✔ Payment saved
✘ Stock update failed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s not a technical issue.&lt;br&gt;&lt;br&gt;
That’s a &lt;strong&gt;business problem&lt;/strong&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  The ACID Properties
&lt;/h3&gt;

&lt;p&gt;Transactions are usually described using the &lt;strong&gt;ACID&lt;/strong&gt; acronym. You’ve probably heard of it, but let’s translate it into practical backend terms.&lt;/p&gt;
&lt;h4&gt;
  
  
  Atomicity
&lt;/h4&gt;

&lt;p&gt;“All or nothing.”&lt;/p&gt;

&lt;p&gt;Either all operations inside the transaction succeed, or all of them are rolled back.&lt;br&gt;&lt;br&gt;
No partial updates, no “we’ll fix it later”.&lt;/p&gt;
&lt;h4&gt;
  
  
  Consistency
&lt;/h4&gt;

&lt;p&gt;The system moves from one valid state to another valid state.&lt;/p&gt;

&lt;p&gt;Constraints, invariants, and business rules must always hold — &lt;em&gt;before and after&lt;/em&gt; the transaction.&lt;/p&gt;
&lt;h4&gt;
  
  
  Isolation
&lt;/h4&gt;

&lt;p&gt;Concurrent transactions should not step on each other’s toes.&lt;/p&gt;

&lt;p&gt;What one transaction sees (or doesn’t see) from another transaction is controlled and predictable.&lt;/p&gt;

&lt;p&gt;This is where things start getting tricky — and interesting.&lt;/p&gt;
&lt;h4&gt;
  
  
  Durability
&lt;/h4&gt;

&lt;p&gt;Once a transaction is committed, it stays committed.&lt;/p&gt;

&lt;p&gt;Even if the application crashes right after, the data is there.&lt;/p&gt;


&lt;h3&gt;
  
  
  Transactions Are Not Just About Databases
&lt;/h3&gt;

&lt;p&gt;This is an important point that often gets overlooked.&lt;/p&gt;

&lt;p&gt;Transactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;are &lt;strong&gt;implemented by the database&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;but &lt;strong&gt;defined by your business logic&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The database doesn’t know what an “order” or a “payment” is.&lt;br&gt;&lt;br&gt;
It only knows rows, tables, and constraints.&lt;/p&gt;

&lt;p&gt;It’s the backend application that decides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what belongs together&lt;/li&gt;
&lt;li&gt;where the transactional boundary starts&lt;/li&gt;
&lt;li&gt;and where it ends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is exactly why frameworks like Spring exist:&lt;br&gt;&lt;br&gt;
to help you &lt;strong&gt;define transactional boundaries in your application code&lt;/strong&gt;, not in SQL scripts.&lt;/p&gt;


&lt;h3&gt;
  
  
  Why Transactions Are Fundamental in Backend Systems
&lt;/h3&gt;

&lt;p&gt;Modern backend systems are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concurrent&lt;/li&gt;
&lt;li&gt;stateful&lt;/li&gt;
&lt;li&gt;failure-prone by nature

&lt;ul&gt;
&lt;li&gt;Networks fail.&lt;/li&gt;
&lt;li&gt;Databases timeout.&lt;/li&gt;
&lt;li&gt;External services go down.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Transactions are your &lt;strong&gt;last line of defense&lt;/strong&gt; against data corruption.&lt;/p&gt;

&lt;p&gt;They don’t make failures disappear — but they make failures &lt;em&gt;safe&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;And that’s the key idea we’ll carry into Spring Boot and &lt;code&gt;@Transactional&lt;/code&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  3. Transactions in Spring Boot: The Big Picture
&lt;/h2&gt;

&lt;p&gt;Before diving deeper into &lt;code&gt;@Transactional&lt;/code&gt;, it’s important to understand &lt;strong&gt;how Spring manages transactions at a high level&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Not the full internal implementation — just enough to avoid the most common (and painful) mistakes.&lt;/p&gt;

&lt;p&gt;Because with transactions in Spring, &lt;strong&gt;the “how” matters almost as much as the “what”&lt;/strong&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  Declarative vs Programmatic Transactions
&lt;/h3&gt;

&lt;p&gt;Spring supports two ways of managing transactions:&lt;/p&gt;
&lt;h4&gt;
  
  
  1. Programmatic transactions
&lt;/h4&gt;

&lt;p&gt;You explicitly start, commit, and rollback a transaction in code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TransactionStatus&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;transactionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTransaction&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;definition&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// business logic&lt;/span&gt;
    &lt;span class="n"&gt;transactionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commit&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;transactionManager&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;rollback&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works, but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it’s verbose&lt;/li&gt;
&lt;li&gt;it mixes infrastructure concerns with business logic&lt;/li&gt;
&lt;li&gt;it doesn’t scale well in complex services&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; use it, but in most Spring Boot applications, you shouldn’t.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Declarative transactions (the Spring way)
&lt;/h4&gt;

&lt;p&gt;This is where &lt;code&gt;@Transactional&lt;/code&gt; comes in.&lt;/p&gt;

&lt;p&gt;You declare &lt;strong&gt;what should be transactional&lt;/strong&gt;, not &lt;em&gt;how&lt;/em&gt; to manage the transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// business logic&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring takes care of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;opening the transaction&lt;/li&gt;
&lt;li&gt;committing it if everything goes well&lt;/li&gt;
&lt;li&gt;rolling it back if something goes wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This separation of concerns is one of the reasons why Spring-based backends are so readable and maintainable.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Role of &lt;code&gt;PlatformTransactionManager&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;At runtime, Spring delegates all transaction operations to a &lt;strong&gt;&lt;code&gt;PlatformTransactionManager&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Think of it as an abstraction layer between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your application&lt;/li&gt;
&lt;li&gt;and the actual transaction implementation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Depending on what you use, Spring will plug in a different implementation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DataSourceTransactionManager&lt;/code&gt; → JDBC&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;JpaTransactionManager&lt;/code&gt; → JPA / Hibernate&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ReactiveTransactionManager&lt;/code&gt; → reactive stacks&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This abstraction is what allows you to write &lt;strong&gt;framework-agnostic transactional code&lt;/strong&gt;, while still being tightly integrated with your persistence technology.&lt;/p&gt;

&lt;p&gt;You almost never interact with it directly — but it’s always there.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. What Actually Happens When You Use &lt;code&gt;@Transactional&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;At first glance, &lt;code&gt;@Transactional&lt;/code&gt; looks deceptively simple.&lt;/p&gt;

&lt;p&gt;You put it on a method, and magically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a transaction starts&lt;/li&gt;
&lt;li&gt;your logic runs&lt;/li&gt;
&lt;li&gt;everything is committed or rolled back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And in &lt;em&gt;happy-path demos&lt;/em&gt;, that’s exactly what happens.&lt;/p&gt;

&lt;p&gt;In real-world applications, however, &lt;strong&gt;where and how you use &lt;code&gt;@Transactional&lt;/code&gt; makes a huge difference&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s break it down.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Simplest (and Most Common) Use Case
&lt;/h3&gt;

&lt;p&gt;The most basic usage looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;paymentRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payment&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an exception is thrown during the method execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring marks the transaction for rollback&lt;/li&gt;
&lt;li&gt;all database changes are reverted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the method completes successfully:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the transaction is committed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So far, so good.&lt;/p&gt;

&lt;p&gt;But this simplicity hides a lot of assumptions.&lt;/p&gt;

&lt;p&gt;One of the biggest misconceptions is thinking of &lt;code&gt;@Transactional&lt;/code&gt; as something that &lt;em&gt;adds behavior&lt;/em&gt;.&lt;br&gt;
It doesn’t. It defines a &lt;strong&gt;transactional boundary&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;In other words, when you annotate a method with &lt;code&gt;@Transactional&lt;/code&gt;, &lt;strong&gt;Spring does NOT modify your method&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Instead, Spring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; creates a &lt;strong&gt;proxy&lt;/strong&gt; around your bean&lt;/li&gt;
&lt;li&gt; intercepts calls to transactional methods&lt;/li&gt;
&lt;li&gt; starts a transaction &lt;em&gt;before&lt;/em&gt; the method execution&lt;/li&gt;
&lt;li&gt; commits or rolls back &lt;em&gt;after&lt;/em&gt; the method returns or throws an exception&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is done using &lt;strong&gt;Spring AOP (Aspect-Oriented Programming)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In practice, the flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Client → Spring Proxy → Transaction Interceptor
        → Your Method → Transaction Commit / Rollback
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is this important?&lt;/p&gt;

&lt;p&gt;Because &lt;strong&gt;only method calls that go through the proxy are transactional&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This single sentence explains:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;why self-invocation doesn’t work&lt;/li&gt;
&lt;li&gt;why &lt;code&gt;@Transactional&lt;/code&gt; on &lt;code&gt;private&lt;/code&gt; methods is ignored&lt;/li&gt;
&lt;li&gt;why calling a transactional method from the same class can silently break everything&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’ll get back to this later in the pitfalls section, but keep this mental model in mind.&lt;/p&gt;




&lt;h3&gt;
  
  
  Where &lt;code&gt;@Transactional&lt;/code&gt; Should Live (and Where It Shouldn’t)
&lt;/h3&gt;

&lt;p&gt;A common question is: &lt;em&gt;where do I put &lt;code&gt;@Transactional&lt;/code&gt;?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The short answer:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;On service-layer methods that define a business operation.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Typically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Controllers → no business logic, no transactions&lt;/li&gt;
&lt;li&gt;⚠️ Repositories → usually too low-level&lt;/li&gt;
&lt;li&gt;✅ Services → perfect place for transactional boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CreateOrderCommand&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// validate input&lt;/span&gt;
        &lt;span class="c1"&gt;// persist order&lt;/span&gt;
        &lt;span class="c1"&gt;// update stock&lt;/span&gt;
        &lt;span class="c1"&gt;// trigger side effects&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes your transactional boundary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;explicit&lt;/li&gt;
&lt;li&gt;easy to reason about&lt;/li&gt;
&lt;li&gt;aligned with business use cases&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Class-Level vs Method-Level &lt;code&gt;@Transactional&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Spring allows you to annotate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a single method&lt;/li&gt;
&lt;li&gt;or the entire class
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;every public method&lt;/strong&gt; is transactional by default&lt;/li&gt;
&lt;li&gt;unless overridden at method level&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can be useful, but it’s also dangerous if overused.&lt;/p&gt;

&lt;p&gt;From experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;class-level works well for &lt;em&gt;simple&lt;/em&gt; services&lt;/li&gt;
&lt;li&gt;method-level is safer for complex ones&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Explicit is better than implicit — especially with transactions.&lt;/p&gt;




&lt;p&gt;At this point, we have a solid foundation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what a transaction is&lt;/li&gt;
&lt;li&gt;why it matters&lt;/li&gt;
&lt;li&gt;how Spring manages it under the hood&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. Understanding &lt;code&gt;@Transactional&lt;/code&gt; Attributes (The Real Ones)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@Transactional&lt;/code&gt; is not just an on/off switch.&lt;/p&gt;

&lt;p&gt;Behind that single annotation there are &lt;strong&gt;rules that control how transactions behave&lt;/strong&gt;, especially when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple methods interact&lt;/li&gt;
&lt;li&gt;exceptions are thrown&lt;/li&gt;
&lt;li&gt;concurrent operations happen&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most bugs related to transactions come from &lt;strong&gt;default assumptions&lt;/strong&gt; that turn out to be wrong.&lt;/p&gt;

&lt;p&gt;Let’s go through the attributes that actually matter in real projects.&lt;/p&gt;




&lt;h3&gt;
  
  
  5.1 Propagation: How Transactions Interact with Each Other
&lt;/h3&gt;

&lt;p&gt;Propagation defines &lt;strong&gt;what happens when a transactional method is called from another transactional method&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is by far the most important attribute.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;code&gt;REQUIRED&lt;/code&gt; (Default)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUIRED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;join the existing transaction if there is one&lt;/li&gt;
&lt;li&gt;otherwise, create a new transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is what you want &lt;strong&gt;most of the time&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;saveOrder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;charge()&lt;/code&gt; fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the entire transaction rolls back&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;nothing is persisted&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is usually correct and desirable.&lt;/p&gt;




&lt;h4&gt;
  
  
  &lt;code&gt;REQUIRES_NEW&lt;/code&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;propagation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Propagation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;REQUIRES_NEW&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Meaning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;suspend the existing transaction&lt;/li&gt;
&lt;li&gt;start a completely new one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Classic use case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;audit logs&lt;/li&gt;
&lt;li&gt;technical events&lt;/li&gt;
&lt;li&gt;actions that must persist &lt;strong&gt;even if the main transaction fails&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;auditService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;logOrderAttempt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// REQUIRES_NEW&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;order → rolled back&lt;/li&gt;
&lt;li&gt;audit log → committed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is powerful — and dangerous if misused.&lt;/p&gt;




&lt;h4&gt;
  
  
  Other Propagation Types (Quick but Honest)
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SUPPORTS&lt;/code&gt; → join if exists, otherwise run non-transactionally&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MANDATORY&lt;/code&gt; → fail if no transaction exists&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NOT_SUPPORTED&lt;/code&gt; → suspend any existing transaction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NEVER&lt;/code&gt; → fail if a transaction exists&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NESTED&lt;/code&gt; → savepoints (DB-dependent)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 In most applications:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;REQUIRED&lt;/code&gt; and &lt;code&gt;REQUIRES_NEW&lt;/code&gt; cover 95% of use cases&lt;/li&gt;
&lt;li&gt;the others are niche and should be used intentionally&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5.2 Isolation: How Much You See of Other Transactions
&lt;/h3&gt;

&lt;p&gt;Isolation defines &lt;strong&gt;how concurrent transactions affect each other&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isolation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Isolation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;DEFAULT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This delegates to the database default (often &lt;code&gt;READ_COMMITTED&lt;/code&gt;).&lt;/p&gt;

&lt;h4&gt;
  
  
  The Practical Levels
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;READ_COMMITTED&lt;/strong&gt;
You only see committed data.
Good balance between consistency and performance.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;REPEATABLE_READ&lt;/strong&gt;
Data read once won’t change during the transaction.
Prevents non-repeatable reads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SERIALIZABLE&lt;/strong&gt;
Full isolation. Transactions behave as if executed sequentially.
Very safe. Very expensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Higher isolation = fewer anomalies = lower throughput.&lt;/p&gt;

&lt;p&gt;👉 Rule of thumb:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;trust your DB defaults&lt;/li&gt;
&lt;li&gt;increase isolation only when you have a &lt;em&gt;proven&lt;/em&gt; concurrency problem&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  5.3 Rollback Rules: The Most Common Source of Bugs
&lt;/h3&gt;

&lt;p&gt;This is where many Spring developers get burned.&lt;/p&gt;

&lt;p&gt;By default:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Spring rolls back only on unchecked exceptions (&lt;code&gt;RuntimeException&lt;/code&gt;) and &lt;code&gt;Error&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That means this will &lt;strong&gt;NOT rollback&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Checked exception"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a business perspective, this operation &lt;strong&gt;clearly failed&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
From Spring’s perspective, however, this is a &lt;em&gt;checked exception&lt;/em&gt; — and the transaction is committed.&lt;/p&gt;

&lt;p&gt;No rollback. No warning. Just inconsistent data.&lt;br&gt;
Yes, really.&lt;/p&gt;
&lt;h4&gt;
  
  
  Explicit Rollback Rules
&lt;/h4&gt;

&lt;p&gt;You can override this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rollbackFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or the opposite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;noRollbackFor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;BusinessException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is essential when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using checked exceptions&lt;/li&gt;
&lt;li&gt;modeling business failures explicitly&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  A Better Approach: Business Exceptions as Runtime Exceptions
&lt;/h4&gt;

&lt;p&gt;In most real-world Spring Boot applications, business failures &lt;strong&gt;should invalidate the transaction&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A clean and effective way to model this is by using &lt;strong&gt;custom unchecked exceptions&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaymentFailedException&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;RuntimeException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;PaymentFailedException&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach has several advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rollback happens automatically&lt;/li&gt;
&lt;li&gt;transactional behavior is explicit&lt;/li&gt;
&lt;li&gt;no need for extra configuration&lt;/li&gt;
&lt;li&gt;business intent is clear&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the operation fails, the transaction fails. No ambiguity.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Always be explicit&lt;/strong&gt; if you rely on checked exceptions.&lt;/p&gt;




&lt;h3&gt;
  
  
  5.4 &lt;code&gt;readOnly&lt;/code&gt;: Small Flag, Big Impact
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;readOnly&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;hints the persistence provider&lt;/li&gt;
&lt;li&gt;may optimize flushing and dirty checking&lt;/li&gt;
&lt;li&gt;documents intent clearly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perfect for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query-only service methods&lt;/li&gt;
&lt;li&gt;read-heavy paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not a silver bullet — but a good habit.&lt;/p&gt;




&lt;h3&gt;
  
  
  5.5 &lt;code&gt;timeout&lt;/code&gt;: A Safety Net
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the transaction runs longer than 5 seconds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it’s rolled back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Useful for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;protecting DB resources&lt;/li&gt;
&lt;li&gt;preventing stuck transactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Especially relevant under load.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Hard-Earned Lesson
&lt;/h3&gt;

&lt;p&gt;Most transactional bugs are not caused by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wrong SQL&lt;/li&gt;
&lt;li&gt;broken databases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They come from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;wrong assumptions about propagation&lt;/li&gt;
&lt;li&gt;unexpected rollback behavior&lt;/li&gt;
&lt;li&gt;hidden transactional boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Understanding these attributes turns &lt;code&gt;@Transactional&lt;/code&gt; from a “magic annotation” into a &lt;strong&gt;precise tool&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Common Transactional Pitfalls in Spring Boot
&lt;/h2&gt;

&lt;p&gt;At this point, we understand how transactions &lt;em&gt;should&lt;/em&gt; work.&lt;/p&gt;

&lt;p&gt;Unfortunately, many transactional bugs don’t come from a lack of knowledge —&lt;br&gt;&lt;br&gt;
they come from &lt;strong&gt;small details that are easy to miss and hard to debug&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s go through the most common pitfalls you’ll encounter in real Spring Boot applications.&lt;/p&gt;


&lt;h3&gt;
  
  
  6.1 Self-Invocation: The Silent Transaction Killer
&lt;/h3&gt;

&lt;p&gt;This is probably &lt;strong&gt;the most famous Spring transactional pitfall&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;saveOrder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ❌ no transaction&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saveOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this looks fine.&lt;/p&gt;

&lt;p&gt;It’s not.&lt;/p&gt;

&lt;h4&gt;
  
  
  What’s the problem?
&lt;/h4&gt;

&lt;p&gt;The call to &lt;code&gt;saveOrder()&lt;/code&gt; happens &lt;strong&gt;inside the same class&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
It never goes through the Spring proxy.&lt;/p&gt;

&lt;p&gt;Result:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Transactional&lt;/code&gt; is completely ignored&lt;/li&gt;
&lt;li&gt;no transaction is started&lt;/li&gt;
&lt;li&gt;no error, no warning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is one of the reasons transactional bugs feel “random”.&lt;/p&gt;
&lt;h4&gt;
  
  
  How to fix it
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;move the transactional method to another bean&lt;/li&gt;
&lt;li&gt;or make sure it’s called from outside the class
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderPersistenceService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Transactional&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saveOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 &lt;code&gt;@Transactional&lt;/code&gt; on &lt;code&gt;private&lt;/code&gt; (or non-public) Methods
&lt;/h3&gt;

&lt;p&gt;Another classic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;saveOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will &lt;strong&gt;never work&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Spring proxies intercept &lt;strong&gt;public method calls only&lt;/strong&gt; (by default).&lt;br&gt;&lt;br&gt;
Private, protected, or package-private methods are ignored.&lt;/p&gt;

&lt;p&gt;Again:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no exception&lt;/li&gt;
&lt;li&gt;no warning&lt;/li&gt;
&lt;li&gt;just no transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 &lt;strong&gt;Transactional methods must be public. Always.&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  6.3 Catching Exceptions and Accidentally Preventing Rollback
&lt;/h3&gt;

&lt;p&gt;This one is subtle — and extremely common.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;paymentService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;charge&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PaymentFailedException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks harmless, right?&lt;/p&gt;

&lt;p&gt;But now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the exception is swallowed&lt;/li&gt;
&lt;li&gt;the method completes normally&lt;/li&gt;
&lt;li&gt;the transaction is &lt;strong&gt;committed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spring rolls back &lt;strong&gt;only if the exception escapes the transactional boundary&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Correct approaches
&lt;/h4&gt;

&lt;p&gt;Either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rethrow the exception&lt;/li&gt;
&lt;li&gt;or mark the transaction for rollback manually
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;TransactionAspectSupport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;currentTransactionStatus&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;setRollbackOnly&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But in most cases, &lt;strong&gt;rethowing is the cleanest solution&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  6.4 Mixing Transactional and Non-Transactional Logic
&lt;/h3&gt;

&lt;p&gt;Sometimes transactional methods grow too much:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;emailService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendConfirmationEmail&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// ❌&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Problems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;emails are slow&lt;/li&gt;
&lt;li&gt;emails can fail&lt;/li&gt;
&lt;li&gt;emails should not be part of a DB transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the email fails:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;do you really want to rollback the order?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Probably not.&lt;/p&gt;

&lt;h4&gt;
  
  
  Better approach
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;keep transactions short&lt;/li&gt;
&lt;li&gt;isolate side effects&lt;/li&gt;
&lt;li&gt;use events or async processing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Transactional boundaries should protect &lt;strong&gt;data consistency&lt;/strong&gt;, not external systems.&lt;/p&gt;




&lt;h3&gt;
  
  
  6.5 Unexpected Rollbacks (&lt;code&gt;UnexpectedRollbackException&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;Sooner or later, you’ll see this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UnexpectedRollbackException: Transaction silently rolled back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This usually happens when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an inner transactional method marks the transaction as rollback-only&lt;/li&gt;
&lt;li&gt;but the outer method tries to commit&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;caught exceptions inside nested transactional calls&lt;/li&gt;
&lt;li&gt;mixed propagation settings&lt;/li&gt;
&lt;li&gt;overuse of &lt;code&gt;REQUIRES_NEW&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you see this exception:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;don’t look at the commit&lt;/li&gt;
&lt;li&gt;look at &lt;strong&gt;what marked the transaction as rollback-only earlier&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  6.6 Long-Running Transactions
&lt;/h3&gt;

&lt;p&gt;Technically correct. Practically dangerous.&lt;/p&gt;

&lt;p&gt;Long transactions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lock rows for too long&lt;/li&gt;
&lt;li&gt;reduce throughput&lt;/li&gt;
&lt;li&gt;increase deadlock probability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Common causes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;doing I/O inside transactions&lt;/li&gt;
&lt;li&gt;calling remote services&lt;/li&gt;
&lt;li&gt;waiting for user input (yes, it happens…)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Transactions should be as short as possible, but as long as necessary.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  A Pattern You’ll Start to Recognize
&lt;/h3&gt;

&lt;p&gt;Most transactional problems share a common theme:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the code &lt;em&gt;looks&lt;/em&gt; correct&lt;/li&gt;
&lt;li&gt;the behavior is implicit&lt;/li&gt;
&lt;li&gt;Spring does exactly what you told it to do — not what you meant&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;understanding proxies&lt;/li&gt;
&lt;li&gt;defining clear boundaries&lt;/li&gt;
&lt;li&gt;modeling failures explicitly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…is more important than memorizing annotations.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. &lt;code&gt;@Transactional&lt;/code&gt; and &lt;code&gt;@Async&lt;/code&gt;: A Dangerous Combination
&lt;/h2&gt;

&lt;p&gt;At some point, almost every Spring Boot developer tries to combine:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@Transactional&lt;/code&gt; → consistency&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Async&lt;/code&gt; → performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On paper, it sounds like a great idea.&lt;/p&gt;

&lt;p&gt;In practice, it’s one of the &lt;strong&gt;most misunderstood and dangerous combinations&lt;/strong&gt; in Spring.&lt;/p&gt;

&lt;p&gt;Let’s clear things up.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Core Problem: Different Threads, Different Transactions
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@Transactional&lt;/code&gt; is &lt;strong&gt;thread-bound&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A transaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;is associated with the current thread&lt;/li&gt;
&lt;li&gt;lives and dies inside that thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;@Async&lt;/code&gt;, on the other hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;executes the method in a &lt;strong&gt;different thread&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;outside the original call stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;asyncService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sendConfirmationEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Async&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;sendConfirmationEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;does &lt;strong&gt;not&lt;/strong&gt; mean:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“send the email in the same transaction, but asynchronously”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“start a completely separate execution, with no transaction at all (unless explicitly defined)”&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  The Most Common Wrong Assumption
&lt;/h3&gt;

&lt;p&gt;Many developers assume:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“If the async method is called from a transactional one, it participates in the same transaction.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It doesn’t.&lt;/p&gt;

&lt;p&gt;Ever.&lt;/p&gt;

&lt;p&gt;Different thread = different transactional context.&lt;/p&gt;




&lt;h3&gt;
  
  
  What Happens in Practice
&lt;/h3&gt;

&lt;p&gt;Let’s look at a slightly more subtle example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;asyncService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notifyWarehouse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;RuntimeException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Payment failed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Possible outcome:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transaction rolls back&lt;/li&gt;
&lt;li&gt;order is NOT persisted&lt;/li&gt;
&lt;li&gt;async method &lt;strong&gt;still runs&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;warehouse is notified about an order that doesn’t exist&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is how distributed inconsistencies are born.&lt;/p&gt;




&lt;h3&gt;
  
  
  Making &lt;code&gt;@Async&lt;/code&gt; Transactional (Yes, But…)
&lt;/h3&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; put &lt;code&gt;@Transactional&lt;/code&gt; on an async method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Async&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;notifyWarehouse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a &lt;strong&gt;new transaction&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;completely independent from the original one&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This might be fine — or disastrous — depending on intent.&lt;/p&gt;

&lt;p&gt;Again: &lt;strong&gt;there is no shared transaction&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Better Patterns Than &lt;code&gt;@Transactional + @Async&lt;/code&gt;
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Transactional Events
&lt;/h4&gt;

&lt;p&gt;Spring provides a much safer mechanism:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;placeOrder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;applicationEventPublisher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;publishEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OrderPlacedEvent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;order&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@TransactionalEventListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TransactionPhase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AFTER_COMMIT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onOrderPlaced&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderPlacedEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;sendEmail&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the event is processed &lt;strong&gt;only if the transaction commits&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;no ghost side effects&lt;/li&gt;
&lt;li&gt;no inconsistent state&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This pattern is gold.&lt;/p&gt;




&lt;h4&gt;
  
  
  2. Messaging / Event-Driven Architecture
&lt;/h4&gt;

&lt;p&gt;For more complex systems:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kafka&lt;/li&gt;
&lt;li&gt;RabbitMQ&lt;/li&gt;
&lt;li&gt;cloud queues&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Persist state first, then publish events.&lt;/p&gt;

&lt;p&gt;Transactions protect &lt;strong&gt;your database&lt;/strong&gt;, not the world.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Simple Rule That Saves a Lot of Pain
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Never assume an async operation is part of your transaction.&lt;br&gt;&lt;br&gt;
It never is.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If consistency matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;finish the transaction&lt;/li&gt;
&lt;li&gt;then trigger async behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Always in that order.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thought on &lt;code&gt;@Async&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@Async&lt;/code&gt; is not dangerous by itself.&lt;/p&gt;

&lt;p&gt;What’s dangerous is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;mixing it with transactions&lt;/li&gt;
&lt;li&gt;without understanding thread boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you internalize this model, the behavior becomes predictable — and safe.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Transaction Logging and Debugging
&lt;/h2&gt;

&lt;p&gt;One of the most frustrating things about transactional bugs is that &lt;strong&gt;everything looks fine until it’s not&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;No errors.&lt;br&gt;&lt;br&gt;
No stack traces.&lt;br&gt;&lt;br&gt;
Just data in the wrong state.&lt;/p&gt;

&lt;p&gt;When that happens, logging is often the &lt;strong&gt;only way to understand what Spring is actually doing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Let’s see how to make transactions visible.&lt;/p&gt;


&lt;h3&gt;
  
  
  Why Transactional Bugs Are Hard to Debug
&lt;/h3&gt;

&lt;p&gt;Transactional behavior is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implicit&lt;/li&gt;
&lt;li&gt;proxy-based&lt;/li&gt;
&lt;li&gt;spread across multiple layers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So when a transaction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;starts&lt;/li&gt;
&lt;li&gt;commits&lt;/li&gt;
&lt;li&gt;rolls back&lt;/li&gt;
&lt;li&gt;or is marked as rollback-only&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…it usually happens &lt;strong&gt;outside your business code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Without proper logs, you’re debugging blind.&lt;/p&gt;


&lt;h3&gt;
  
  
  Enabling Transaction Logs in Spring Boot
&lt;/h3&gt;

&lt;p&gt;Spring exposes very useful logs — you just need to turn them on.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;application.yml&lt;/code&gt; (or &lt;code&gt;application.properties&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;org.springframework.transaction&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This alone already shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;when a transaction is created&lt;/li&gt;
&lt;li&gt;when it’s committed&lt;/li&gt;
&lt;li&gt;when it’s rolled back&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Logging Transaction Boundaries
&lt;/h3&gt;

&lt;p&gt;With transaction logging enabled, you’ll start seeing logs like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Creating new transaction with name [OrderService.placeOrder]
Participating in existing transaction
Committing JDBC transaction
Rolling back JDBC transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This tells you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;where&lt;/strong&gt; the transaction starts&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;which method&lt;/strong&gt; owns it&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;how nested calls behave&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When debugging propagation issues, this is invaluable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Hibernate / JPA Logs: Seeing What Actually Hits the DB
&lt;/h3&gt;

&lt;p&gt;Transactions are about &lt;em&gt;when&lt;/em&gt; changes are flushed.&lt;/p&gt;

&lt;p&gt;To see &lt;em&gt;what&lt;/em&gt; is executed, enable SQL logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;org.hibernate.SQL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DEBUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Optionally, parameter binding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;logging&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;org.hibernate.type.descriptor.sql&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TRACE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can correlate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;transaction boundaries&lt;/li&gt;
&lt;li&gt;SQL statements&lt;/li&gt;
&lt;li&gt;commit / rollback events&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is often where inconsistencies finally make sense.&lt;/p&gt;




&lt;h3&gt;
  
  
  Debugging Rollbacks That “Come from Nowhere”
&lt;/h3&gt;

&lt;p&gt;If you’ve ever seen this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UnexpectedRollbackException: Transaction silently rolled back
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;it means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the transaction was marked as rollback-only earlier&lt;/li&gt;
&lt;li&gt;but the outer layer tried to commit it anyway&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To debug this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; enable transaction logs&lt;/li&gt;
&lt;li&gt; look for a &lt;code&gt;setRollbackOnly&lt;/code&gt; event&lt;/li&gt;
&lt;li&gt; check for swallowed exceptions&lt;/li&gt;
&lt;li&gt; inspect nested transactional methods&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The rollback never comes from nowhere — it’s just hidden.&lt;/p&gt;




&lt;h3&gt;
  
  
  Business Logging vs Transaction Logging
&lt;/h3&gt;

&lt;p&gt;A common mistake is relying only on business logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order placed successfully"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This log may appear:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;before&lt;/strong&gt; the transaction commits&lt;/li&gt;
&lt;li&gt;even if the transaction later rolls back&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need certainty, log:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;after commit&lt;/li&gt;
&lt;li&gt;or via transactional events
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@TransactionalEventListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;phase&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TransactionPhase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AFTER_COMMIT&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;onOrderCommitted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OrderPlacedEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order committed: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getOrderId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now your logs reflect &lt;strong&gt;reality&lt;/strong&gt;, not intent.&lt;/p&gt;




&lt;h3&gt;
  
  
  A Debugging Workflow That Actually Works
&lt;/h3&gt;

&lt;p&gt;When dealing with transactional issues:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Enable Spring transaction logs&lt;/li&gt;
&lt;li&gt; Enable SQL logs&lt;/li&gt;
&lt;li&gt; Identify transaction boundaries&lt;/li&gt;
&lt;li&gt; Track exception flow&lt;/li&gt;
&lt;li&gt; Verify commit / rollback timing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This approach turns “random behavior” into &lt;strong&gt;deterministic behavior&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Takeaway
&lt;/h3&gt;

&lt;p&gt;Transactions don’t fail silently.&lt;/p&gt;

&lt;p&gt;They fail &lt;em&gt;quietly&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Logging is what gives them a voice.&lt;/p&gt;

&lt;p&gt;Once you get used to reading transaction logs, you’ll start spotting problems &lt;strong&gt;before&lt;/strong&gt; they reach production.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Conclusion &amp;amp; Takeaways
&lt;/h2&gt;

&lt;p&gt;Transactions are one of the most powerful tools in Spring Boot — but they are also one of the most misunderstood.&lt;/p&gt;

&lt;p&gt;Here’s what you should remember:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Transactions are about business consistency&lt;/strong&gt;, not just database operations.
Define your transactional boundaries around &lt;em&gt;business operations&lt;/em&gt;, not technical details.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;@Transactional&lt;/code&gt; is declarative, but precise.&lt;/strong&gt;
Understand propagation, isolation, rollback rules, and method visibility.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exceptions drive rollbacks.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Unchecked exceptions → rollback by default&lt;/li&gt;
&lt;li&gt;Checked exceptions → explicit rollback required
Model your business exceptions carefully.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Avoid common pitfalls:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Self-invocation&lt;/li&gt;
&lt;li&gt;Private methods&lt;/li&gt;
&lt;li&gt;Catching exceptions and swallowing them&lt;/li&gt;
&lt;li&gt;Long-running transactions&lt;/li&gt;
&lt;li&gt;Mixing async and transactional logic without awareness&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Logging is your friend.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Enable transaction and SQL logging to debug propagation, rollback, and commit behavior. Use &lt;code&gt;@TransactionalEventListener&lt;/code&gt; for post-commit business logging.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Async and transactions are tricky.&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Transactions are thread-bound. Async methods run in a different thread and have a separate transactional context. Prefer events or queues for safe decoupling.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Final Thought
&lt;/h3&gt;

&lt;p&gt;Spring Boot gives you &lt;strong&gt;powerful tools&lt;/strong&gt;, but with great power comes great responsibility.&lt;br&gt;&lt;br&gt;
Transactions can protect your data, but only if you understand how they work — not just how they look.&lt;/p&gt;




&lt;p&gt;💬 &lt;strong&gt;I’d love to hear from you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What are the trickiest transactional issues you’ve faced?&lt;/li&gt;
&lt;li&gt;Do you have any favorite patterns for async + transactional operations?&lt;/li&gt;
&lt;li&gt;Any hidden pitfalls you’ve learned the hard way?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Share your experiences in the comments — let’s learn from each other.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>backend</category>
      <category>webdev</category>
      <category>java</category>
    </item>
    <item>
      <title>Inside the Backend: What Really Powers Modern Applications</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Thu, 30 Oct 2025 07:35:30 +0000</pubDate>
      <link>https://dev.to/gianfcop98/inside-the-backend-what-really-powers-modern-applications-4hhg</link>
      <guid>https://dev.to/gianfcop98/inside-the-backend-what-really-powers-modern-applications-4hhg</guid>
      <description>&lt;p&gt;&lt;em&gt;A Deep Dive into the Invisible Core of Modern Applications&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When most people think about an application, they picture what they can see and touch — a sleek mobile interface, a beautiful website, an interactive dashboard.&lt;br&gt;&lt;br&gt;
But under that surface lies an invisible engine that actually makes everything happen: &lt;strong&gt;the backend&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As a backend developer, I’ve often joked that our best work is the kind no one ever notices — until it stops working. The backend doesn’t seek attention, but it powers everything that matters: data persistence, logic, security, communication, and performance.&lt;/p&gt;

&lt;p&gt;This article isn’t about code snippets or frameworks (though we’ll mention a few). It’s about understanding &lt;em&gt;what the backend really does&lt;/em&gt;, why it’s essential, and how all its moving parts come together to make modern applications possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Role of the Backend
&lt;/h2&gt;

&lt;p&gt;If the frontend is the face of an application, the backend is the brain and nervous system.&lt;br&gt;&lt;br&gt;
It handles three key things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Data&lt;/strong&gt; — storing, retrieving, and transforming information.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Logic&lt;/strong&gt; — making decisions based on rules or workflows.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Communication&lt;/strong&gt; — connecting the application with other systems.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Whether you’re building a SaaS product, an e-commerce platform, or a mobile app backend, these three pillars remain the same. The backend is responsible for ensuring that data flows correctly and safely across all parts of the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  Core Responsibilities of a Backend
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Data Management
&lt;/h3&gt;

&lt;p&gt;Every backend starts with data.&lt;br&gt;&lt;br&gt;
It could be a PostgreSQL database, a MongoDB collection, or even a distributed data lake — but the principles are the same: &lt;strong&gt;store, retrieve, and manipulate data efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A well-designed backend abstracts away the raw SQL (or NoSQL queries) behind a clean data access layer. Tools like ORMs (e.g., SQLAlchemy, Prisma, or Hibernate) make this easier, but performance tuning and understanding how queries behave remain core skills for any backend developer.&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Input Validation
&lt;/h3&gt;

&lt;p&gt;Before anything hits the database, it needs to be trusted.&lt;br&gt;&lt;br&gt;
Input validation ensures that data sent from the frontend or other services is &lt;em&gt;complete, correct, and safe&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;For instance, you don’t want a user submitting &lt;code&gt;null&lt;/code&gt; as their password or an external API sending malformed JSON. Proper validation helps prevent bugs, data corruption, and security issues like SQL injection.&lt;/p&gt;

&lt;p&gt;I’ve learned that solid validation is one of those invisible things that users never notice — &lt;em&gt;until it’s missing&lt;/em&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Business Logic
&lt;/h3&gt;

&lt;p&gt;This is the heart of the application.&lt;br&gt;&lt;br&gt;
The &lt;strong&gt;business logic layer&lt;/strong&gt; (often called the service layer) is where the actual “rules of the game” live.&lt;/p&gt;

&lt;p&gt;Example:&lt;br&gt;&lt;br&gt;
If your app sells subscriptions, this layer decides whether a user can upgrade, when to charge them, and what happens when their payment fails.&lt;br&gt;&lt;br&gt;
It’s where domain knowledge meets code.&lt;/p&gt;

&lt;p&gt;A good backend keeps business logic isolated from both the database and the API routes. This makes the system easier to test, reason about, and evolve.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. External Services &amp;amp; Integrations
&lt;/h3&gt;

&lt;p&gt;Modern applications are rarely self-contained.&lt;br&gt;&lt;br&gt;
Your backend might need to communicate with payment gateways, email providers, analytics tools, or even AI models. This “machine-to-machine” communication often happens via HTTP calls, SDKs, or message queues.&lt;/p&gt;

&lt;p&gt;The key challenge here is &lt;strong&gt;resilience&lt;/strong&gt; — handling timeouts, retries, and rate limits gracefully.&lt;br&gt;&lt;br&gt;
When you’re working with multiple integrations, having good error handling and observability is essential to avoid cascading failures.&lt;/p&gt;




&lt;h3&gt;
  
  
  5. Data Exposure via REST APIs
&lt;/h3&gt;

&lt;p&gt;The backend exposes data to the outside world through &lt;strong&gt;APIs&lt;/strong&gt; — typically RESTful ones that speak JSON over HTTP.&lt;br&gt;&lt;br&gt;
APIs are the contract between your backend and the frontend (or other systems).&lt;/p&gt;

&lt;p&gt;Good APIs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Predictable&lt;/li&gt;
&lt;li&gt;  Well-documented&lt;/li&gt;
&lt;li&gt;  Versioned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Versioning (e.g., &lt;code&gt;/api/v1/users&lt;/code&gt;) prevents breaking existing clients when you update logic or data structures.&lt;br&gt;&lt;br&gt;
And tools like &lt;strong&gt;OpenAPI&lt;/strong&gt; or &lt;strong&gt;Swagger&lt;/strong&gt; make collaboration with frontend or mobile teams much smoother.&lt;/p&gt;




&lt;h3&gt;
  
  
  6. Authentication &amp;amp; Authorization
&lt;/h3&gt;

&lt;p&gt;Security is one of the most fundamental backend responsibilities.&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Authentication&lt;/strong&gt; confirms &lt;em&gt;who&lt;/em&gt; the user is, while &lt;strong&gt;authorization&lt;/strong&gt; determines &lt;em&gt;what&lt;/em&gt; they can access.&lt;/p&gt;

&lt;p&gt;There are many patterns here — session-based, JWT, OAuth2 — and the right one depends on your system’s architecture.&lt;br&gt;&lt;br&gt;
A secure backend never trusts input blindly and always validates tokens or credentials before giving access to data or functionality.&lt;/p&gt;




&lt;h3&gt;
  
  
  7. Error Handling, Logging &amp;amp; Configuration
&lt;/h3&gt;

&lt;p&gt;No backend is perfect. Errors happen — network issues, database timeouts, unexpected input. What matters is how you handle them.&lt;/p&gt;

&lt;p&gt;A robust backend:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Logs errors in a structured way (JSON logs are great for this).&lt;/li&gt;
&lt;li&gt;  Differentiates between client errors (4xx) and server errors (5xx).&lt;/li&gt;
&lt;li&gt;  Uses centralized logging tools like ELK, Loki, or CloudWatch.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configuration is another overlooked topic: using environment variables, secrets management, and per-environment settings prevents nasty surprises when deploying to production.&lt;/p&gt;




&lt;h3&gt;
  
  
  8. Caching &amp;amp; Performance Optimization
&lt;/h3&gt;

&lt;p&gt;Performance isn’t about writing faster code — it’s about making &lt;strong&gt;fewer expensive operations&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Caching is one of the most effective tools for this.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Caching user sessions or API responses in &lt;strong&gt;Redis&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Storing expensive query results&lt;/li&gt;
&lt;li&gt;  Using HTTP caching headers (ETag, Cache-Control)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tricky part is invalidation: making sure the cache stays fresh when data changes. But when done right, caching can drastically improve response times and scalability.&lt;/p&gt;




&lt;h3&gt;
  
  
  9. Asynchronous Tasks &amp;amp; Background Jobs
&lt;/h3&gt;

&lt;p&gt;Not everything needs to happen inside a request.&lt;br&gt;&lt;br&gt;
Sending emails, generating reports, or cleaning up old data are perfect examples of tasks that can be done &lt;strong&gt;asynchronously&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Tools like Celery (Python), Sidekiq (Ruby), or BullMQ (Node.js) let you process these tasks in the background, improving user experience and system performance.&lt;/p&gt;




&lt;h3&gt;
  
  
  10. Monitoring &amp;amp; Observability
&lt;/h3&gt;

&lt;p&gt;Once your backend is live, you need visibility into how it behaves.&lt;br&gt;&lt;br&gt;
Monitoring goes beyond “is it up?” — it’s about &lt;strong&gt;knowing when and why things go wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A well-instrumented backend includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Metrics (response times, error rates, queue lengths)&lt;/li&gt;
&lt;li&gt;  Tracing (e.g., OpenTelemetry, Jaeger)&lt;/li&gt;
&lt;li&gt;  Dashboards (Prometheus, Grafana, Datadog)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observability turns backend development from guesswork into engineering.&lt;/p&gt;




&lt;h2&gt;
  
  
  Popular Languages and Frameworks
&lt;/h2&gt;

&lt;p&gt;Backend development isn’t tied to a single language or stack — it’s an ecosystem of tools, each with its own philosophy and strengths.&lt;br&gt;&lt;br&gt;
Here are some of the most common choices you’ll encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Java / Kotlin&lt;/strong&gt; → &lt;strong&gt;Spring Boot&lt;/strong&gt;, &lt;strong&gt;Micronaut&lt;/strong&gt; (great for enterprise-scale systems)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Python&lt;/strong&gt; → &lt;strong&gt;Django&lt;/strong&gt;, &lt;strong&gt;Flask&lt;/strong&gt;, &lt;strong&gt;FastAPI&lt;/strong&gt; (very popular for APIs and data-heavy apps)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;JavaScript / TypeScript&lt;/strong&gt; → Node.js with frameworks like &lt;strong&gt;Express&lt;/strong&gt;, &lt;strong&gt;NestJS&lt;/strong&gt;, or &lt;strong&gt;Fastify&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;C# / .NET Core&lt;/strong&gt; → common in enterprise and cloud-native environments&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Go (Golang)&lt;/strong&gt; → favored for lightweight, high-performance microservices&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ruby&lt;/strong&gt; → &lt;strong&gt;Ruby on Rails&lt;/strong&gt;, still used for rapid web development&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rust&lt;/strong&gt; → gaining traction for highly performant, safe backends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In practice, the “best” choice depends on what you’re building, the team’s experience, and your scalability or performance needs.  &lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The backend is the hidden backbone of modern software.&lt;br&gt;&lt;br&gt;
It handles the invisible work — storing data, enforcing logic, integrating with other systems, and keeping everything secure and fast.&lt;/p&gt;

&lt;p&gt;Understanding its moving parts helps you design systems that are not only functional but also reliable and scalable.&lt;/p&gt;

&lt;p&gt;If you’re just starting your backend journey, focus on the fundamentals: &lt;strong&gt;data, APIs, security, and reliability&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
And remember — the best backend code isn’t the one that does the most, but the one that makes everything else run smoothly.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Thanks for reading! If you found this post useful, feel free to share your own backend “lessons learned” in the comments — I’d love to hear how you approach these challenges.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>backend</category>
      <category>webdev</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Spring Boot and Validation: A Complete Guide with @Valid and @Validated</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Mon, 27 Oct 2025 07:56:44 +0000</pubDate>
      <link>https://dev.to/gianfcop98/spring-boot-and-validation-a-complete-guide-with-valid-and-validated-471p</link>
      <guid>https://dev.to/gianfcop98/spring-boot-and-validation-a-complete-guide-with-valid-and-validated-471p</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Data validation is one of those topics that every backend developer &lt;em&gt;knows is important&lt;/em&gt;—but it often doesn’t get the attention it deserves. A solid validation strategy keeps your APIs clean, secure, and predictable.&lt;/p&gt;

&lt;p&gt;In this guide, we’ll explore how to use &lt;strong&gt;Spring Boot&lt;/strong&gt; validation effectively with &lt;code&gt;@Valid&lt;/code&gt; and &lt;code&gt;@Validated&lt;/code&gt;. You’ll learn how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Validate request DTOs cleanly&lt;/li&gt;
&lt;li&gt;  Handle nested objects and lists&lt;/li&gt;
&lt;li&gt;  Customize error messages&lt;/li&gt;
&lt;li&gt;  Manage validation groups for create/update operations&lt;/li&gt;
&lt;li&gt;  Build centralized exception handling&lt;/li&gt;
&lt;li&gt;  Implement custom validation annotations with &lt;code&gt;ConstraintValidator&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All examples are based on a simple product and category API built with &lt;strong&gt;Spring Boot 3&lt;/strong&gt;, &lt;strong&gt;Hibernate Validator&lt;/strong&gt;, and &lt;strong&gt;OpenAPI documentation&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Validation Matters
&lt;/h2&gt;

&lt;p&gt;Validation helps ensure &lt;strong&gt;data integrity&lt;/strong&gt;, &lt;strong&gt;security&lt;/strong&gt;, and &lt;strong&gt;user experience&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Prevents invalid or malicious data from reaching your business logic.&lt;/li&gt;
&lt;li&gt;  Improves API predictability by enforcing consistent input rules.&lt;/li&gt;
&lt;li&gt;  Provides clear feedback to clients via structured error responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Spring Boot natively supports validation via the &lt;strong&gt;Bean Validation API (JSR 380)&lt;/strong&gt;, implemented by &lt;strong&gt;Hibernate Validator&lt;/strong&gt; under the hood.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Enabling Validation
&lt;/h2&gt;

&lt;p&gt;Add the &lt;code&gt;spring-boot-starter-validation&lt;/code&gt; dependency to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;spring-boot-starter-validation&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once included, Spring automatically integrates it into your controller request mappings.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Basic Validation Annotations
&lt;/h2&gt;

&lt;p&gt;Spring Boot’s validation system supports all standard JSR-380 annotations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Annotation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@NotNull&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Field cannot be null&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@NotNull Double price&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@NotBlank&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;String cannot be null or empty (ignores spaces)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@NotBlank String name&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Size&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Limits size of strings, arrays, or lists&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Size(max = 5)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Email&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validates email format&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Email String contactEmail&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@Min&lt;/code&gt;, &lt;code&gt;@Max&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Numeric boundaries&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Min(1) @Max(10000)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@Positive&lt;/code&gt;, &lt;code&gt;@Negative&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Must be &amp;gt; 0 or &amp;lt; 0&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Positive Integer quantity&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@Past&lt;/code&gt;, &lt;code&gt;@Future&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;For dates&lt;/td&gt;
&lt;td&gt;&lt;code&gt;@Future LocalDate availabilityDate&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  4. Example: ProductRequest DTO
&lt;/h2&gt;

&lt;p&gt;Here’s a real-world DTO used in our API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@NotBlank&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product name cannot be blank"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nd"&gt;@Size&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;min&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Product name must be between 2 and 100 characters"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Price cannot be null"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Positive&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Price must be positive"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@NotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Category ID is required"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;categoryId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Valid&lt;/span&gt;
    &lt;span class="nd"&gt;@Size&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"You can add up to 5 tags"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nd"&gt;@NotBlank&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Tag cannot be blank"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Email&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Invalid email format for warranty"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;emailForWarranty&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Discount cannot be negative"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nd"&gt;@Max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Discount cannot exceed 80%"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;discountPercentage&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Future&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Availability date must be in the future"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;availabilityDate&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Sku&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SKU must be 8 uppercase letters or digits"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This DTO combines multiple annotation types and supports both &lt;strong&gt;create&lt;/strong&gt; and &lt;strong&gt;update&lt;/strong&gt; operations through validation groups.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Nested Object and List Validation
&lt;/h2&gt;

&lt;p&gt;To validate nested objects or lists, use &lt;code&gt;@Valid&lt;/code&gt; on the field that contains them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Valid&lt;/span&gt;
&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nd"&gt;@NotBlank&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Tag cannot be blank"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Spring will automatically cascade the validation down into each element of the list.&lt;/p&gt;

&lt;p&gt;For complex nested DTOs (e.g. &lt;code&gt;AddressRequest&lt;/code&gt; inside a &lt;code&gt;UserRequest&lt;/code&gt;), you’d also annotate the nested field with &lt;code&gt;@Valid&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. &lt;code&gt;@Valid&lt;/code&gt; vs &lt;code&gt;@Validated&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A common source of confusion!&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Annotation&lt;/th&gt;
&lt;th&gt;Used on&lt;/th&gt;
&lt;th&gt;Supports Groups&lt;/th&gt;
&lt;th&gt;Typical Use Case&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Valid&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Method parameters and fields&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Validate request bodies&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@Validated&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Class level or method level&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Used on method parameters, path variables, or when validation groups are needed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Example:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Validated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@PutMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nd"&gt;@Min&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Validated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;@Validated&lt;/code&gt; enables &lt;strong&gt;group-based validation&lt;/strong&gt;, enforcing different constraints on creation vs update.&lt;/p&gt;

&lt;p&gt;Meanwhile, in simpler cases (like categories), you can still use &lt;code&gt;@Valid&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CategoryResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createCategory&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;CategoryRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. Validation Groups Explained
&lt;/h2&gt;

&lt;p&gt;Validation groups let you define which constraints apply depending on context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OnCreate&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then assign them in your annotations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@NotNull&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Positive&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="nc"&gt;OnCreate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OnUpdate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When validating with &lt;code&gt;@Validated(OnCreate.class)&lt;/code&gt;, only constraints belonging to that group will be triggered.&lt;/p&gt;

&lt;p&gt;This approach is essential for APIs where fields are optional on update but mandatory on create.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Custom Error Messages
&lt;/h2&gt;

&lt;p&gt;You can define messages directly in annotations (as shown earlier), or externalize them in a &lt;code&gt;messages.properties&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;product.name.notblank&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Product name cannot be blank&lt;/span&gt;
&lt;span class="py"&gt;product.price.positive&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Price must be positive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reference them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@NotBlank&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"{product.name.notblank}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This improves maintainability and supports localization.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. Global Exception Handling for Validation
&lt;/h2&gt;

&lt;p&gt;When validation fails in Spring Boot, two main exceptions are typically thrown:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MethodArgumentNotValidException&lt;/code&gt;: triggered when a &lt;code&gt;@Valid&lt;/code&gt; or &lt;code&gt;@Validated&lt;/code&gt; annotated &lt;code&gt;@RequestBody&lt;/code&gt; fails validation.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ConstraintViolationException&lt;/code&gt;: triggered when validation fails on method parameters, such as &lt;code&gt;@PathVariable&lt;/code&gt;, &lt;code&gt;@RequestParam&lt;/code&gt;, or directly on method arguments annotated with &lt;code&gt;@Validated&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If not handled, these exceptions result in default error responses that are not user-friendly and may expose internal details.&lt;br&gt;&lt;br&gt;
To keep your API responses consistent and developer-friendly, you should implement centralized exception handling using &lt;code&gt;@RestControllerAdvice&lt;/code&gt; (or &lt;code&gt;@ControllerAdvice&lt;/code&gt;, depending on your use case).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@RestControllerAdvic&lt;/code&gt;e is the preferred choice for REST APIs, as it automatically adds @ResponseBody to all methods, returning structured JSON error responses.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@ControllerAdvice&lt;/code&gt;, on the other hand, is more general-purpose and suitable if your application also serves web pages or uses view templates, since it doesn’t assume a JSON response by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both annotations act as global interceptors for exceptions thrown from controllers, allowing you to define a single, centralized place to handle and format error responses.&lt;/p&gt;

&lt;p&gt;A typical example looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@RestControllerAdvice&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalExceptionHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MethodArgumentNotValidException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleValidationError&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MethodArgumentNotValidException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errors"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getBindingResult&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getFieldErrors&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getField&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDefaultMessage&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;badRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConstraintViolationException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleConstraintViolation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ConstraintViolationException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;errors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;LinkedHashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"timestamp"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;LocalDateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"status"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BAD_REQUEST&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"errors"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getConstraintViolations&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;stream&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"field"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPropertyPath&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;()))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;badRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;errors&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why You Should Use @ControllerAdvice
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Centralization:&lt;/strong&gt; all validation errors are managed in one place, improving maintainability.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Consistency:&lt;/strong&gt; every endpoint returns validation errors with the same JSON structure.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Security:&lt;/strong&gt; prevents exposing internal exception details to the client.&lt;/li&gt;
&lt;li&gt;  ✅ &lt;strong&gt;Extensibility:&lt;/strong&gt; you can easily extend the same class to handle business or authentication errors in the future.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach not only improves developer experience but also helps frontend teams rely on predictable, machine-readable error formats.&lt;/p&gt;




&lt;h2&gt;
  
  
  10. Custom Validators with ConstraintValidator
&lt;/h2&gt;

&lt;p&gt;Sometimes standard annotations aren’t enough. You can create your own validator easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: SKU Validator
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Target&lt;/span&gt;&lt;span class="o"&gt;({&lt;/span&gt;&lt;span class="nc"&gt;ElementType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@Retention&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;RetentionPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;RUNTIME&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Constraint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;validatedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SkuValidator&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nd"&gt;@interface&lt;/span&gt; &lt;span class="nc"&gt;Sku&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;message&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="s"&gt;"Invalid SKU format"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&amp;gt;[]&lt;/span&gt; &lt;span class="n"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;{};&lt;/span&gt;
    &lt;span class="nc"&gt;Class&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;?&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Payload&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;[]&lt;/span&gt; &lt;span class="nf"&gt;payload&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;{};&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Validator implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SkuValidator&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ConstraintValidator&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Sku&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;SKU_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"^[A-Z0-9]{8}$"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Override&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;isValid&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConstraintValidatorContext&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;SKU_PATTERN&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then use it in your DTO:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Sku&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SKU must be 8 uppercase letters or digits"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  11. Best Practices
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Validate at the edge&lt;/strong&gt; — always in controllers, before reaching the service layer.
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Group constraints&lt;/strong&gt; logically (create/update).
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Use &lt;code&gt;@Valid&lt;/code&gt; for simple cases&lt;/strong&gt;, &lt;code&gt;@Validated&lt;/code&gt; for grouped or parameter-level validation.
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Keep error messages clean&lt;/strong&gt; and user-friendly.
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Centralize exception handling&lt;/strong&gt; with &lt;code&gt;@ControllerAdvice&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Externalize messages&lt;/strong&gt; for easier maintenance.
&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Prefer DTO validation over entity validation&lt;/strong&gt; to maintain separation of concerns.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  12. Conclusion
&lt;/h2&gt;

&lt;p&gt;Validation is more than just checking inputs—it’s part of your API design.&lt;br&gt;&lt;br&gt;
Spring Boot, with Bean Validation, makes it easy to enforce correctness, improve security, and offer better UX.&lt;/p&gt;

&lt;p&gt;We’ve covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Basic and advanced annotations&lt;/li&gt;
&lt;li&gt;  Nested and list validation&lt;/li&gt;
&lt;li&gt;  Custom constraints&lt;/li&gt;
&lt;li&gt;  Validation groups&lt;/li&gt;
&lt;li&gt;  Global error handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🧩 &lt;strong&gt;Want more?&lt;/strong&gt; Check out the &lt;a href="https://github.com/gianfcop/springboot-api-validation-demo.git" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for more examples and full implementations and my &lt;a href="https://gianfcop.gumroad.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pro Starter Kit on Gumroad&lt;/strong&gt;&lt;/a&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 &lt;strong&gt;Join the Discussion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;What’s your approach to validation and error handling in Spring Boot? Have you tried alternative strategies like custom &lt;code&gt;ErrorResponse&lt;/code&gt; wrappers or problem-spring-web? Let’s discuss best practices and experiences from real-world projects.&lt;/p&gt;

&lt;p&gt;Share your thoughts in the comments! 👇&lt;br&gt;&lt;br&gt;
Your feedback will help other developers (and me!) improve future articles.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>backend</category>
      <category>tutorial</category>
      <category>java</category>
    </item>
    <item>
      <title>10 Lombok Annotations Every Java Developer Should Know</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Sun, 26 Oct 2025 19:07:48 +0000</pubDate>
      <link>https://dev.to/gianfcop98/10-lombok-annotations-every-java-developer-should-know-pcd</link>
      <guid>https://dev.to/gianfcop98/10-lombok-annotations-every-java-developer-should-know-pcd</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Java is a powerful and mature language, but it comes with a cost: &lt;strong&gt;a lot of boilerplate code&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Think about the endless getters, setters, constructors, and &lt;code&gt;toString()&lt;/code&gt; methods you’ve written over the years. They don’t add business value — they just make your code longer and harder to read.&lt;/p&gt;

&lt;p&gt;That’s where &lt;a href="https://projectlombok.org/" rel="noopener noreferrer"&gt;&lt;strong&gt;Project Lombok&lt;/strong&gt;&lt;/a&gt; comes to the rescue. Lombok uses annotations to &lt;strong&gt;generate boilerplate code at compile time&lt;/strong&gt;, letting you focus on actual logic instead of repetitive syntax.&lt;/p&gt;

&lt;p&gt;It integrates perfectly with &lt;strong&gt;Spring Boot&lt;/strong&gt;, which makes it one of the most popular tools in modern Java development.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore &lt;strong&gt;10 Lombok annotations every Java developer should know&lt;/strong&gt; and discuss &lt;strong&gt;best practices and common pitfalls&lt;/strong&gt; so you can use Lombok effectively and safely.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;How to Add Lombok to Your Project&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before you start using Lombok, make sure your project and IDE are properly configured.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;strong&gt;Maven&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.projectlombok&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;lombok&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;1.18.32&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;scope&amp;gt;&lt;/span&gt;provided&lt;span class="nt"&gt;&amp;lt;/scope&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Gradle&lt;/strong&gt;
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;compileOnly&lt;/span&gt; &lt;span class="s1"&gt;'org.projectlombok:lombok:1.18.32'&lt;/span&gt;
&lt;span class="n"&gt;annotationProcessor&lt;/span&gt; &lt;span class="s1"&gt;'org.projectlombok:lombok:1.18.32'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;IDE Configuration&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;IntelliJ IDEA&lt;/strong&gt; →&lt;br&gt;&lt;br&gt;
&lt;code&gt;Settings &amp;gt; Build, Execution, Deployment &amp;gt; Compiler &amp;gt; Annotation Processors &amp;gt; Enable annotation processing&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Eclipse&lt;/strong&gt; →&lt;br&gt;&lt;br&gt;
&lt;code&gt;Preferences &amp;gt; Java Compiler &amp;gt; Annotation Processing &amp;gt; Enable&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;VS Code&lt;/strong&gt; →  &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Make sure you have the &lt;strong&gt;Extension Pack for Java&lt;/strong&gt; installed (includes the Language Support for Java by Red Hat).&lt;/li&gt;
&lt;li&gt; Open your workspace settings and add:
&lt;code&gt;"java.configuration.annotationProcessing.enabled":  true&lt;/code&gt; &lt;/li&gt;
&lt;li&gt; Restart VS Code and recompile the project.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once configured, Lombok’s annotations will work seamlessly in your IDE, and you’re ready to say goodbye to boilerplate.&lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;The 10 Most Useful Lombok Annotations&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here are the top annotations that will make your Java code cleaner, shorter, and more readable.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;1. &lt;code&gt;@Getter&lt;/code&gt; / &lt;code&gt;@Setter&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;These annotations generate standard getter and setter methods automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Getter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Setter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Getter&lt;/span&gt; &lt;span class="nd"&gt;@Setter&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Getter&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At class level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Getter&lt;/span&gt;
&lt;span class="nd"&gt;@Setter&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Removes repetitive method definitions.&lt;/li&gt;
&lt;li&gt;  Keeps your classes focused on business logic.&lt;/li&gt;
&lt;li&gt;  You can control visibility: &lt;code&gt;@Getter(AccessLevel.PROTECTED)&lt;/code&gt; for example.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;2. &lt;code&gt;@Data&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Combines several Lombok annotations into one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;@Getter&lt;/code&gt; / &lt;code&gt;@Setter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@ToString&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@EqualsAndHashCode&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Data&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Great for simple DTOs and data containers.&lt;/li&gt;
&lt;li&gt;  Saves multiple lines of boilerplate.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;⚠ &lt;strong&gt;When to avoid it:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Not ideal for &lt;strong&gt;JPA entities&lt;/strong&gt; or &lt;strong&gt;domain models&lt;/strong&gt; where you need more control over equality and mutability.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;3. &lt;code&gt;@Builder&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Implements the &lt;strong&gt;Builder pattern&lt;/strong&gt;, letting you create objects in a more expressive and flexible way.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Builder&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Usage&lt;/span&gt;
&lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;age&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Eliminates complex constructors with many parameters.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Improves code readability.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Makes object creation safe and expressive (especially in tests).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;4. &lt;code&gt;@AllArgsConstructor&lt;/code&gt; / &lt;code&gt;@NoArgsConstructor&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Generates constructors with &lt;strong&gt;all fields&lt;/strong&gt; or &lt;strong&gt;no fields&lt;/strong&gt; respectively.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.AllArgsConstructor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.NoArgsConstructor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Perfect for frameworks like &lt;strong&gt;JPA&lt;/strong&gt; or &lt;strong&gt;Jackson&lt;/strong&gt;, which require a no-args constructor.&lt;/li&gt;
&lt;li&gt;  Speeds up development when dealing with DTOs or entity creation.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;5. &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Generates a constructor for all &lt;strong&gt;final&lt;/strong&gt; fields (and those marked with &lt;code&gt;@NonNull&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.RequiredArgsConstructor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Service&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Repository&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;strong&gt;Spring Boot&lt;/strong&gt;, this shines with constructor injection:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Encourages &lt;strong&gt;dependency injection via constructors&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  No need for &lt;code&gt;@Autowired&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  Promotes &lt;strong&gt;immutability&lt;/strong&gt; and cleaner design.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;6. &lt;code&gt;@FieldDefaults&lt;/code&gt; (and Advanced Combo)&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@FieldDefaults&lt;/code&gt; lets you define &lt;strong&gt;default modifiers&lt;/strong&gt; for fields across a class, reducing repetitive access modifiers.&lt;/p&gt;

&lt;p&gt;It has two main properties:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;level&lt;/code&gt;&lt;/strong&gt; → sets default access level (&lt;code&gt;PRIVATE&lt;/code&gt;, &lt;code&gt;PROTECTED&lt;/code&gt;, &lt;code&gt;PUBLIC&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;makeFinal&lt;/code&gt;&lt;/strong&gt; → makes all fields &lt;code&gt;final&lt;/code&gt; if &lt;code&gt;true&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt; Set default access level&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.experimental.FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;lombok&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt; Combine with &lt;code&gt;makeFinal&lt;/code&gt; for immutability&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.experimental.FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.RequiredArgsConstructor&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;lombok&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;makeFinal&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@RequiredArgsConstructor&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="n"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;NotificationService&lt;/span&gt; &lt;span class="n"&gt;notificationService&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Keeps field definitions consistent and clean.&lt;/li&gt;
&lt;li&gt;  Reduces risk of accidentally exposing internal state.&lt;/li&gt;
&lt;li&gt;  Perfect for &lt;strong&gt;constructor injection&lt;/strong&gt; when combined with &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💡 &lt;strong&gt;Tip:&lt;/strong&gt; You don’t always need both properties.&lt;br&gt;&lt;br&gt;
Use only &lt;code&gt;level&lt;/code&gt; for access control, or &lt;code&gt;makeFinal&lt;/code&gt; when enforcing immutability.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;7. &lt;code&gt;@Value&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@Value&lt;/code&gt; is like &lt;code&gt;@Data&lt;/code&gt;, but it creates &lt;strong&gt;immutable classes&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  All fields are &lt;code&gt;private final&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  No setters are generated.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.Value&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Value&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;zip&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Promotes immutability and thread-safety.&lt;/li&gt;
&lt;li&gt;  Excellent for DTOs and value objects.&lt;/li&gt;
&lt;li&gt;  Works perfectly with the Builder pattern.&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;8. &lt;code&gt;@Slf4j&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Generates an &lt;strong&gt;SLF4J logger&lt;/strong&gt; automatically.&lt;/p&gt;

&lt;p&gt;Without Lombok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Logger&lt;/span&gt; &lt;span class="n"&gt;log&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LoggerFactory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;MyService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Lombok:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.extern.slf4j.Slf4j&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Slf4j&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;processOrder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing order: {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// some business logic&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error processing order {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orderId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  No more manual logger setup.&lt;/li&gt;
&lt;li&gt;  Fully compatible with &lt;strong&gt;Spring Boot’s default logging (SLF4J + Logback)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;  Supports structured logging and all log levels (&lt;code&gt;info&lt;/code&gt;, &lt;code&gt;warn&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;debug&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;  Keeps code cleaner and easier to maintain.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Other variants available:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;code&gt;@Log&lt;/code&gt;, &lt;code&gt;@Log4j&lt;/code&gt;, &lt;code&gt;@Log4j2&lt;/code&gt;, &lt;code&gt;@CommonsLog&lt;/code&gt; — but &lt;code&gt;@Slf4j&lt;/code&gt; is the most common and recommended.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;9. &lt;code&gt;@UtilityClass&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@UtilityClass&lt;/code&gt; is a hidden gem in Lombok’s toolkit.&lt;br&gt;&lt;br&gt;
It’s used to create &lt;strong&gt;utility classes&lt;/strong&gt; — classes that only contain static methods and constants.&lt;/p&gt;

&lt;p&gt;Lombok will automatically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Make the class &lt;code&gt;final&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  Add a &lt;strong&gt;private constructor&lt;/strong&gt; (preventing instantiation).&lt;/li&gt;
&lt;li&gt;  Convert all fields and methods to &lt;strong&gt;static&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.experimental.UtilityClass&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@UtilityClass&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="no"&gt;PI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;3.14159&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Usage:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;int  sum  = MathUtils.add(5, 10);&lt;/code&gt; &lt;/p&gt;

&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Eliminates boilerplate for utility classes.&lt;/li&gt;
&lt;li&gt;  Enforces correct usage (non-instantiable, static members).&lt;/li&gt;
&lt;li&gt;  Perfect for helpers, validators, or constants.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without Lombok, you’d need to write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MathUtils&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;MathUtils&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Lombok — one line. Clean and elegant.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;10. &lt;code&gt;@ToString&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Generates a &lt;code&gt;toString()&lt;/code&gt; method automatically.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;lombok.ToString&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@ToString&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;age&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exclude fields when needed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ToString&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exclude&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ &lt;strong&gt;Why is it useful?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Great for debugging and logging.&lt;/li&gt;
&lt;li&gt;  Avoids maintenance issues when adding or renaming fields.&lt;/li&gt;
&lt;li&gt;  Works perfectly with &lt;code&gt;@Data&lt;/code&gt; or on its own for better control.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices &amp;amp; When to Avoid Lombok&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While Lombok can make your life easier, it’s not always the right tool for every situation.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Avoid overusing &lt;code&gt;@Data&lt;/code&gt;&lt;/strong&gt;
It may generate methods you don’t need and cause issues in entities or complex models.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Prefer immutability&lt;/strong&gt;
Use &lt;code&gt;@Value&lt;/code&gt; or &lt;code&gt;makeFinal = true&lt;/code&gt; in &lt;code&gt;@FieldDefaults&lt;/code&gt; when possible.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mind your team’s tooling&lt;/strong&gt;
Ensure everyone’s IDE and CI/CD pipelines support annotation processing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Be explicit when needed&lt;/strong&gt;
Don’t hide complexity behind annotations if it reduces clarity for new contributors.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Lombok is a &lt;strong&gt;must-have library&lt;/strong&gt; for Java developers who value clean, maintainable, and expressive code.&lt;br&gt;&lt;br&gt;
It removes the friction of boilerplate, integrates seamlessly with Spring Boot, and helps you focus on what truly matters: &lt;strong&gt;business logic&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key takeaways:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use Lombok for faster development and cleaner classes.&lt;/li&gt;
&lt;li&gt;  Combine &lt;code&gt;@FieldDefaults&lt;/code&gt; with &lt;code&gt;@RequiredArgsConstructor&lt;/code&gt; for elegant constructor injection.&lt;/li&gt;
&lt;li&gt;  Use &lt;code&gt;@Value&lt;/code&gt; for immutability and &lt;code&gt;@Builder&lt;/code&gt; for readability.&lt;/li&gt;
&lt;li&gt;  Don’t overlook gems like &lt;code&gt;@Slf4j&lt;/code&gt; and &lt;code&gt;@UtilityClass&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ &lt;strong&gt;Want more?&lt;/strong&gt; Check out the &lt;a href="https://github.com/gianfcop" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for more examples and full implementations and my &lt;a href="https://gianfcop.gumroad.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;Pro Starter Kit on Gumroad&lt;/strong&gt;&lt;/a&gt;!&lt;/p&gt;




&lt;h2&gt;
  
  
  💬 &lt;strong&gt;Join the Discussion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;What do you think about Lombok?  Do you love it, or do you find it hides too much magic?&lt;/p&gt;

&lt;p&gt;Share your thoughts in the comments!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;👉 How has Lombok helped (or hurt) your Java projects?
&lt;/li&gt;
&lt;li&gt;👉 Which annotations do you use the most?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your feedback will help other developers (and me!) improve future articles.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>java</category>
      <category>lombok</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>How to Write Clean DTO &amp; Entity Mappers in Java (with Spring Boot)</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Sun, 26 Oct 2025 18:08:17 +0000</pubDate>
      <link>https://dev.to/gianfcop98/how-to-write-clean-dto-entity-mappers-in-java-with-spring-boot-5ac6</link>
      <guid>https://dev.to/gianfcop98/how-to-write-clean-dto-entity-mappers-in-java-with-spring-boot-5ac6</guid>
      <description>&lt;h1&gt;
  
  
  &lt;strong&gt;How to Write Clean DTO &amp;amp; Entity Mappers in Java (with Spring Boot)&lt;/strong&gt;
&lt;/h1&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Introduction&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When building a Spring Boot application, one of the most common patterns you’ll encounter is &lt;strong&gt;mapping between Entities and DTOs&lt;/strong&gt; (Data Transfer Objects).&lt;/p&gt;

&lt;p&gt;But why should you separate them? Why not just expose your JPA entities directly through your API?&lt;/p&gt;

&lt;p&gt;The short answer: &lt;strong&gt;maintainability, security, and performance&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability:&lt;/strong&gt; DTOs decouple your API layer from the database schema, giving you flexibility to change one without breaking the other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Security:&lt;/strong&gt; You can control exactly what data is exposed to the client, avoiding accidental leaks of sensitive fields.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Performance:&lt;/strong&gt; DTOs can be optimized to include only the required fields, reducing payload size and unnecessary joins.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this article, we’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What DTOs are and why you need them&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;How to map Entities to DTOs (and vice versa) cleanly&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Different approaches: manual mapping, &lt;strong&gt;MapStruct&lt;/strong&gt;, and &lt;strong&gt;ModelMapper&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Best practices for keeping your code clean and maintainable&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What is a DTO and Why Use It?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;DTO (Data Transfer Object)&lt;/strong&gt; is a plain object used to transfer data between processes, layers, or services—typically between your API and the client.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Entity vs DTO&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Entity:&lt;/strong&gt; Represents a database table. Usually annotated with &lt;code&gt;@Entity&lt;/code&gt; and tied to JPA/Hibernate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;DTO:&lt;/strong&gt; Represents the data structure you expose in your API response or accept in requests. It is &lt;strong&gt;not&lt;/strong&gt; managed by JPA.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Common Problems Without DTO&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you expose your Entity directly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tight coupling:&lt;/strong&gt; Any change in your database model breaks your API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Sensitive data exposure:&lt;/strong&gt; Internal fields like IDs or relationships may leak.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lazy loading issues:&lt;/strong&gt; Serializing JPA entities directly can trigger unwanted queries or &lt;code&gt;LazyInitializationException&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;DTOs solve these problems by acting as a safe and controlled data layer.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Our Example Domain: Category &amp;amp; Product&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We’ll use the following Entities:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"categories"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;unique&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToMany&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mappedBy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"category"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cascade&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CascadeType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;orphanRemoval&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@Table&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="nd"&gt;@GeneratedValue&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strategy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GenerationType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;IDENTITY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Column&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ManyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fetch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FetchType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;LAZY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinColumn&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"category_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the following DTOs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;CategoryRequest&lt;/strong&gt; (for creating categories)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ProductRequest&lt;/strong&gt; (for creating products)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ProductResponse&lt;/strong&gt; (for returning product data)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Data&lt;/span&gt;
&lt;span class="nd"&gt;@Builder&lt;/span&gt;
&lt;span class="nd"&gt;@NoArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@AllArgsConstructor&lt;/span&gt;
&lt;span class="nd"&gt;@FieldDefaults&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PRIVATE&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Approaches to Mapping Entities and DTOs&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Manual Mapping&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The most basic approach: write the conversion logic by hand.&lt;/p&gt;

&lt;p&gt;Example for &lt;code&gt;Product&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductMapper&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt; &lt;span class="nf"&gt;toResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategory&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="c1"&gt;// Extract category name&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;toEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt; &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt; Full control, no dependencies&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Cons:&lt;/strong&gt; Boilerplate, hard to maintain at scale&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;2. Automatic Mapping with Libraries&lt;/strong&gt;
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;MapStruct&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;MapStruct&lt;/strong&gt; is a &lt;strong&gt;compile-time code generator&lt;/strong&gt; for Java bean mappings. It uses &lt;strong&gt;annotations&lt;/strong&gt; to define the mapping between source and target objects, and then generates the implementation at build time.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;No reflection at runtime&lt;/strong&gt; → faster than libraries like ModelMapper&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Compile-time type checking&lt;/strong&gt; → if a field mapping is missing or incorrect, you get a compiler error&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;No runtime surprises&lt;/strong&gt; → everything is generated as plain Java code&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;How does MapStruct work?&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;When you define an interface and annotate it with &lt;code&gt;@Mapper&lt;/code&gt;, MapStruct creates a class that implements it during compilation.&lt;br&gt;&lt;br&gt;
Example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Mapper&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;componentModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"spring"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;ProductMapper&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Mapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"category.name"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"category"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;ProductResponse&lt;/span&gt; &lt;span class="nf"&gt;toResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

    &lt;span class="nd"&gt;@Mapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"categoryId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"category.id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;toEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  &lt;strong&gt;Breaking Down the Mapping&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Product → ProductResponse&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;id&lt;/code&gt; → &lt;code&gt;id&lt;/code&gt; (same name → automatically mapped)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;name&lt;/code&gt; → &lt;code&gt;name&lt;/code&gt; (same name → automatically mapped)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;price&lt;/code&gt; → &lt;code&gt;price&lt;/code&gt; (same name → automatically mapped)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;product.category.name&lt;/code&gt; → &lt;code&gt;category&lt;/code&gt;
(custom mapping because the field names differ)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;ProductRequest → Product&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;name&lt;/code&gt; → &lt;code&gt;name&lt;/code&gt; (automatic)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;price&lt;/code&gt; → &lt;code&gt;price&lt;/code&gt; (automatic)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;categoryId&lt;/code&gt; → &lt;code&gt;category.id&lt;/code&gt;
(nested property mapping)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Generated Code Example (by MapStruct)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;After compilation, MapStruct generates an implementation like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="nd"&gt;@Component&lt;/span&gt;  &lt;span class="kd"&gt;public&lt;/span&gt;  &lt;span class="kd"&gt;class&lt;/span&gt;  &lt;span class="nc"&gt;ProductMapperImpl&lt;/span&gt;  &lt;span class="kd"&gt;implements&lt;/span&gt;  &lt;span class="nc"&gt;ProductMapper&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nd"&gt;@Override&lt;/span&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt; &lt;span class="nf"&gt;toResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ProductResponseBuilder&lt;/span&gt;  &lt;span class="n"&gt;productResponse&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;productResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;productResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;productResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategory&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;productResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategory&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;productResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="nd"&gt;@Override&lt;/span&gt;  &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;toEntity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProductRequest&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;  &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ProductBuilder&lt;/span&gt;  &lt;span class="n"&gt;product&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;builder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPrice&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategoryId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Category&lt;/span&gt;  &lt;span class="n"&gt;category&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="k"&gt;new&lt;/span&gt;  &lt;span class="nc"&gt;Category&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCategoryId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✔ Notice how &lt;strong&gt;MapStruct takes care of null checks and builder patterns automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pros and cons of MapStruct
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pros:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Compile-time safety&lt;/li&gt;
&lt;li&gt;  High performance&lt;/li&gt;
&lt;li&gt;  Clear, explicit mappings&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Cons:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  Requires additional annotation processing setup&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;ModelMapper&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Uses reflection at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Configuration&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MapperConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Bean&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="nf"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;ModelMapper&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Service&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ProductService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;ProductService&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ModelMapper&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;modelMapper&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt; &lt;span class="nf"&gt;toResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;modelMapper&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductResponse&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;strong&gt;nested fields&lt;/strong&gt; (like &lt;code&gt;category.name&lt;/code&gt;), you need extra configuration for ModelMapper.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Best Practices for Clean Mapping&lt;/strong&gt;
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Keep mappers simple:&lt;/strong&gt; No business logic inside.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Place mappers logically:&lt;/strong&gt; Usually in &lt;code&gt;mapper&lt;/code&gt; or inside the related module/package.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Choose the right approach:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Manual mapping:&lt;/strong&gt; Small projects, simple models.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;MapStruct:&lt;/strong&gt; Most production apps (fast, safe).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ModelMapper:&lt;/strong&gt; Quick prototypes or POCs.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Avoid over-fetching:&lt;/strong&gt; Map only the fields required by your API.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Benchmark performance:&lt;/strong&gt; For large-scale apps, MapStruct &amp;gt; Manual &amp;gt; ModelMapper in most cases.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Separating Entities and DTOs is a fundamental best practice in modern Spring Boot applications. It makes your code cleaner, more secure, and easier to maintain.&lt;br&gt;
When it comes to mapping:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Manual mapping&lt;/strong&gt; for small apps&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;MapStruct&lt;/strong&gt; for most production scenarios&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;ModelMapper&lt;/strong&gt; for quick prototypes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Want more?&lt;/strong&gt; Check out the GitHub repository for more examples and full implementations and my &lt;strong&gt;Pro Starter Kit on Gumroad&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gianfcop/springboot-dto-mapping-demo.git" rel="noopener noreferrer"&gt;Spring Boot DTO Mapping (Lite) on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gianfcop.gumroad.com/" rel="noopener noreferrer"&gt;Pro Starter Kit on Gumroad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;✅ &lt;strong&gt;What’s your favorite mapping strategy? Comment below and share your experience!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>restapi</category>
      <category>springboot</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Ultimate Guide to HTTP Status Codes in REST APIs</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Tue, 02 Sep 2025 06:49:16 +0000</pubDate>
      <link>https://dev.to/gianfcop98/the-ultimate-guide-to-http-status-codes-in-rest-apis-40cp</link>
      <guid>https://dev.to/gianfcop98/the-ultimate-guide-to-http-status-codes-in-rest-apis-40cp</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;The Ultimate Guide to HTTP Status Codes in REST APIs&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;When building REST APIs, &lt;strong&gt;HTTP status codes&lt;/strong&gt; play a crucial role in communication between the client and the server. They aren’t just numbers; they provide valuable context about what happened with a request—whether it succeeded, failed, or needs further action.&lt;/p&gt;

&lt;p&gt;In this guide, you’ll learn:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Why status codes matter for REST APIs.&lt;/li&gt;
&lt;li&gt;  The main HTTP status code categories.&lt;/li&gt;
&lt;li&gt;  Which codes to use and when.&lt;/li&gt;
&lt;li&gt;  Best practices for consistent responses.&lt;/li&gt;
&lt;li&gt;  Practical Spring Boot examples to implement them.&lt;/li&gt;
&lt;li&gt;  How to handle errors properly with custom responses.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Why HTTP Status Codes Matter&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Status codes are more than formalities—they enhance &lt;strong&gt;client-server communication&lt;/strong&gt; and &lt;strong&gt;developer experience&lt;/strong&gt;. When used correctly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Clients know how to respond&lt;/strong&gt; (e.g., retry, show an error message).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;APIs are self-descriptive&lt;/strong&gt;, reducing the need for extra documentation.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Debugging becomes easier&lt;/strong&gt;, as error codes indicate the exact problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Incorrect usage can confuse API consumers and lead to bad UX. For example, returning &lt;code&gt;200 OK&lt;/code&gt; for an invalid request hides the real issue.&lt;/p&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Categories of HTTP Status Codes&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;HTTP status codes are grouped into five categories:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;1xx – Informational&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Indicates the request was received and is being processed.&lt;/li&gt;
&lt;li&gt;  Rarely used in REST APIs.&lt;/li&gt;
&lt;li&gt;  Example: &lt;code&gt;100 Continue&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;2xx – Success&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Indicates the request was successfully processed.&lt;/li&gt;
&lt;li&gt;  Common codes:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;200 OK&lt;/strong&gt; – Generic success response.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;201 Created&lt;/strong&gt; – Resource successfully created.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;202 Accepted&lt;/strong&gt; – Request accepted but processing is asynchronous.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;204 No Content&lt;/strong&gt; – Action successful, no body returned.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;3xx – Redirection&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Indicates further action is needed (usually another URL).&lt;/li&gt;
&lt;li&gt;  Common in browsers, rarely in APIs.&lt;/li&gt;
&lt;li&gt;  Example: &lt;code&gt;301 Moved Permanently&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;4xx – Client Errors&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  The client sent an invalid request.&lt;/li&gt;
&lt;li&gt;  Common codes:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;400 Bad Request&lt;/strong&gt; – Invalid input or missing parameters.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;401 Unauthorized&lt;/strong&gt; – Authentication required or failed.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;403 Forbidden&lt;/strong&gt; – Authenticated, but no permission.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;404 Not Found&lt;/strong&gt; – Resource doesn’t exist.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;409 Conflict&lt;/strong&gt; – Request conflicts with the current state.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt; – Validation error.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h4&gt;
  
  
  &lt;strong&gt;5xx – Server Errors&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;  Something went wrong on the server side.&lt;/li&gt;
&lt;li&gt;  Common codes:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;500 Internal Server Error&lt;/strong&gt; – Generic server error.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;503 Service Unavailable&lt;/strong&gt; – Server temporarily down.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Most Common HTTP Status Codes and When to Use Them&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Here’s a quick guide to avoid confusion:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;200 OK&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use when the request succeeds and returns data.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John Doe"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;201 Created&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use after successfully creating a resource.&lt;/li&gt;
&lt;li&gt;  Example: POST request creating a user.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;202 Accepted&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use when the request has been accepted for processing but the operation is asynchronous.&lt;/li&gt;
&lt;li&gt;Example: triggering a background job, processing a file upload, sending an email.
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;202&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Processing started"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You will receive a callback when the job completes"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;204 No Content&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use for successful operations without a response body (e.g., DELETE).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use when the client sends invalid input.&lt;/li&gt;
&lt;li&gt;  Example: Missing required fields.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;401 Unauthorized vs 403 Forbidden&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;401&lt;/strong&gt; – The client must authenticate.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;403&lt;/strong&gt; – The client is authenticated but not allowed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Resource not found (e.g., wrong ID).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;409 Conflict&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Use when there’s a resource state conflict (e.g., duplicate username).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Validation passed structurally but semantically invalid (e.g., age &amp;lt; 0).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;500 Internal Server Error&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Unexpected server failure—should be avoided as much as possible.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;Best Practices for Choosing the Right Status Code&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;✅ &lt;strong&gt;Be consistent&lt;/strong&gt;: If you use &lt;code&gt;201&lt;/code&gt; for resource creation in one endpoint, don’t switch to &lt;code&gt;200&lt;/code&gt; in another.&lt;br&gt;
✅ &lt;strong&gt;Don’t overuse 200&lt;/strong&gt;: Returning &lt;code&gt;200 OK&lt;/code&gt; for every response defeats the purpose of HTTP status codes.&lt;br&gt;
✅ &lt;strong&gt;Provide meaningful error responses&lt;/strong&gt;: Include a JSON body with &lt;code&gt;code&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, and optionally &lt;code&gt;details&lt;/code&gt;.&lt;br&gt;
✅ &lt;strong&gt;Follow REST conventions&lt;/strong&gt;: Avoid creating custom semantics when a standard code exists.&lt;/p&gt;


&lt;h3&gt;
  
  
  &lt;strong&gt;Practical Examples with Spring Boot&lt;/strong&gt;
&lt;/h3&gt;
&lt;h4&gt;
  
  
  &lt;strong&gt;Using &lt;code&gt;ResponseEntity&lt;/code&gt; to Set Status Codes&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Spring Boot makes it easy to control status codes via &lt;code&gt;ResponseEntity&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// GET&lt;/span&gt;
&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findById&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;ResponseEntity:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
               &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseGet&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// POST&lt;/span&gt;
&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;createdUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;createdUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;created&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;createdUser&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// PUT&lt;/span&gt;
&lt;span class="nd"&gt;@PutMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Optional&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updatedUser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;update&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;updatedUser&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;ResponseEntity:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;orElseGet&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;notFound&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// DELETE&lt;/span&gt;
&lt;span class="nd"&gt;@DeleteMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;deleteUser&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;userService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;delete&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;noContent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ASYNC example with 202&lt;/span&gt;
&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/process"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;startProcess&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;processService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startBackgroundJob&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="no"&gt;URI&lt;/span&gt; &lt;span class="n"&gt;statusUri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;URI&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;create&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/process/status/123"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accepted&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;statusUri&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                         &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Processing started"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Error Handling and Custom Responses&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of returning raw error messages, structure your error responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Global Exception Handling with &lt;code&gt;@ControllerAdvice&lt;/code&gt;&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@ControllerAdvice&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GlobalExceptionHandler&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@ExceptionHandler&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ResourceNotFoundException&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;ErrorResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleNotFound&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ResourceNotFoundException&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;ErrorResponse&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ErrorResponse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Resource not found"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NOT_FOUND&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Example Error Response&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Resource not found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"User with id 123 not found"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This approach makes your API predictable and easier to debug.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;HTTP status codes are essential for building robust, self-descriptive APIs. Choosing the right one improves communication, reduces confusion, and makes your API a joy to consume.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Use the correct code for each scenario.
&lt;/li&gt;
&lt;li&gt;✅ Be consistent and follow REST conventions.
&lt;/li&gt;
&lt;li&gt;✅ Provide meaningful error messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Check out the GitHub repository for more examples&lt;/strong&gt; and full implementations. And if you want to go further, &lt;strong&gt;read my next article “Choosing the Right HTTP Method for Your REST API”&lt;/strong&gt;, or discover my &lt;strong&gt;Pro Starter Kit on Gumroad&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gianfcop/springboot-rest-api-starter-lite" rel="noopener noreferrer"&gt;Spring Boot API Starter Kit (Lite) on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/gianfcop98/choosing-the-right-http-method-for-your-rest-api-2lp3"&gt;Choosing the Right HTTP Method for Your REST API&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gianfcop.gumroad.com/" rel="noopener noreferrer"&gt;Pro Starter Kit on Gumroad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>restapi</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Choosing the Right HTTP Method for Your REST API</title>
      <dc:creator>Gianfranco Coppola</dc:creator>
      <pubDate>Sun, 31 Aug 2025 18:36:34 +0000</pubDate>
      <link>https://dev.to/gianfcop98/choosing-the-right-http-method-for-your-rest-api-2lp3</link>
      <guid>https://dev.to/gianfcop98/choosing-the-right-http-method-for-your-rest-api-2lp3</guid>
      <description>&lt;h2&gt;
  
  
  ✅ Choosing the Right HTTP Method for Your REST API
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Introduction
&lt;/h3&gt;

&lt;p&gt;When designing a REST API, choosing the right HTTP method is &lt;strong&gt;more than a convention&lt;/strong&gt;—it’s a fundamental aspect of API design. The HTTP method you pick impacts the &lt;strong&gt;clarity, consistency&lt;/strong&gt;, and &lt;strong&gt;maintainability&lt;/strong&gt; of your API.&lt;/p&gt;

&lt;p&gt;A well-designed API is easy to understand because developers can predict its behavior based on standard HTTP semantics. Misusing methods (e.g., using &lt;code&gt;POST&lt;/code&gt; for everything) can lead to confusion, poor documentation, and even security risks.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ The most common HTTP methods&lt;/li&gt;
&lt;li&gt;✅ When to use each&lt;/li&gt;
&lt;li&gt;✅ Key concepts like &lt;strong&gt;idempotence&lt;/strong&gt; and &lt;strong&gt;safety&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Practical examples using &lt;strong&gt;Spring Boot&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;✅ Real-world tips (file uploads, logical deletes)&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Overview of HTTP Methods
&lt;/h3&gt;

&lt;p&gt;The HTTP specification defines several methods, but in REST APIs, the most common are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GET&lt;/strong&gt;: Retrieve data from the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;POST&lt;/strong&gt;: Create a new resource on the server.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PUT&lt;/strong&gt;: Update or replace an existing resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PATCH&lt;/strong&gt;: Partially update an existing resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DELETE&lt;/strong&gt;: Remove a resource from the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each method has a specific purpose and semantic meaning defined by the &lt;strong&gt;HTTP/1.1 specification (RFC 7231)&lt;/strong&gt;. Let’s see how to use them correctly.&lt;/p&gt;




&lt;h3&gt;
  
  
  🔍 Why Choosing the Right Method Matters
&lt;/h3&gt;

&lt;p&gt;Using the proper HTTP method:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improves &lt;strong&gt;clarity&lt;/strong&gt; for API consumers&lt;/li&gt;
&lt;li&gt;Aligns your API with &lt;strong&gt;RESTful best practices&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Ensures &lt;strong&gt;compatibility&lt;/strong&gt; with HTTP caching, proxies, and tools&lt;/li&gt;
&lt;li&gt;Helps with &lt;strong&gt;SEO&lt;/strong&gt; and API discoverability when exposed as OpenAPI/Swagger&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔑 Common HTTP Methods and When to Use Them
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;GET&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Retrieve resources without modifying them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Safe&lt;/em&gt;: Does not change server state.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Idempotent&lt;/em&gt;: Multiple identical requests return the same result.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example use case:&lt;/strong&gt; Get a list of products or retrieve a specific product.
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  GET /api/products
  GET /api/products/{id}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getAllProducts&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;products&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAllProducts&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;products&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;POST&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Create a new resource &lt;strong&gt;or&lt;/strong&gt; start an action that modifies state.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Not Idempotent&lt;/em&gt;: Calling multiple times creates multiple resources or triggers the action repeatedly.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;201 Created&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example use case:&lt;/strong&gt; &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add a new product&lt;/li&gt;
&lt;li&gt;Upload a file for processing (e.g., importing a CSV)&lt;/li&gt;
&lt;li&gt;Trigger a background job
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;POST /api/products
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Request Body:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Updated Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1099.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"categoryId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Create a new product&lt;/span&gt;
&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;productRequest&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productRequest&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpStatus&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CREATED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Upload a file for processing&lt;/span&gt;
&lt;span class="nd"&gt;@PostMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/import"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;importProducts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@RequestParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nc"&gt;MultipartFile&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;importFromFile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;accepted&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Import started"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🔍 Notice the use of &lt;strong&gt;&lt;code&gt;202 Accepted&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  This status code is perfect when the request is accepted but the processing will occur &lt;strong&gt;asynchronously&lt;/strong&gt; (e.g., background job, file import).&lt;/li&gt;
&lt;li&gt;  The client can later check the status of the operation via another endpoint (polling) or with a callback mechanism.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  &lt;strong&gt;PUT&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Replace the entire resource with a new one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Idempotent&lt;/em&gt;: Multiple identical requests result in the same state.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt; or &lt;code&gt;204 No Content&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example use case:&lt;/strong&gt; Update all details of a product.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PUT /api/products/10
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Request Body:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Updated Laptop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1099.99&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"categoryId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PutMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;updateProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;PATCH&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Partially update a resource (modify only specific fields)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Not necessarily Idempotent&lt;/em&gt;: Depends on implementation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;200 OK&lt;/code&gt; or &lt;code&gt;204 No Content&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Example use case:&lt;/strong&gt; Update only the price of a product.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PATCH /api/products/10
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;Request Body:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;899.99&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@PatchMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;updateProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;partialUpdate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;updates&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ok&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  &lt;strong&gt;DELETE&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Purpose:&lt;/strong&gt; Delete an existing resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Characteristics&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Idempotent&lt;/em&gt;: Deleting the same resource multiple times has the same effect.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Response:&lt;/strong&gt; &lt;code&gt;204 No Content&lt;/code&gt;
&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Example use case:&lt;/strong&gt; Delete a product.
&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  DELETE /api/products/10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DeleteMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/products/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Void&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;deleteProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deleteProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;noContent&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  🔍 What About Logical Deletion?
&lt;/h4&gt;

&lt;p&gt;In some systems, we don't actually remove the record from the database, but &lt;strong&gt;mark it as deleted&lt;/strong&gt; (soft delete).&lt;br&gt;&lt;br&gt;
You have &lt;strong&gt;two common options&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;DELETE&lt;/code&gt; but implement it as logical deletion internally&lt;/strong&gt; (recommended for API clarity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;PATCH&lt;/code&gt; with a &lt;code&gt;status&lt;/code&gt; field&lt;/strong&gt;, e.g., &lt;code&gt;{ "status": "INACTIVE" }&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  🛠 Spring Boot Example
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@DeleteMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"api/products/{id}"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;softDeleteProduct&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;productService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;markAsDeleted&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Sets deleted flag instead of removing&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📌 Idempotence and Safety Explained
&lt;/h2&gt;

&lt;p&gt;Two important concepts in HTTP semantics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safety&lt;/strong&gt;: A method is safe if it does not change the server state.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Safe methods&lt;/strong&gt;: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;HEAD&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Idempotence&lt;/strong&gt;: A method is idempotent if multiple identical requests have the same effect as a single request.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent methods&lt;/strong&gt;: &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why does this matter?&lt;/strong&gt;&lt;br&gt;
Understanding these concepts helps you design APIs that behave predictably and handle retries without side effects.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concept&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Safe&lt;/td&gt;
&lt;td&gt;No state changes&lt;/td&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Idempotent&lt;/td&gt;
&lt;td&gt;Same result after multiple identical calls&lt;/td&gt;
&lt;td&gt;PUT, DELETE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  ✅ Summary Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;HTTP Method&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Idempotent&lt;/th&gt;
&lt;th&gt;Safe&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Create&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;Replace&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;Update&lt;/td&gt;
&lt;td&gt;✅ Yes*&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;*&lt;code&gt;PATCH&lt;/code&gt; is generally idempotent if the same patch is applied multiple times.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚠️ Common Mistakes to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;❌ Using &lt;code&gt;GET&lt;/code&gt; to create or update resources&lt;/li&gt;
&lt;li&gt;❌ Using &lt;code&gt;POST&lt;/code&gt; for all operations&lt;/li&gt;
&lt;li&gt;❌ Returning &lt;code&gt;200 OK&lt;/code&gt; for resource creation (should be &lt;code&gt;201 Created&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ Best Practices for HTTP Methods
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;GET&lt;/strong&gt; for retrieval&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;POST&lt;/strong&gt; for creation&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;PUT&lt;/strong&gt; for full update&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;PATCH&lt;/strong&gt; for partial update&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;DELETE&lt;/strong&gt; for deletion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do not abuse POST&lt;/strong&gt;: Use &lt;code&gt;POST&lt;/code&gt; only for creating resources or triggering processes, not for updates or deletions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Be consistent&lt;/strong&gt;: Always use methods according to their semantics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid custom verbs&lt;/strong&gt;: Stick to standard HTTP methods instead of inventing new ones like &lt;code&gt;/users/disable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Handle status codes properly:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;200 OK&lt;/code&gt; → Successful GET or update.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;201 Created&lt;/code&gt; → Resource successfully created.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;202 Accepted&lt;/code&gt; → Request accepted for &lt;strong&gt;asynchronous processing&lt;/strong&gt; (e.g., file import, background job).&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;204 No Content&lt;/code&gt; → Successful DELETE or update with no response body.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;400 Bad Request&lt;/code&gt; → Client sent invalid data.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;404 Not Found&lt;/code&gt; → Resource not found.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Want to dive deeper into &lt;strong&gt;status codes&lt;/strong&gt;?&lt;br&gt;&lt;br&gt;
👉 Read my other article: &lt;a href="https://dev.to/gianfcop98/the-ultimate-guide-to-http-status-codes-in-rest-apis-40cp"&gt;&lt;strong&gt;The Ultimate Guide to HTTP Status Codes in REST APIs&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔗 Conclusion and Related Resources
&lt;/h2&gt;

&lt;p&gt;Choosing the right HTTP method is crucial for building clean, predictable, and RESTful APIs. Stick to standard semantics, respect idempotence and safety, and your API will be easier to understand and maintain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want more?&lt;/strong&gt; Check out the GitHub repository for more examples and full implementations and my &lt;strong&gt;Pro Starter Kit on Gumroad&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/gianfcop/springboot-rest-api-starter-lite" rel="noopener noreferrer"&gt;Spring Boot REST API Starter Kit (Lite) on GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gianfcop.gumroad.com/" rel="noopener noreferrer"&gt;Pro Starter Kit on Gumroad&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>restapi</category>
      <category>webdev</category>
      <category>programming</category>
      <category>springboot</category>
    </item>
  </channel>
</rss>
