<?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: AnkitDevCode</title>
    <description>The latest articles on DEV Community by AnkitDevCode (@ankitdevcode).</description>
    <link>https://dev.to/ankitdevcode</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%2F2887935%2F9bf870cc-f8c4-49fd-9073-cbf75fe0aa50.jpg</url>
      <title>DEV Community: AnkitDevCode</title>
      <link>https://dev.to/ankitdevcode</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ankitdevcode"/>
    <language>en</language>
    <item>
      <title>JPA Mapping with Hibernate- Many-to-Many Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 14 Mar 2026 11:56:20 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-many-to-many-relationship-3p82</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-many-to-many-relationship-3p82</guid>
      <description>&lt;p&gt;In the previous section, we discussed the &lt;a href="https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf"&gt;One-to-Many and Many-to-One Relationship&lt;/a&gt; Now, let’s look at the Many-to-many relationship&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;The Relational Model Behind @ManyToMany&lt;/li&gt;
&lt;li&gt;Unidirectional @ManyToMany — Simplest Form&lt;/li&gt;
&lt;li&gt;Bidirectional @ManyToMany&lt;/li&gt;
&lt;li&gt;equals() and hashCode() — The Critical Foundation&lt;/li&gt;
&lt;li&gt;Intermediate Entity for Join Table With Extra Columns&lt;/li&gt;
&lt;li&gt;Fetch Strategies &amp;amp; The N+1 Problem&lt;/li&gt;
&lt;li&gt;Cascade Types — What to Use and When&lt;/li&gt;
&lt;li&gt;Serialization — Avoiding Infinite Recursion&lt;/li&gt;
&lt;li&gt;Performance Best Practices&lt;/li&gt;
&lt;li&gt;Quick Reference — Best Practices vs Pitfalls&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. Introduction
&lt;/h2&gt;

&lt;p&gt;A many-to-many (M:N) relationship is one of the most common yet most misunderstood associations in relational modeling. When mapped carelessly in JPA/Hibernate, it becomes a prime source of N+1 query problems, infinite JSON recursion, unnecessary eager loading, and subtle data-integrity bugs.&lt;/p&gt;

&lt;p&gt;This article walks through every important aspect of &lt;code&gt;@ManyToMany&lt;/code&gt;, from the simplest unidirectional form to a full intermediate-entity approach, and pairs every concept with the best practices that keep your application performant and maintainable.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Relational Model Behind @ManyToMany
&lt;/h2&gt;

&lt;p&gt;In a relational database, an M:N relationship is always implemented via a &lt;strong&gt;join table&lt;/strong&gt; (also called a bridge or association table). For example, a &lt;code&gt;Student ↔ Course&lt;/code&gt; relationship requires a &lt;code&gt;student_course&lt;/code&gt; join table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;students           student_course         courses
──────────────     ─────────────────      ──────────────────
student_id (PK)    student_id  (FK) ─▶    course_id (PK)
name               course_id   (FK) ─▶    title
email              enrolled_at            credits
                   grade
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If the join table carries extra columns (&lt;code&gt;enrolled_at&lt;/code&gt;, &lt;code&gt;grade&lt;/code&gt;), you &lt;strong&gt;must&lt;/strong&gt; model it as a separate entity — a plain &lt;code&gt;@JoinTable&lt;/code&gt; annotation cannot capture those columns.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Unidirectional @ManyToMany — Simplest Form
&lt;/h2&gt;

&lt;p&gt;Use this when only one side needs to navigate to the other, and the join table has &lt;strong&gt;no extra columns&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3.1 Mapping
&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;@Entity&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;Student&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="kd"&gt;private&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="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;@ManyToMany&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinTable&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;"student_course"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&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;"student_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;inverseJoinColumns&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;"course_id"&lt;/span&gt;&lt;span class="o"&gt;)&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;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// ← Set, never List&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&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;Course&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="kd"&gt;private&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// No back-reference here → unidirectional&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice — Use &lt;code&gt;Set&lt;/code&gt;, not &lt;code&gt;List&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; for &lt;code&gt;@ManyToMany&lt;/code&gt; collections. Hibernate's handling of &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; in many-to-many associations can throw &lt;code&gt;MultipleBagFetchException&lt;/code&gt; when fetching multiple bag collections in the same query, and may produce duplicate records.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  4. Bidirectional @ManyToMany
&lt;/h2&gt;

&lt;p&gt;Bidirectional mapping lets both sides navigate to each other. Exactly &lt;strong&gt;one side&lt;/strong&gt; must be the owning side (holds &lt;code&gt;@JoinTable&lt;/code&gt;); the other is the inverse side (uses &lt;code&gt;mappedBy&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  4.1 Mapping
&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;@Entity&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;Student&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;                            &lt;span class="c1"&gt;// OWNING SIDE&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToMany&lt;/span&gt;
    &lt;span class="nd"&gt;@JoinTable&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;"student_course"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&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;"student_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;inverseJoinColumns&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;"course_id"&lt;/span&gt;&lt;span class="o"&gt;)&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;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&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;Course&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;                            &lt;span class="c1"&gt;// INVERSE SIDE&lt;/span&gt;
    &lt;span class="nd"&gt;@ManyToMany&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;"courses"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// mappedBy is mandatory&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — Forgetting &lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Without &lt;code&gt;mappedBy&lt;/code&gt; on the inverse side, JPA creates &lt;strong&gt;two independent join tables&lt;/strong&gt; and double-inserts every link row. Always declare &lt;code&gt;mappedBy&lt;/code&gt; on exactly one side.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4.2 Keeping Both Sides in Sync
&lt;/h3&gt;

&lt;p&gt;In a bidirectional relationship you must update both sides programmatically in your helper methods, because the in-memory state is independent of the database state until flush:&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;// Add a convenience method on the owning side&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;enroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&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;courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStudents&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&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="c1"&gt;// keep inverse in sync&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;unenroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&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;courses&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;course&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getStudents&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;remove&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. equals() and hashCode() — The Critical Foundation
&lt;/h2&gt;

&lt;p&gt;Hibernate uses &lt;code&gt;equals()&lt;/code&gt; and &lt;code&gt;hashCode()&lt;/code&gt; to determine whether two entity instances represent the same row, especially when adding/removing from &lt;code&gt;Set&lt;/code&gt; collections and when merging detached entities. The &lt;strong&gt;default &lt;code&gt;Object&lt;/code&gt; identity implementation breaks all of this&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  5.1 Correct Implementation (Business Key or UUID)
&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;@Entity&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;Course&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="kd"&gt;private&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;@NaturalId&lt;/span&gt;                     &lt;span class="c1"&gt;// Hibernate annotation&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// e.g. "CS-101" — stable business key&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;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&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="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&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;true&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;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Course&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;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&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;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;courseCode&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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&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;Objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;courseCode&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// must be stable across states&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;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — Using &lt;code&gt;id&lt;/code&gt; for &lt;code&gt;hashCode&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Never base &lt;code&gt;hashCode&lt;/code&gt; on &lt;code&gt;@Id&lt;/code&gt; if entities can be in a &lt;code&gt;Set&lt;/code&gt; before being persisted. A transient entity has &lt;code&gt;id = null&lt;/code&gt;, so its &lt;code&gt;hashCode&lt;/code&gt; changes on persist, which &lt;strong&gt;silently corrupts&lt;/strong&gt; any &lt;code&gt;Set&lt;/code&gt; or &lt;code&gt;HashMap&lt;/code&gt; that contained it.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Intermediate Entity for Join Table With Extra Columns
&lt;/h2&gt;

&lt;p&gt;When the join table needs to store data (enrollment date, grade, seat number, etc.), replace the &lt;code&gt;@ManyToMany&lt;/code&gt; shortcut with an &lt;strong&gt;explicit intermediate entity&lt;/strong&gt;. This is the most robust and recommended pattern in production systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  6.1 Composite Key Class
&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;@Embeddable&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;EnrollmentId&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;Serializable&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"student_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;Long&lt;/span&gt; &lt;span class="n"&gt;studentId&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"course_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;Long&lt;/span&gt; &lt;span class="n"&gt;courseId&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// equals() + hashCode() required for @Embeddable PKs&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;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&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;@Override&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;hashCode&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6.2 Enrollment (Intermediate) Entity
&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;@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;"student_course"&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;Enrollment&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@EmbeddedId&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;EnrollmentId&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;new&lt;/span&gt; &lt;span class="nc"&gt;EnrollmentId&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;@MapsId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"studentId"&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;Student&lt;/span&gt; &lt;span class="n"&gt;student&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;@MapsId&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"courseId"&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;Course&lt;/span&gt; &lt;span class="n"&gt;course&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;enrolledAt&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;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;grade&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.3 Parent Entities
&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;@Entity&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;Student&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;"student"&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Enrollment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;enrollments&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;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;enroll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;course&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;date&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Enrollment&lt;/span&gt; &lt;span class="n"&gt;e&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;Enrollment&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="na"&gt;setStudent&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="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setCourse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;course&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="na"&gt;setEnrolledAt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;enrollments&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&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="nd"&gt;@Entity&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;Course&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;"course"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// no cascade from Course side&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Enrollment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;enrollments&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice — Cascade only from the aggregate root&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Apply &lt;code&gt;CascadeType.ALL&lt;/code&gt; + &lt;code&gt;orphanRemoval&lt;/code&gt; only on the owning aggregate root side (&lt;code&gt;Student&lt;/code&gt;). Do &lt;strong&gt;not&lt;/strong&gt; cascade from &lt;code&gt;Course&lt;/code&gt; — it is a separate aggregate and should not delete enrollments when a course is touched.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. Fetch Strategies &amp;amp; The N+1 Problem
&lt;/h2&gt;

&lt;p&gt;Fetch strategy is the single most impactful performance decision in any JPA application.&lt;/p&gt;

&lt;h3&gt;
  
  
  7.1 Always Use LAZY — Never EAGER
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ✅ Correct — LAZY is the safe default&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToMany&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Wrong — loads ALL courses for ALL students every time a Student is loaded&lt;/span&gt;
&lt;span class="nd"&gt;@ManyToMany&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;EAGER&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;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.2 Solving N+1 With JOIN FETCH
&lt;/h3&gt;

&lt;p&gt;Even with LAZY loading, iterating a collection inside a loop produces one SQL query per iteration. Fix this with a &lt;code&gt;JOIN FETCH&lt;/code&gt; JPQL query:&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;// N+1 — fires one extra query per student&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;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT s FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Student&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="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCourses&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// N hits&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Fixed — single JOIN query&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;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;em&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT DISTINCT s FROM Student s JOIN FETCH s.courses"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="nc"&gt;Student&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="na"&gt;getResultList&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  7.3 Using @BatchSize as a Middle Ground
&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;@ManyToMany&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;@BatchSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="c1"&gt;// loads 25 students' courses in one IN (...) query&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — &lt;code&gt;MultipleBagFetchException&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You cannot &lt;code&gt;JOIN FETCH&lt;/code&gt; two &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; collections in the same JPQL query. Hibernate throws &lt;code&gt;MultipleBagFetchException&lt;/code&gt;. &lt;strong&gt;Fix:&lt;/strong&gt; change both to &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt;, or fetch one in JPQL and use &lt;code&gt;@BatchSize&lt;/code&gt; for the second.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  8. Cascade Types — What to Use and When
&lt;/h2&gt;

&lt;p&gt;Cascade types control which JPA lifecycle operations (&lt;code&gt;PERSIST&lt;/code&gt;, &lt;code&gt;MERGE&lt;/code&gt;, &lt;code&gt;REMOVE&lt;/code&gt;, etc.) are propagated from parent to child.&lt;/p&gt;

&lt;h3&gt;
  
  
  8.1 Recommended Cascade Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Cascade&lt;/th&gt;
&lt;th&gt;orphanRemoval&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Simple &lt;code&gt;@ManyToMany&lt;/code&gt; (no extra cols)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PERSIST, MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Do &lt;strong&gt;NOT&lt;/strong&gt; use &lt;code&gt;REMOVE&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate entity (owned)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only from aggregate root&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intermediate entity (shared)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PERSIST, MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shared = don't remove&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Course → Enrollment&lt;/code&gt; (inverse)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;(none)&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Let &lt;code&gt;Student&lt;/code&gt; own it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;❌ &lt;strong&gt;Pitfall — &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; on &lt;code&gt;@ManyToMany&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; (or &lt;code&gt;ALL&lt;/code&gt;) on a plain &lt;code&gt;@ManyToMany&lt;/code&gt; will delete the &lt;strong&gt;related entities themselves&lt;/strong&gt; — not just the join row. Removing one &lt;code&gt;Student&lt;/code&gt; will delete all their &lt;code&gt;Course&lt;/code&gt; records from the courses table, affecting every other enrolled student.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Serialization — Avoiding Infinite Recursion
&lt;/h2&gt;

&lt;p&gt;Bidirectional relationships create circular object graphs. When Jackson (or any JSON library) tries to serialize a &lt;code&gt;Student&lt;/code&gt; that contains &lt;code&gt;Courses&lt;/code&gt;, which contain &lt;code&gt;Students&lt;/code&gt;, which contain &lt;code&gt;Courses&lt;/code&gt;... it throws a &lt;code&gt;StackOverflowError&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  9.1 Jackson Annotations
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// On the owning side (Student)&lt;/span&gt;
&lt;span class="nd"&gt;@JsonManagedReference&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// On the inverse side (Course)&lt;/span&gt;
&lt;span class="nd"&gt;@JsonBackReference&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;students&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// this side is NOT serialized&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9.2 Better: Use DTOs (Recommended)
&lt;/h3&gt;

&lt;p&gt;Never serialize JPA entities directly to your API layer. Use dedicated DTO/response classes:&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;// DTO — safe, no cycles, no Hibernate proxies&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;CourseDTO&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;title&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;credits&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;CourseDTO&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Course&lt;/span&gt; &lt;span class="n"&gt;c&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;CourseDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&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;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTitle&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCredits&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="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;StudentDTO&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;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;CourseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;courses&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;StudentDTO&lt;/span&gt; &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt; &lt;span class="n"&gt;s&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;StudentDTO&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;s&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;s&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;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCourses&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="na"&gt;map&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;CourseDTO:&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;&lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;collect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Collectors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toSet&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;
  
  
  10. Performance Best Practices
&lt;/h2&gt;

&lt;h3&gt;
  
  
  10.1 Projections and DTO Queries
&lt;/h3&gt;

&lt;p&gt;For read-heavy endpoints, skip entity loading entirely and query directly into DTOs:&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;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT new com.example.dto.StudentCourseDTO(s.name, c.title) "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt;
       &lt;span class="s"&gt;"FROM Student s JOIN s.courses c WHERE s.id = :studentId"&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;StudentCourseDTO&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findCoursesByStudent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Param&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"studentId"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10.2 Pagination — Never Paginate With JOIN FETCH
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Wrong — Hibernate loads ALL rows into memory, then paginates&lt;/span&gt;
&lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT DISTINCT s FROM Student s JOIN FETCH s.courses"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// issues HHH90003004 warning&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Correct — paginate the root entity, load collection separately&lt;/span&gt;
&lt;span class="nd"&gt;@Query&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="s"&gt;"SELECT s FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;countQuery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SELECT COUNT(s) FROM Student s"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;Page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Student&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Pageable&lt;/span&gt; &lt;span class="n"&gt;pageable&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Then use @BatchSize or a second query to load courses&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  10.3 Use @Transactional on Service, Not Repository
&lt;/h3&gt;

&lt;p&gt;Keep your transactions at the service layer where the full unit of work is clear. Opening a transaction in a repository method gives you no control over lazy loading in the service.&lt;/p&gt;




&lt;h2&gt;
  
  
  11. Quick Reference — Best Practices vs Pitfalls
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;✅ Best Practice&lt;/th&gt;
&lt;th&gt;❌ Pitfall to Avoid&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;@ManyToMany&lt;/code&gt; with intermediate entity for extra columns&lt;/td&gt;
&lt;td&gt;Using plain &lt;code&gt;@JoinTable&lt;/code&gt; when join table has extra data&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set &lt;code&gt;fetch = FetchType.LAZY&lt;/code&gt; on both sides&lt;/td&gt;
&lt;td&gt;Using &lt;code&gt;FetchType.EAGER&lt;/code&gt; (causes N+1 queries)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Define owning side clearly with &lt;code&gt;mappedBy&lt;/code&gt; on inverse&lt;/td&gt;
&lt;td&gt;Bidirectional mapping without &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; instead of &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; to avoid duplicates&lt;/td&gt;
&lt;td&gt;Using &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; and getting &lt;code&gt;MultipleBagFetchException&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;orphanRemoval&lt;/code&gt; + &lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent side only&lt;/td&gt;
&lt;td&gt;Cascading &lt;code&gt;ALL&lt;/code&gt; on both sides (infinite loops / dual deletes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implement &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; based on business key&lt;/td&gt;
&lt;td&gt;Using default &lt;code&gt;Object&lt;/code&gt; identity for &lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use &lt;code&gt;@BatchSize&lt;/code&gt; or &lt;code&gt;JOIN FETCH&lt;/code&gt; to load related data&lt;/td&gt;
&lt;td&gt;Loading collections in a loop (classic N+1 problem)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use DTOs and projections for read-heavy queries&lt;/td&gt;
&lt;td&gt;Serializing full entity graphs to JSON (&lt;code&gt;StackOverflow&lt;/code&gt; risk)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;p&gt;Many-to-many associations are powerful but require deliberate design. The key takeaways are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; — always.&lt;/strong&gt; &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; in M:N leads to bags, duplicates, and &lt;code&gt;MultipleBagFetchException&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prefer intermediate entity&lt;/strong&gt; — as soon as the join table has any extra column, model it explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep &lt;code&gt;fetch=LAZY&lt;/code&gt; everywhere&lt;/strong&gt; — solve loading problems with &lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@BatchSize&lt;/code&gt;, not &lt;code&gt;EAGER&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a stable business key&lt;/strong&gt; — never rely on the database-generated &lt;code&gt;id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade carefully&lt;/strong&gt; — &lt;code&gt;REMOVE&lt;/code&gt; and &lt;code&gt;orphanRemoval&lt;/code&gt; belong only on aggregate-root-owned children.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use DTOs at the API layer&lt;/strong&gt; — never serialize entity graphs directly to JSON.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Measure first&lt;/strong&gt; — use Hibernate's statistics or a query logger (P6Spy/datasource-proxy) to confirm you have no N+1 queries before shipping.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
      <category>java</category>
    </item>
    <item>
      <title>JPA Mapping with Hibernate- One-to-Many and Many-to-One Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 14 Mar 2026 11:14:02 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-many-and-many-to-one-relationship-38nf</guid>
      <description>&lt;p&gt;In the previous section, we discussed the &lt;a href="https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41"&gt;One-to-One Relationship&lt;/a&gt; Now, let’s look at the one-to-many and many-to-one relationships&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What Are These Relationships?&lt;/li&gt;
&lt;li&gt;
Setting Up a Bidirectional Relationship

&lt;ul&gt;
&lt;li&gt;The Parent (One Side)&lt;/li&gt;
&lt;li&gt;The Child (Many Side)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Unidirectional @OneToMany — Avoid It&lt;/li&gt;
&lt;li&gt;
Best Practices

&lt;ul&gt;
&lt;li&gt;1. Always Use Lazy Fetching&lt;/li&gt;
&lt;li&gt;2. Use List Instead of Set&lt;/li&gt;
&lt;li&gt;3. Keep Both Sides in Sync&lt;/li&gt;
&lt;li&gt;4. Use orphanRemoval for Parent-Owned Children&lt;/li&gt;
&lt;li&gt;5. Never Cascade on @ManyToOne&lt;/li&gt;
&lt;li&gt;6. Override equals and hashCode&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Common Pitfalls

&lt;ul&gt;
&lt;li&gt;The N+1 Query Problem&lt;/li&gt;
&lt;li&gt;LazyInitializationException&lt;/li&gt;
&lt;li&gt;Out-of-Sync Bidirectional State&lt;/li&gt;
&lt;li&gt;CascadeType.REMOVE + orphanRemoval Redundancy&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Cascade Types Reference&lt;/li&gt;
&lt;li&gt;Quick Reference Table&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What Are These Relationships?
&lt;/h2&gt;

&lt;p&gt;In JPA, &lt;strong&gt;One-to-Many&lt;/strong&gt; and &lt;strong&gt;Many-to-One&lt;/strong&gt; are two sides of the same coin. A &lt;code&gt;Department&lt;/code&gt; can have many &lt;code&gt;Employee&lt;/code&gt; records, and each &lt;code&gt;Employee&lt;/code&gt; belongs to one &lt;code&gt;Department&lt;/code&gt;. In the database, the foreign key (&lt;code&gt;department_id&lt;/code&gt;) lives on the &lt;code&gt;employees&lt;/code&gt; table — making &lt;code&gt;Employee&lt;/code&gt; the &lt;strong&gt;owning side&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Terminology
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Term&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Owning side&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The entity that holds the foreign key column in the database — always the &lt;code&gt;@ManyToOne&lt;/code&gt; side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Inverse side&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;The entity with &lt;code&gt;mappedBy&lt;/code&gt; — does not control the FK, just navigates the relationship&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Used on the inverse (non-owning) side to point back to the owning field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;CascadeType&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Which operations (&lt;code&gt;PERSIST&lt;/code&gt;, &lt;code&gt;MERGE&lt;/code&gt;, &lt;code&gt;REMOVE&lt;/code&gt;, etc.) propagate from parent to child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automatically deletes a child row when it is removed from the parent collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Child data is loaded on demand when accessed — default for collections&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;FetchType.EAGER&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Child data is always loaded with the parent — default for single associations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Database Representation
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;departments            employees
────────────────       ──────────────────────
dept_id  (PK)    ◀─    employee_id  (PK)
name                   name
                       department_id  (FK)  ← FK lives here (owning side)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Setting Up a Bidirectional Relationship
&lt;/h2&gt;

&lt;p&gt;The recommended approach is a &lt;strong&gt;bidirectional&lt;/strong&gt; mapping, where both entities know about each other.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Parent (One Side)
&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;@Entity&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;Department&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="kd"&gt;private&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="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;@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;"department"&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="kd"&gt;private&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;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Always use helper methods to keep both sides in sync&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;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&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="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;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Child (Many Side)
&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;@Entity&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;Employee&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="kd"&gt;private&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="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;@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="c1"&gt;// Always override the default!&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;"department_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;Department&lt;/span&gt; &lt;span class="n"&gt;department&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why &lt;code&gt;mappedBy&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
It tells JPA that &lt;code&gt;Department&lt;/code&gt; is the &lt;em&gt;inverse&lt;/em&gt; side — it does not own the FK column. Without it, Hibernate creates a join table, which is almost never what you want.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  3. Unidirectional @OneToMany — Avoid It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Looks clean but generates extra SQL&lt;/span&gt;
&lt;span class="nd"&gt;@OneToMany&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;"department_id"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// no mappedBy&lt;/span&gt;
&lt;span class="kd"&gt;private&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;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&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;Department&lt;/code&gt; knows about &lt;code&gt;Employee&lt;/code&gt;, but &lt;code&gt;Employee&lt;/code&gt; does not reference &lt;code&gt;Department&lt;/code&gt;. When Hibernate cannot write the FK during the child &lt;code&gt;INSERT&lt;/code&gt; (because it doesn't own the column), it issues &lt;strong&gt;extra &lt;code&gt;UPDATE&lt;/code&gt; statements&lt;/strong&gt; afterward. This produces unnecessary SQL and hurts performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problems with unidirectional &lt;code&gt;@OneToMany&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generates extra &lt;code&gt;UPDATE&lt;/code&gt; queries on every insert&lt;/li&gt;
&lt;li&gt;Poor performance at scale&lt;/li&gt;
&lt;li&gt;May silently create join tables if &lt;code&gt;@JoinColumn&lt;/code&gt; is omitted&lt;/li&gt;
&lt;li&gt;Harder to maintain relationship consistency&lt;/li&gt;
&lt;li&gt;Limited query capability from the child side&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Best Practice:&lt;/strong&gt; Always use &lt;code&gt;@ManyToOne&lt;/code&gt; as the owning side and &lt;code&gt;@OneToMany(mappedBy = "...")&lt;/code&gt; as the inverse side for bidirectional relationships.&lt;/p&gt;
&lt;/blockquote&gt;




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

&lt;h3&gt;
  
  
  1. Always Use Lazy Fetching
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@ManyToOne&lt;/code&gt; defaults to &lt;code&gt;EAGER&lt;/code&gt; — a hidden performance trap that loads the parent every time you load a child, even when you don't need it. Always override it explicitly.&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;@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="c1"&gt;// override the EAGER default&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;"department_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;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@OneToMany&lt;/code&gt; is lazy by default, which is correct — leave it as-is.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Use &lt;code&gt;List&lt;/code&gt; Instead of &lt;code&gt;Set&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If a JPA entity’s equals() and hashCode() are based on an auto-generated identifier, developers often use a constant hashCode() until the ID is assigned.&lt;/p&gt;

&lt;p&gt;However, this hurts Set performance because all elements end up in the same hash bucket. As a result, every add() or contains() operation requires scanning all elements, degrading performance from O(1) to O(n).&lt;/p&gt;

&lt;p&gt;In a Set, duplicate elements are not allowed because it relies on equals() and hashCode() to determine uniqueness. However, in Jakarta Persistence entities, equality typically corresponds to the identity of the associated database record. Since every child entity has a unique primary key (or a unique business key), entities retrieved from the database will already be distinct.&lt;br&gt;
Because of this, a Set rarely provides any real benefit for a bidirectional @OneToMany association. Hibernate will not return duplicate rows for the same entity when loading the collection, so the collection naturally contains unique elements.&lt;/p&gt;

&lt;p&gt;From a performance perspective, List implementations such as ArrayList are usually faster and simpler.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Keep Both Sides in Sync
&lt;/h3&gt;

&lt;p&gt;In a bidirectional relationship, JPA uses the &lt;em&gt;owning side&lt;/em&gt; to write to the DB. If you only update the inverse side (&lt;code&gt;Department.employees&lt;/code&gt;), the FK won't be persisted. Always use helper methods:&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;// ✅ Good — updates both sides&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ❌ Bad — only updates the inverse side, FK not persisted&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;employee&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Use &lt;code&gt;orphanRemoval&lt;/code&gt; for Parent-Owned Children
&lt;/h3&gt;

&lt;p&gt;When the parent fully owns the lifecycle of its children, enable &lt;code&gt;orphanRemoval&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;@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;"department"&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Removing a child from the collection now &lt;strong&gt;automatically deletes it&lt;/strong&gt; from the database on the next flush — no need for an explicit &lt;code&gt;em.remove()&lt;/code&gt; call.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Never Cascade on &lt;code&gt;@ManyToOne&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;cascade = CascadeType.ALL&lt;/code&gt; belongs on the parent (&lt;code&gt;@OneToMany&lt;/code&gt;) side. Adding it to &lt;code&gt;@ManyToOne&lt;/code&gt; can cause &lt;strong&gt;catastrophic side effects&lt;/strong&gt; — like deleting a &lt;code&gt;Department&lt;/code&gt; when you delete one &lt;code&gt;Employee&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;// ❌ WRONG — could delete the entire department!&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;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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ CORRECT — no cascade on the child side&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;"department_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;Department&lt;/span&gt; &lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Override &lt;code&gt;equals&lt;/code&gt; and &lt;code&gt;hashCode&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Use a natural &lt;strong&gt;business key&lt;/strong&gt; (e.g., &lt;code&gt;email&lt;/code&gt; for an employee), not the database ID. The ID is &lt;code&gt;null&lt;/code&gt; before the entity is persisted, so ID-based equality breaks collections like &lt;code&gt;HashSet&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;@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;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&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="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&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;true&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;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Employee&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;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&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;email&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&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="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;  &lt;span class="c1"&gt;// stable across transient and persistent states&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Common Pitfalls
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The N+1 Query Problem
&lt;/h3&gt;

&lt;p&gt;The most common JPA performance issue. Loading a list of departments, then accessing each one's &lt;code&gt;employees&lt;/code&gt;, fires &lt;strong&gt;one query per department&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="c1"&gt;// ❌ Triggers 1 + N queries&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;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;depts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;depts&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;d&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// N extra queries!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix 1 — &lt;code&gt;JOIN FETCH&lt;/code&gt; in JPQL:&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;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.employees"&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;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithEmployees&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;Fix 2 — &lt;code&gt;@EntityGraph&lt;/code&gt; (Spring Data):&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;@EntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributePaths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"employees"&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;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&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;Fix 3 — &lt;code&gt;@BatchSize&lt;/code&gt; (Hibernate):&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;@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;"department"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nd"&gt;@BatchSize&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&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="kd"&gt;private&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;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;@BatchSize&lt;/code&gt; loads children in batches using an &lt;code&gt;IN (...)&lt;/code&gt; clause rather than one query each — a low-friction option when you don't want to change your JPQL queries.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;LazyInitializationException&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Accessing a lazy collection &lt;strong&gt;outside of an active Hibernate session&lt;/strong&gt; (e.g., after the transaction has closed) throws this exception.&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;// ❌ Fails if the session is already closed&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// LazyInitializationException!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Always access lazy relationships within a &lt;code&gt;@Transactional&lt;/code&gt; context, or eagerly fetch what you need in the repository query.&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;// ✅ Safe — transaction is open for the duration of the method&lt;/span&gt;
&lt;span class="nd"&gt;@Transactional&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;DepartmentDTO&lt;/span&gt; &lt;span class="nf"&gt;getDepartmentWithEmployees&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="o"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;dept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repo&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="na"&gt;orElseThrow&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// safe here&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;DepartmentDTO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dept&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;
  
  
  Out-of-Sync Bidirectional State
&lt;/h3&gt;

&lt;p&gt;Setting the child's reference without updating the parent's collection (or vice versa) leaves the &lt;strong&gt;in-memory object graph inconsistent&lt;/strong&gt;, even if the DB is correct after flush.&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;// ❌ Only sets one side — department.getEmployees() won't contain emp in memory&lt;/span&gt;
&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Use the helper method to keep both sides consistent&lt;/span&gt;
&lt;span class="n"&gt;department&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; + &lt;code&gt;orphanRemoval&lt;/code&gt; Redundancy
&lt;/h3&gt;

&lt;p&gt;Both cause child deletion when the parent is removed. Using both together is redundant and signals a misunderstanding of their purpose.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CascadeType.REMOVE&lt;/code&gt;&lt;/strong&gt; — deletes children when the parent entity is explicitly removed via &lt;code&gt;em.remove()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;orphanRemoval = true&lt;/code&gt;&lt;/strong&gt; — deletes children when they are removed from the parent's collection &lt;em&gt;or&lt;/em&gt; when the parent is deleted.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;✅ &lt;strong&gt;Use &lt;code&gt;orphanRemoval = true&lt;/code&gt;&lt;/strong&gt; when the parent fully owns the child lifecycle — it covers the &lt;code&gt;REMOVE&lt;/code&gt; case and also handles disassociation from the collection. You do not need both.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  6. Cascade Types Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cascade Type&lt;/th&gt;
&lt;th&gt;Effect&lt;/th&gt;
&lt;th&gt;Use When&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PERSIST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is saved for the first time, unsaved children are also saved&lt;/td&gt;
&lt;td&gt;Always safe on parent side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;MERGE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is merged (updated), children are also merged&lt;/td&gt;
&lt;td&gt;Almost always paired with &lt;code&gt;PERSIST&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REMOVE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is deleted, all children are deleted&lt;/td&gt;
&lt;td&gt;Only on owned children; use &lt;code&gt;orphanRemoval&lt;/code&gt; instead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;REFRESH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is refreshed from DB, children are also refreshed&lt;/td&gt;
&lt;td&gt;Rarely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DETACH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;When parent is detached from context, children are also detached&lt;/td&gt;
&lt;td&gt;Rarely needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ALL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Shorthand for all of the above&lt;/td&gt;
&lt;td&gt;Convenient but &lt;strong&gt;potentially dangerous&lt;/strong&gt; — review carefully&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  7. Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Attribute&lt;/th&gt;
&lt;th&gt;Recommended Setting&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToMany&lt;/code&gt; fetch&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LAZY&lt;/code&gt; &lt;em&gt;(default)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Don't override unless necessary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@ManyToOne&lt;/code&gt; fetch&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;LAZY&lt;/code&gt; &lt;em&gt;(explicit)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;Default is &lt;code&gt;EAGER&lt;/code&gt; — &lt;strong&gt;always override&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;On the &lt;code&gt;@OneToMany&lt;/code&gt; side&lt;/td&gt;
&lt;td&gt;Marks the inverse (non-owning) side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cascade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent only&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never&lt;/strong&gt; put cascade on &lt;code&gt;@ManyToOne&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; for fully owned children&lt;/td&gt;
&lt;td&gt;Handles both removal and disassociation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection type&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Set&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safer than &lt;code&gt;List&amp;lt;T&amp;gt;&lt;/code&gt; with multiple joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 prevention&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@EntityGraph&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@BatchSize&lt;/code&gt; is a low-friction alternative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Based on business key&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Never&lt;/strong&gt; based on database ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unidirectional &lt;code&gt;@OneToMany&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Avoid&lt;/td&gt;
&lt;td&gt;Extra &lt;code&gt;UPDATE&lt;/code&gt; SQL, harder to maintain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Helper methods&lt;/td&gt;
&lt;td&gt;Always define on parent&lt;/td&gt;
&lt;td&gt;Keep both sides of bidirectional mapping in sync&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  8. Summary
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;FK lives on the many side&lt;/strong&gt; — that entity is the &lt;strong&gt;owning side&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;mappedBy&lt;/code&gt; on &lt;code&gt;@OneToMany&lt;/code&gt; to declare the inverse side and avoid a spurious join table.&lt;/li&gt;
&lt;li&gt;Always set &lt;code&gt;@ManyToOne(fetch = FetchType.LAZY)&lt;/code&gt; — the default &lt;code&gt;EAGER&lt;/code&gt; is a performance trap.&lt;/li&gt;
&lt;li&gt;Write &lt;strong&gt;bidirectional helper methods&lt;/strong&gt; (&lt;code&gt;addEmployee&lt;/code&gt; / &lt;code&gt;removeEmployee&lt;/code&gt;) to keep both sides in sync.&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;Set&amp;lt;&amp;gt;&lt;/code&gt; over &lt;code&gt;List&amp;lt;&amp;gt;&lt;/code&gt; to avoid Cartesian products when joining multiple collections.&lt;/li&gt;
&lt;li&gt;Watch for the &lt;strong&gt;N+1 problem&lt;/strong&gt; whenever you iterate over collections — fix with &lt;code&gt;JOIN FETCH&lt;/code&gt;, &lt;code&gt;@EntityGraph&lt;/code&gt;, or &lt;code&gt;@BatchSize&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never cascade&lt;/strong&gt; from child to parent (&lt;code&gt;@ManyToOne&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Prefer &lt;code&gt;orphanRemoval = true&lt;/code&gt; over &lt;code&gt;CascadeType.REMOVE&lt;/code&gt; — it handles more cases cleanly.&lt;/li&gt;
&lt;li&gt;Base &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a &lt;strong&gt;stable business key&lt;/strong&gt;, never on the auto-generated database ID.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Code
&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;@Entity&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;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Department&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="kd"&gt;private&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="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;@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;"department"&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="kd"&gt;private&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;Employee&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;employees&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;ArrayList&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Always use helper methods to keep both sides in sync&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;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&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="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;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;employees&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;emp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDepartment&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="o"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;@Entity&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;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Employee&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="kd"&gt;private&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="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="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="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;Employee&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;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;span class="k"&gt;this&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;name&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;email&lt;/span&gt; &lt;span class="o"&gt;=&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;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="c1"&gt;// Always override the default!&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;"department_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;Department&lt;/span&gt; &lt;span class="n"&gt;department&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;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&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="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&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;true&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;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;Employee&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;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;other&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;o&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;email&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;other&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;email&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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&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="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;DepartmentRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Department&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&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;interface&lt;/span&gt; &lt;span class="nc"&gt;EmployeeRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Employee&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;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;@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;DataLoader&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ApplicationRunner&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;DepartmentRepository&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&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;EmployeeRepository&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&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;DataLoader&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;DepartmentRepository&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;EmployeeRepository&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&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;departmentRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&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;employeeRepo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;employeeRepo&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="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;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ApplicationArguments&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 1: Create a Department entity
         * No DB query yet because the entity is only created in memory.
         */&lt;/span&gt;
        &lt;span class="nc"&gt;Department&lt;/span&gt; &lt;span class="n"&gt;engineering&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;Department&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Engineering"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 2: Create Employee entities
         * These are also only in memory at this point.
         */&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;alice&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;Employee&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="s"&gt;"alice@example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Employee&lt;/span&gt; &lt;span class="n"&gt;bob&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;Employee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Bob"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;   &lt;span class="s"&gt;"bob@example.com"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 3: Link employees to department
         * The helper method usually:
         * 1. Adds employee to department.employees list
         * 2. Sets employee.department = this
         *
         * This ensures both sides of the bidirectional relationship stay consistent.
         */&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 4: Persist department
         *
         * Because the Department entity likely has:
         *
         * @OneToMany(mappedBy="department", cascade = CascadeType.ALL, orphanRemoval = true)
         *
         * Hibernate will cascade the persist operation to Employee entities.
         *
         * Expected SQL queries:
         *
         * INSERT INTO department (name)
         * INSERT INTO employee (name, email, department_id)
         * INSERT INTO employee (name, email, department_id)
         *
         * Total queries = 3
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&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;engineering&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 5: Read all departments
         *
         * Expected SQL:
         * SELECT * FROM department
         *
         * If employees collection is LAZY (recommended),
         * employees are NOT fetched until accessed.
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findAll&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Dept: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&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="cm"&gt;/*
         * STEP 6: Find department by ID
         *
         * Expected SQL:
         * SELECT * FROM department WHERE id = ?
         */&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;Department&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dept&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;departmentRepo&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;engineering&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;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ifPresent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Found: "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;d&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="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Department loaded"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

      &lt;span class="c1"&gt;// employees should load only here&lt;/span&gt;
        &lt;span class="n"&gt;dept&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getEmployees&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;forEach&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;-&amp;gt;&lt;/span&gt;
                &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&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="na"&gt;getName&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;


        &lt;span class="cm"&gt;/*
         * STEP 7: Update employee
         *
         * Changing Alice's name marks the entity as dirty.
         *
         * Expected SQL on flush/commit:
         * UPDATE employee SET name='Alice Smith' WHERE id=?
         */&lt;/span&gt;
        &lt;span class="n"&gt;alice&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setName&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Alice Smith"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;employeeRepo&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;alice&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * STEP 8: Remove employee from department
         *
         * Helper method typically:
         * 1. Removes Bob from department.employees list
         * 2. Sets bob.department = null
         *
         * Because orphanRemoval = true:
         * Hibernate will DELETE the employee record automatically.
         *
         * Expected SQL:
         * DELETE FROM employee WHERE id = ?
         */&lt;/span&gt;
        &lt;span class="n"&gt;engineering&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;removeEmployee&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bob&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="cm"&gt;/*
         * Saving department ensures Hibernate detects the orphan removal.
         */&lt;/span&gt;
        &lt;span class="n"&gt;departmentRepo&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;engineering&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;



</description>
      <category>java</category>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
    </item>
    <item>
      <title>JPA Mapping with Hibernate-One-to-One Relationship</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 07 Mar 2026 12:46:24 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41</link>
      <guid>https://dev.to/ankitdevcode/jpa-mapping-with-hibernate-one-to-one-relationship-g41</guid>
      <description>&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What is JPA?&lt;/li&gt;
&lt;li&gt;What is Hibernate?&lt;/li&gt;
&lt;li&gt;JPA vs Hibernate — Key Differences&lt;/li&gt;
&lt;li&gt;Relationship Mapping in JPA — Quick Reference&lt;/li&gt;
&lt;li&gt;
What is a One-to-One Relationship?

&lt;ul&gt;
&lt;li&gt;Unidirectional vs Bidirectional&lt;/li&gt;
&lt;li&gt;How to Identify Owner, FK Side, and Child&lt;/li&gt;
&lt;li&gt;JPA Annotation Summary&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Bidirectional One-to-One — Standard Mapping

&lt;ul&gt;
&lt;li&gt;The Child Entity (Owning Side)&lt;/li&gt;
&lt;li&gt;The Parent Entity (Inverse Side)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
The Lazy Loading Problem on the Inverse Side

&lt;ul&gt;
&lt;li&gt;Why the Extra Query Fires&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Solutions to the Inverse-Side Probe Query

&lt;ul&gt;
&lt;li&gt;Option 1 — @LazyToOne Bytecode Instrumentation&lt;/li&gt;
&lt;li&gt;Option 2 — JOIN FETCH Query&lt;/li&gt;
&lt;li&gt;Option 3 — @NamedEntityGraph&lt;/li&gt;
&lt;li&gt;Option 4 — @MapsId Shared Primary Key (Best &amp;amp; Simplest)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Deep Dive — @MapsId

&lt;ul&gt;
&lt;li&gt;Advantages of @MapsId&lt;/li&gt;
&lt;li&gt;Downsides of @MapsId&lt;/li&gt;
&lt;li&gt;When @MapsId Is Ideal&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Comparison — @JoinColumn vs @MapsId&lt;/li&gt;
&lt;li&gt;Quick Reference Table&lt;/li&gt;
&lt;li&gt;Summary&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  1. What is JPA?
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Java Persistence API (JPA)&lt;/strong&gt; is a &lt;strong&gt;Java specification&lt;/strong&gt; that defines a standard way to manage relational data in Java applications using &lt;strong&gt;Object Relational Mapping (ORM)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It provides a set of &lt;strong&gt;interfaces and annotations&lt;/strong&gt; that allow developers to map Java objects to database tables, perform CRUD operations, and manage persistence without writing large amounts of SQL.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;JPA is a &lt;strong&gt;specification, not an implementation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;It standardizes how Java applications interact with relational databases&lt;/li&gt;
&lt;li&gt;It uses &lt;strong&gt;annotations and configuration&lt;/strong&gt; to map objects to tables&lt;/li&gt;
&lt;li&gt;It simplifies database operations through &lt;strong&gt;entity management and persistence context&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. What is Hibernate?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Hibernate&lt;/strong&gt; is a popular &lt;strong&gt;open-source ORM framework&lt;/strong&gt; that provides a concrete implementation of the &lt;strong&gt;JPA specification&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It allows developers to interact with databases using &lt;strong&gt;Java objects instead of writing complex SQL queries&lt;/strong&gt;, making application code &lt;strong&gt;loosely coupled&lt;/strong&gt; with the underlying database.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Hibernate is a &lt;strong&gt;JPA implementation&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Provides powerful ORM capabilities beyond the JPA spec&lt;/li&gt;
&lt;li&gt;Handles &lt;strong&gt;CRUD operations automatically&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;caching, lazy loading, and transaction management&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reduces boilerplate JDBC code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. JPA vs Hibernate — Key Differences
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;JPA&lt;/th&gt;
&lt;th&gt;Hibernate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Type&lt;/td&gt;
&lt;td&gt;Specification (standard API)&lt;/td&gt;
&lt;td&gt;Implementation of JPA&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Who defines it&lt;/td&gt;
&lt;td&gt;Jakarta EE / Oracle&lt;/td&gt;
&lt;td&gt;Red Hat / JBoss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Can run standalone?&lt;/td&gt;
&lt;td&gt;No — needs an implementation&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra features&lt;/td&gt;
&lt;td&gt;Standard only&lt;/td&gt;
&lt;td&gt;Caching, &lt;code&gt;@BatchSize&lt;/code&gt;, &lt;code&gt;@NaturalId&lt;/code&gt;, etc.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Code coupling&lt;/td&gt;
&lt;td&gt;Low — portable across providers&lt;/td&gt;
&lt;td&gt;Higher — Hibernate-specific annotations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Developers write code using &lt;strong&gt;JPA annotations and interfaces&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hibernate executes the actual database operations&lt;/strong&gt; behind the scenes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Relationship Mapping in JPA — Quick Reference
&lt;/h2&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;Relationship&lt;/th&gt;
&lt;th&gt;FK Location&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@OneToOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One entity ↔ one entity&lt;/td&gt;
&lt;td&gt;Child / owning side&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@OneToMany&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;One entity → many entities&lt;/td&gt;
&lt;td&gt;Child table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ManyToOne&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Many entities → one entity&lt;/td&gt;
&lt;td&gt;Owning entity (FK here)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@ManyToMany&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Many entities ↔ many entities&lt;/td&gt;
&lt;td&gt;Join / junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key best practices across all relationship types:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Always use &lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/strong&gt; on relationships — avoids N+1 query problems&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt; goes on the &lt;em&gt;non-owning&lt;/em&gt; side (the side without the FK column)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cascade carefully&lt;/strong&gt; — only cascade from parent to child, never upward&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@JoinColumn&lt;/code&gt;&lt;/strong&gt; explicitly names your FK column for clarity&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;&lt;code&gt;Set&lt;/code&gt;&lt;/strong&gt; instead of &lt;code&gt;List&lt;/code&gt; for &lt;code&gt;@ManyToMany&lt;/code&gt; to avoid duplicate join queries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  5. What is a One-to-One Relationship?
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;One-to-One relationship&lt;/strong&gt; occurs when &lt;strong&gt;one entity is associated with exactly one instance of another entity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world examples:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Person&lt;/code&gt; → &lt;code&gt;Passport&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;User&lt;/code&gt; → &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Order&lt;/code&gt; → &lt;code&gt;Invoice&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Unidirectional vs Bidirectional
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Unidirectional&lt;/strong&gt; — Only one entity has a reference to the other. Navigation works in one direction only.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User  →  UserProfile      (User knows about UserProfile; UserProfile does NOT know about User)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bidirectional&lt;/strong&gt; — Both entities have a reference to each other. Navigation works in both directions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User  ⇆  UserProfile      (both sides can navigate to the other)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to Identify Owner, FK Side, and Child
&lt;/h3&gt;

&lt;p&gt;Ask yourself these three questions:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Answer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who cannot exist without the other?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;child&lt;/strong&gt; → holds the FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who exists independently?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;parent&lt;/strong&gt; → has &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Who makes sense to delete first?&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;That's the &lt;strong&gt;child&lt;/strong&gt; → cascade from parent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Rule of thumb:&lt;/strong&gt; Child = cannot exist without parent = has &lt;code&gt;@JoinColumn&lt;/code&gt; (FK).&lt;br&gt;
Parent = exists independently = has &lt;code&gt;mappedBy&lt;/code&gt; (no FK).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  JPA Annotation Summary
&lt;/h3&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;Side&lt;/th&gt;
&lt;th&gt;FK&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@JoinColumn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Owning / Child&lt;/td&gt;
&lt;td&gt;FK lives here&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mappedBy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inverse / Parent&lt;/td&gt;
&lt;td&gt;No FK&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@JoinTable&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@ManyToMany&lt;/code&gt; owner&lt;/td&gt;
&lt;td&gt;Junction table&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cascade&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Parent → Child&lt;/td&gt;
&lt;td&gt;Delete parent = delete child&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  6. Bidirectional One-to-One — Standard Mapping
&lt;/h2&gt;

&lt;p&gt;In a &lt;code&gt;User ↔ UserProfile&lt;/code&gt; relationship, &lt;code&gt;UserProfile&lt;/code&gt; is the &lt;strong&gt;child&lt;/strong&gt; because the foreign key (&lt;code&gt;user_id&lt;/code&gt;) lives in the &lt;code&gt;user_profile&lt;/code&gt; table. &lt;code&gt;User&lt;/code&gt; is the &lt;strong&gt;parent&lt;/strong&gt; (inverse side).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users                  user_profile
─────────────          ──────────────────────
id  (PK)        ◀──    id  (PK)
username               phone
password               address
enabled                user_id  (FK) ← FK lives here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Child Entity (Owning Side)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;@JoinColumn&lt;/code&gt; is used on the owning side — the child entity contains the foreign key pointing to the parent's primary key.&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;"user_profile"&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;UserProfile&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="kd"&gt;private&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToOne&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;"user_id"&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="c1"&gt;// FK column lives here&lt;/span&gt;
    &lt;span class="kd"&gt;private&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Parent Entity (Inverse Side)
&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;@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;"users"&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;@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="kd"&gt;private&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="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;50&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="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="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="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="kd"&gt;private&lt;/span&gt; &lt;span class="kt"&gt;boolean&lt;/span&gt; &lt;span class="n"&gt;enabled&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;@OneToOne&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;"user"&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;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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@ElementCollection&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;EAGER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@CollectionTable&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;"user_roles"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;joinColumns&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;"user_id"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;uniqueConstraints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;@UniqueConstraint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;columnNames&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"user_id"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"role"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="nd"&gt;@Enumerated&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;EnumType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STRING&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;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"role"&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Set&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;roles&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;HashSet&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Bidirectional sync helper — keeps both sides consistent&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;setProfile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&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;profile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;profile&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;profile&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;profile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUser&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="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Role helper methods&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;addRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&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;role&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="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&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="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;removeRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&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;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&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="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;hasRole&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Role&lt;/span&gt; &lt;span class="n"&gt;role&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;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&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="kt"&gt;boolean&lt;/span&gt; &lt;span class="nf"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Object&lt;/span&gt; &lt;span class="n"&gt;o&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="k"&gt;this&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;o&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;true&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;o&lt;/span&gt; &lt;span class="k"&gt;instanceof&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;username&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&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="na"&gt;getUsername&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="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;hashCode&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="nf"&gt;getClass&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;hashCode&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;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on &lt;code&gt;@ElementCollection&lt;/code&gt;:&lt;/strong&gt; &lt;code&gt;@ElementCollection&lt;/code&gt; stores simple values (like enum &lt;code&gt;Role&lt;/code&gt;) in a separate table — it is &lt;strong&gt;not&lt;/strong&gt; a full entity relationship, so there is no "other side" to sync.&lt;br&gt;
Keeping &lt;code&gt;FetchType.EAGER&lt;/code&gt; here is intentional when using &lt;strong&gt;Spring Security&lt;/strong&gt; — &lt;code&gt;UserDetails&lt;/code&gt; needs roles immediately on authentication.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  7. The Lazy Loading Problem on the Inverse Side
&lt;/h2&gt;

&lt;p&gt;Declaring &lt;code&gt;fetch = FetchType.LAZY&lt;/code&gt; on the &lt;strong&gt;inverse side&lt;/strong&gt; (&lt;code&gt;mappedBy&lt;/code&gt;) of a &lt;code&gt;@OneToOne&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; actually make it lazy. Hibernate silently fires an extra query anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why the Extra Query Fires
&lt;/h3&gt;

&lt;p&gt;On the &lt;strong&gt;owning side&lt;/strong&gt;, the FK column is in the same row — Hibernate immediately knows if the association is &lt;code&gt;null&lt;/code&gt; or not, and can safely create a proxy.&lt;/p&gt;

&lt;p&gt;On the &lt;strong&gt;inverse side&lt;/strong&gt;, there is no FK column. Hibernate must probe the database just to determine whether a &lt;code&gt;UserProfile&lt;/code&gt; exists for a given &lt;code&gt;User&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;          &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="n"&gt;your&lt;/span&gt; &lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt;   &lt;span class="err"&gt;←&lt;/span&gt; &lt;span class="n"&gt;YOU&lt;/span&gt; &lt;span class="n"&gt;DIDN&lt;/span&gt;&lt;span class="s1"&gt;'T ASK FOR THIS (Hibernate probe)
Query 3: SELECT * FROM user_roles     ← expected (EAGER @ElementCollection)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Query 2 is Hibernate asking: &lt;em&gt;"does a profile exist for this user?"&lt;/em&gt; — because the inverse side has no FK column to check locally.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Solutions to the Inverse-Side Probe Query
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Option 1 — @LazyToOne Bytecode Instrumentation
&lt;/h3&gt;

&lt;p&gt;Forces truly lazy loading via &lt;strong&gt;Hibernate bytecode enhancement&lt;/strong&gt; — Hibernate injects an interceptor into the bytecode so it never needs to probe the DB.&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;// User.java&lt;/span&gt;
&lt;span class="nd"&gt;@OneToOne&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;"user"&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;@LazyToOne&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;LazyToOneOption&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NO_PROXY&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// forces true lazy via bytecode&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="n"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Requires adding the &lt;strong&gt;Hibernate bytecode enhancer&lt;/strong&gt; plugin to your build tool:&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="c"&gt;&amp;lt;!-- Maven --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.hibernate.orm.tooling&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;hibernate-enhance-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;configuration&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;enableLazyInitialization&amp;gt;&lt;/span&gt;true&lt;span class="nt"&gt;&amp;lt;/enableLazyInitialization&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/configuration&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without Enhancement&lt;/th&gt;
&lt;th&gt;With Enhancement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hibernate probes DB to check if profile is null&lt;/td&gt;
&lt;td&gt;Bytecode interceptor handles it — no probe needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra query always fires&lt;/td&gt;
&lt;td&gt;Truly lazy — query only fires on access&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Option 2 — JOIN FETCH Query
&lt;/h3&gt;

&lt;p&gt;Load both entities together in a &lt;strong&gt;single SQL query&lt;/strong&gt; — profile is already in memory, no probe 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;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT u FROM User u LEFT JOIN FETCH u.profile"&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;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithProfile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hibernate generates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;
    &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;username&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;phone&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&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;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Without JOIN FETCH&lt;/th&gt;
&lt;th&gt;With JOIN FETCH&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;SELECT FROM users&lt;/code&gt; + &lt;code&gt;SELECT FROM user_profile&lt;/code&gt; (2 queries)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;SELECT FROM users LEFT JOIN user_profile&lt;/code&gt; (1 query)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Option 3 — @NamedEntityGraph
&lt;/h3&gt;

&lt;p&gt;A &lt;code&gt;@NamedEntityGraph&lt;/code&gt; defines a &lt;strong&gt;pre-declared fetch plan&lt;/strong&gt; that can be reused across multiple repository methods, avoiding the need to write &lt;code&gt;JOIN FETCH&lt;/code&gt; everywhere.&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;@NamedEntityGraph&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;"User.withDetails"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"roles"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"profile"&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;@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;"users"&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="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;Use it in the repository:&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;UserRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&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;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT u FROM User u LEFT JOIN FETCH u.profile"&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;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAllWithProfile&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityGraph&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User.withDetails"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// applies the named graph&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;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;findAll&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;Combining with &lt;code&gt;@NamedSubgraph&lt;/code&gt; for nested associations:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you need to fetch deeply nested associations (e.g., &lt;code&gt;User → UserProfile → Address&lt;/code&gt;), use &lt;code&gt;@NamedSubgraph&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;@Entity&lt;/span&gt;
&lt;span class="nd"&gt;@NamedEntityGraph&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;"User.profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedAttributeNode&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="s"&gt;"profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subgraph&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"profile-subgraph"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;subgraphs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;@NamedSubgraph&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;"profile-subgraph"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;attributeNodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;@NamedAttributeNode&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"address"&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="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="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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProfile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Id&lt;/span&gt;
    &lt;span class="kd"&gt;private&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;@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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Address&lt;/span&gt; &lt;span class="n"&gt;address&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;@Repository&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;UserRepository&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;JpaRepository&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;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@EntityGraph&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="s"&gt;"User.profile"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;EntityGraph&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;EntityGraphType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FETCH&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="nf"&gt;findById&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the entity graph, Hibernate generates a &lt;strong&gt;single query&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;user_profile&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;       &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;address_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to use &lt;code&gt;@EntityGraph&lt;/code&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want &lt;strong&gt;dynamic fetch strategies&lt;/strong&gt; per query&lt;/li&gt;
&lt;li&gt;You want associations to be &lt;strong&gt;LAZY by default&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You want to avoid scattering &lt;code&gt;JOIN FETCH&lt;/code&gt; across all queries&lt;/li&gt;
&lt;li&gt;Different repository methods need &lt;strong&gt;different fetch plans&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Option 4 — @MapsId Shared Primary Key (Best &amp;amp; Simplest)
&lt;/h3&gt;

&lt;p&gt;Since both entities &lt;strong&gt;share the same PK&lt;/strong&gt;, Hibernate already knows the profile ID without probing. This is the cleanest solution for a strict &lt;code&gt;@OneToOne&lt;/code&gt; relationship.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;See Section 9 — Deep Dive: @MapsId for full details.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  9. Deep Dive — @MapsId
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;best way to map a &lt;code&gt;@OneToOne&lt;/code&gt; relationship&lt;/strong&gt; in Hibernate is using &lt;strong&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt;, which allows the child entity to &lt;strong&gt;share the same primary key as the parent entity&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this approach:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;child table uses the parent's PK as its own PK&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;This avoids creating an additional FK column&lt;/li&gt;
&lt;li&gt;Hibernate already knows the child's ID — &lt;strong&gt;no probe query needed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;users                  user_profile
─────────────          ──────────────────────
id  (PK)        ═══    id  (PK = FK) ← shared PK, no separate user_id column
username               phone
password               address
&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;"user_profile"&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;UserProfile&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Id&lt;/span&gt;                          &lt;span class="c1"&gt;// No @GeneratedValue — value is copied from User&lt;/span&gt;
    &lt;span class="kd"&gt;private&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;phone&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="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@OneToOne&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;@MapsId&lt;/span&gt;                      &lt;span class="c1"&gt;// this entity's PK = users.id&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;"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;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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; column in &lt;code&gt;user_profile&lt;/code&gt; serves as &lt;strong&gt;both PK and FK&lt;/strong&gt; — its value is copied directly from &lt;code&gt;User.id&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Key Differences Per Entity
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;User&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;UserProfile&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;@GeneratedValue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅ Yes — generates own PK&lt;/td&gt;
&lt;td&gt;❌ No — copies from &lt;code&gt;User&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Owns the relationship&lt;/td&gt;
&lt;td&gt;No — no &lt;code&gt;profile&lt;/code&gt; field needed&lt;/td&gt;
&lt;td&gt;Yes — has &lt;code&gt;@MapsId&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Probe query on fetch&lt;/td&gt;
&lt;td&gt;Gone — no inverse field&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save strategy&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userRepository.save(user)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;userProfileRepository.save(profile)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Advantages of @MapsId
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No extra FK column&lt;/strong&gt; — cleaner schema, better normalization&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No probe query&lt;/strong&gt; — Hibernate already knows the child's ID&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Efficient joins&lt;/strong&gt; — PK = FK means the join is on the same column&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No need for bidirectional association&lt;/strong&gt; — &lt;code&gt;User&lt;/code&gt; can be unidirectional&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UserProfile&lt;/code&gt; can always be fetched directly using the &lt;code&gt;User&lt;/code&gt; ID&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Downsides of @MapsId
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Tight coupling&lt;/strong&gt; — &lt;code&gt;UserProfile&lt;/code&gt; cannot exist without &lt;code&gt;User&lt;/code&gt; (shares PK)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Insert order dependency&lt;/strong&gt; — &lt;code&gt;User&lt;/code&gt; must be persisted before &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Less flexibility&lt;/strong&gt; — migrating from one-to-one → one-to-many requires schema redesign&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder independent lifecycle management&lt;/strong&gt; — deleting &lt;code&gt;User&lt;/code&gt; invalidates &lt;code&gt;UserProfile&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not suitable for optional relationships&lt;/strong&gt; — if a &lt;code&gt;User&lt;/code&gt; can exist without a &lt;code&gt;UserProfile&lt;/code&gt;, this gets awkward&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Harder for beginners&lt;/strong&gt; — shared PK, &lt;code&gt;@MapsId&lt;/code&gt;, and entity lifecycle can be confusing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Schema migration complexity&lt;/strong&gt; — adding a separate PK to the child table later requires a full refactor&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  When @MapsId Is Ideal
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;@MapsId&lt;/code&gt; when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The relationship is &lt;strong&gt;strictly one-to-one&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The child &lt;strong&gt;cannot exist without the parent&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The child is more like an &lt;strong&gt;extension of the parent&lt;/strong&gt; (e.g., &lt;code&gt;UserProfile&lt;/code&gt; is just extra columns for &lt;code&gt;User&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;You want &lt;strong&gt;zero extra queries&lt;/strong&gt; and a &lt;strong&gt;cleaner schema&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  10. Comparison — @JoinColumn vs @MapsId
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;
&lt;code&gt;@JoinColumn&lt;/code&gt; on child&lt;/th&gt;
&lt;th&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Profile FK column&lt;/td&gt;
&lt;td&gt;&lt;code&gt;user_profile.user_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;user_profile.id&lt;/code&gt; = &lt;code&gt;users.id&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hibernate knows child ID?&lt;/td&gt;
&lt;td&gt;Must probe DB&lt;/td&gt;
&lt;td&gt;Already has it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extra query on &lt;code&gt;getUser()&lt;/code&gt;?&lt;/td&gt;
&lt;td&gt;Always fires (inverse side)&lt;/td&gt;
&lt;td&gt;Only when accessed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DB schema&lt;/td&gt;
&lt;td&gt;Extra &lt;code&gt;user_id&lt;/code&gt; column&lt;/td&gt;
&lt;td&gt;Shared PK — cleaner&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optional relationship?&lt;/td&gt;
&lt;td&gt;✅ Yes&lt;/td&gt;
&lt;td&gt;❌ Not ideal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Child exists independently?&lt;/td&gt;
&lt;td&gt;✅ Can&lt;/td&gt;
&lt;td&gt;❌ Cannot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema simplicity&lt;/td&gt;
&lt;td&gt;Slightly more columns&lt;/td&gt;
&lt;td&gt;Minimal columns&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  11. Quick Reference Table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Topic&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Owning side&lt;/td&gt;
&lt;td&gt;Entity holding the FK (&lt;code&gt;@JoinColumn&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Always the child&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inverse side&lt;/td&gt;
&lt;td&gt;Entity with &lt;code&gt;mappedBy&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No FK — navigation only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToOne&lt;/code&gt; fetch (owning)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;FetchType.LAZY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;FK in same row — proxy safe&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;@OneToOne&lt;/code&gt; fetch (inverse)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;FetchType.LAZY&lt;/code&gt; declared, but won't be&lt;/td&gt;
&lt;td&gt;Hibernate probes regardless&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Probe query fix&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; or &lt;code&gt;JOIN FETCH&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; is cleanest for strict 1:1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 prevention&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;JOIN FETCH&lt;/code&gt; or &lt;code&gt;@EntityGraph&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@NamedSubgraph&lt;/code&gt; for nested graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cascade&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;CascadeType.ALL&lt;/code&gt; on parent only&lt;/td&gt;
&lt;td&gt;Never cascade from child to parent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;orphanRemoval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;true&lt;/code&gt; for owned children&lt;/td&gt;
&lt;td&gt;Handles disassociation too&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;equals&lt;/code&gt;/&lt;code&gt;hashCode&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Based on business key&lt;/td&gt;
&lt;td&gt;Never based on database ID&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Collection for roles&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Set&amp;lt;Role&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Safer than &lt;code&gt;List&lt;/code&gt; with multiple joins&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best overall mapping&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@MapsId&lt;/code&gt; for strict 1:1&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@JoinColumn&lt;/code&gt; for optional or flexible&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




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

&lt;ul&gt;
&lt;li&gt;In a &lt;code&gt;@OneToOne&lt;/code&gt; relationship, the &lt;strong&gt;child holds the FK&lt;/strong&gt; (&lt;code&gt;@JoinColumn&lt;/code&gt;); the &lt;strong&gt;parent uses &lt;code&gt;mappedBy&lt;/code&gt;&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Declaring &lt;code&gt;FetchType.LAZY&lt;/code&gt; on the &lt;strong&gt;inverse side&lt;/strong&gt; does &lt;strong&gt;not&lt;/strong&gt; make it truly lazy — Hibernate fires a probe query anyway because there is no FK column to inspect locally.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four ways to fix the probe query:&lt;/strong&gt; bytecode instrumentation (&lt;code&gt;@LazyToOne&lt;/code&gt;), &lt;code&gt;JOIN FETCH&lt;/code&gt;, &lt;code&gt;@NamedEntityGraph&lt;/code&gt;, or &lt;code&gt;@MapsId&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt; is the cleanest solution for strict one-to-one relationships — the child shares the parent's PK, eliminating the extra FK column and the probe query entirely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bidirectional = convenience&lt;/strong&gt; (&lt;code&gt;user.getProfile()&lt;/code&gt;) but costs a probe query. &lt;strong&gt;Unidirectional = no probe&lt;/strong&gt; but you lose navigation from the &lt;code&gt;User&lt;/code&gt; side.&lt;/li&gt;
&lt;li&gt;For most applications — skip bytecode plugins, go &lt;strong&gt;unidirectional with &lt;code&gt;@MapsId&lt;/code&gt;&lt;/strong&gt;, and use &lt;code&gt;JOIN FETCH&lt;/code&gt; when you need both entities at once.&lt;/li&gt;
&lt;li&gt;Always use &lt;strong&gt;&lt;code&gt;CascadeType.ALL&lt;/code&gt;&lt;/strong&gt; on the parent side only — never cascade upward from child to parent.&lt;/li&gt;
&lt;li&gt;Base &lt;code&gt;equals()&lt;/code&gt;/&lt;code&gt;hashCode()&lt;/code&gt; on a &lt;strong&gt;stable business key&lt;/strong&gt; (e.g., &lt;code&gt;username&lt;/code&gt;), never on the auto-generated database ID.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>hibernate</category>
      <category>jpa</category>
      <category>springboot</category>
      <category>database</category>
    </item>
    <item>
      <title>REST API Design: A Comprehensive Guide</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sat, 07 Mar 2026 09:54:56 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/rest-api-design-3am4</link>
      <guid>https://dev.to/ankitdevcode/rest-api-design-3am4</guid>
      <description>&lt;h2&gt;
  
  
  Understanding REST Fundamentals
&lt;/h2&gt;

&lt;p&gt;REST works on top of the HTTP protocol, where each URI represents a resource. Because of this, endpoints should use &lt;strong&gt;nouns, not verbs&lt;/strong&gt;. An RPC-style endpoint like &lt;code&gt;/api/v1/getStudents&lt;/code&gt; becomes simply &lt;code&gt;/api/v1/students&lt;/code&gt; in REST. The distinction between actions is handled by HTTP methods — &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;POST&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, &lt;code&gt;PATCH&lt;/code&gt;, and &lt;code&gt;DELETE&lt;/code&gt; — rather than the URL itself.&lt;/p&gt;

&lt;p&gt;A REST endpoint is a unique URI that represents a resource. For example, &lt;code&gt;https://demo.app/api/v1/students&lt;/code&gt; is a REST endpoint, where &lt;code&gt;/api/v1/students&lt;/code&gt; is the path and &lt;code&gt;students&lt;/code&gt; is the resource.&lt;/p&gt;

&lt;p&gt;REST does not maintain server-side state — it only transfers state between server and client, which is the origin of the name &lt;em&gt;REpresentational State Transfer&lt;/em&gt;. REST also leverages HTTP cache control, making responses cacheable because every representation is self-descriptive.&lt;/p&gt;

&lt;p&gt;REST operates using three key components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Resources and URIs&lt;/strong&gt; — the nouns that identify what you're working with&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP methods&lt;/strong&gt; — the verbs that define the action&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HATEOAS&lt;/strong&gt; — hypermedia links that guide clients dynamically&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  HTTP Methods
&lt;/h2&gt;

&lt;p&gt;The five primary HTTP methods map to standard CRUD operations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Create (or search)&lt;/td&gt;
&lt;td&gt;Use for search when filter params exceed GET limits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Read&lt;/td&gt;
&lt;td&gt;Should be side-effect free&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PUT&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full update / replace&lt;/td&gt;
&lt;td&gt;Replaces the entire resource&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;PATCH&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Partial update&lt;/td&gt;
&lt;td&gt;Updates only the provided fields&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;DELETE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;Should be idempotent&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Some organisations also expose the &lt;code&gt;HEAD&lt;/code&gt; method to retrieve only response headers without a body — useful for checking resource existence or metadata. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;--head&lt;/span&gt; https://api.github.com/users
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; REST does not strictly mandate which method maps to which operation, but the conventions above are widely adopted across the industry.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  POST
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;POST&lt;/code&gt; is normally used for create operations. There are two notable exceptions where &lt;code&gt;POST&lt;/code&gt; is acceptable for reads:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Long filter criteria&lt;/strong&gt; — &lt;code&gt;GET&lt;/code&gt; query strings are limited to around 2,048 characters. When filter parameters exceed this, &lt;code&gt;POST&lt;/code&gt; is a reasonable alternative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sensitive parameters&lt;/strong&gt; — if input parameters contain private data, &lt;code&gt;POST&lt;/code&gt; with HTTPS keeps them out of the URL and server logs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A successful create operation should return &lt;code&gt;201 Created&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  GET
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;GET&lt;/code&gt; is used for read operations. It should never modify server state. Successful responses return &lt;code&gt;200 OK&lt;/code&gt; when data is present, or &lt;code&gt;204 No Content&lt;/code&gt; when there is nothing to return.&lt;/p&gt;
&lt;h3&gt;
  
  
  PUT
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PUT&lt;/code&gt; is used for full update or replace operations. It replaces the entire resource with the provided representation. Successful responses return &lt;code&gt;200 OK&lt;/code&gt; with data or &lt;code&gt;204 No Content&lt;/code&gt; without.&lt;/p&gt;
&lt;h3&gt;
  
  
  DELETE
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;DELETE&lt;/code&gt; removes a resource. For example, &lt;code&gt;DELETE /licenses/agpl-3.0&lt;/code&gt; deletes the resource identified by the &lt;code&gt;agpl-3.0&lt;/code&gt; key. A successful delete returns &lt;code&gt;204 No Content&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  PATCH
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;PATCH&lt;/code&gt; is used for partial updates — only the supplied fields are changed. A successful response returns &lt;code&gt;200 OK&lt;/code&gt;. Unlike &lt;code&gt;PUT&lt;/code&gt;, you do not need to send the full resource representation.&lt;/p&gt;


&lt;h2&gt;
  
  
  HTTP Status Codes
&lt;/h2&gt;

&lt;p&gt;Status codes fall into five categories:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Range&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;100–199&lt;/td&gt;
&lt;td&gt;Informational&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;200–299&lt;/td&gt;
&lt;td&gt;Success&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;300–399&lt;/td&gt;
&lt;td&gt;Redirection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;400–499&lt;/td&gt;
&lt;td&gt;Client errors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;500–599&lt;/td&gt;
&lt;td&gt;Server errors&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Commonly Used Codes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2xx — Success&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;200 OK&lt;/code&gt; — Request succeeded. Used for &lt;code&gt;GET&lt;/code&gt;, &lt;code&gt;PUT&lt;/code&gt;, and &lt;code&gt;PATCH&lt;/code&gt; responses with a body.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;201 Created&lt;/code&gt; — Resource was successfully created.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;202 Accepted&lt;/code&gt; — Request received but processing is deferred (e.g., async or batch jobs).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;204 No Content&lt;/code&gt; — Request succeeded with no response body.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3xx — Redirection&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;304 Not Modified&lt;/code&gt; — Resource hasn't changed; client should use its cached copy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4xx — Client Errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;400 Bad Request&lt;/code&gt; — Missing, malformed, or invalid input parameters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;401 Unauthorized&lt;/code&gt; — Request is unauthenticated (despite the misleading name).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;403 Forbidden&lt;/code&gt; — Authenticated but not authorised to perform the action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;404 Not Found&lt;/code&gt; — The requested resource does not exist.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;405 Method Not Allowed&lt;/code&gt; — The HTTP method is not supported for this endpoint.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;409 Conflict&lt;/code&gt; — Duplicate create or a state conflict (e.g., version mismatch).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;429 Too Many Requests&lt;/code&gt; — Rate limit has been exceeded.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;5xx — Server Errors&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;500 Internal Server Error&lt;/code&gt; — A generic, unexpected server-side failure.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;502 Bad Gateway&lt;/code&gt; — An upstream dependency (e.g., a payment provider) returned an error.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;503 Service Unavailable&lt;/code&gt; — The server is temporarily unable to handle requests (overload or maintenance).&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  HATEOAS
&lt;/h2&gt;

&lt;p&gt;HATEOAS (Hypermedia as the Engine of Application State) means that a REST API dynamically provides links to related actions and resources within its responses — rather than requiring clients to construct URLs themselves.&lt;/p&gt;

&lt;p&gt;A client starts from a single known URL and discovers everything else through the hypermedia links in each response. This has two key benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clients don't need hardcoded knowledge of the API's URL structure.&lt;/li&gt;
&lt;li&gt;When endpoint paths change, clients pick up the new URLs automatically through the links.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Best Practices
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Use Nouns for Resource Names
&lt;/h3&gt;

&lt;p&gt;HTTP methods already supply the verb. Adding a verb to the URL is redundant and produces RPC-style paths:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;# Wrong (RPC style)
GET /getLicenses
POST /createUser

# Correct (REST style)
GET /licenses
POST /users
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Use Plural Names for Collections
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/licenses   ✓
/license    ✗
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;A &lt;code&gt;GET /licenses&lt;/code&gt; call returns a collection. Plural naming makes the intent clear and consistent regardless of whether you're fetching one or many.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. Version Your APIs
&lt;/h3&gt;

&lt;p&gt;APIs evolve over time, and existing clients may depend on older behaviour. Always include a version identifier so you can introduce breaking changes without affecting existing integrations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option A — Version in the path (most common):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://demo.app/api/v1/students
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Clients always know exactly which version they're calling.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option B — Version in the Accept header (GitHub's approach):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Accept: application/vnd.github.v3+json
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Allows a default version when no header is present, but requires clients to update the header when upgrading.&lt;/p&gt;

&lt;p&gt;Either approach works — the important thing is to pick one and apply it consistently from day one.&lt;/p&gt;
&lt;h3&gt;
  
  
  4. Model Nested Resources
&lt;/h3&gt;

&lt;p&gt;When a resource belongs to another, reflect that in the URL hierarchy:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET    /customers/1/addresses       # all addresses for customer 1
GET    /customers/1/addresses/2     # address 2 of customer 1
POST   /customers/1/addresses       # add a new address
PUT    /customers/1/addresses/2     # replace address 2
PATCH  /customers/1/addresses/2     # partially update address 2
DELETE /customers/1/addresses/2     # delete address 2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;For resources that exist independently (e.g., payments in a microservices architecture), a top-level endpoint is often cleaner:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /payments/1    # instead of /orders/1/payments/1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;HATEOAS helps here — the order response can include a payment link rather than the client needing to know the URL structure upfront.&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Secure Your APIs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Always use &lt;strong&gt;HTTPS&lt;/strong&gt; — never expose REST APIs over plain HTTP.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;JWT or OAuth 2.0&lt;/strong&gt; tokens for authentication. REST is stateless, so cookies and server-side sessions are not appropriate.&lt;/li&gt;
&lt;li&gt;Regularly review &lt;a href="https://owasp.org/www-project-api-security/" rel="noopener noreferrer"&gt;OWASP's API Security Top 10&lt;/a&gt; for current threats and mitigations.&lt;/li&gt;
&lt;li&gt;Validate all inputs on the server side regardless of client-side validation.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  6. Implement Caching
&lt;/h3&gt;

&lt;p&gt;HTTP provides two standard mechanisms for cache validation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ETag&lt;/strong&gt; — the server returns a hash of the response body in the &lt;code&gt;ETag&lt;/code&gt; header. The client sends it back as &lt;code&gt;If-None-Match&lt;/code&gt; on subsequent requests. If the resource hasn't changed, the server returns &lt;code&gt;304 Not Modified&lt;/code&gt; and saves the bandwidth of resending the body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Last-Modified&lt;/strong&gt; — the server returns the resource's last modification timestamp. The client sends it back as &lt;code&gt;If-Modified-Since&lt;/code&gt;. The server returns &lt;code&gt;304&lt;/code&gt; if nothing has changed since that timestamp. This is less precise than ETag and should be used as a fallback.&lt;/p&gt;
&lt;h3&gt;
  
  
  7. Enforce Rate Limiting
&lt;/h3&gt;

&lt;p&gt;Use &lt;code&gt;429 Too Many Requests&lt;/code&gt; when a client exceeds its allowed request quota. Communicate rate limit status via response headers so clients can adapt:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Header&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;&lt;code&gt;X-Ratelimit-Limit&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests allowed per period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;60&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Remaining&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests left in current period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;55&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Used&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Requests used in current period&lt;/td&gt;
&lt;td&gt;&lt;code&gt;5&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X-Ratelimit-Reset&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Seconds until the period resets&lt;/td&gt;
&lt;td&gt;&lt;code&gt;1601299930&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  8. Support Filtering, Sorting, and Pagination
&lt;/h3&gt;

&lt;p&gt;For endpoints that return collections, always support query parameters to avoid returning unbounded result sets:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET /products?category=electronics&amp;amp;sort=price       # filter + sort
GET /users?page=1&amp;amp;size=20                           # pagination
GET /orders?status=pending&amp;amp;sort=created_at&amp;amp;page=2  # combined
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  9. Maintain Documentation
&lt;/h3&gt;

&lt;p&gt;Good documentation is part of your API contract. It should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Stay in sync with the current implementation, including version history.&lt;/li&gt;
&lt;li&gt;Include code samples and example request/response pairs.&lt;/li&gt;
&lt;li&gt;Clearly document deprecated endpoints and provide migration paths.&lt;/li&gt;
&lt;li&gt;Be generated from the code where possible (e.g., OpenAPI/Swagger) to reduce drift.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  10. Return Consistent Error Responses
&lt;/h3&gt;

&lt;p&gt;Error responses should follow a predictable structure so clients can handle them programmatically:&lt;br&gt;
&lt;/p&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;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&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;"Bad Request"&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;"The 'email' field is required."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/v1/users"&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;Consistency here dramatically reduces the integration effort for API consumers.&lt;/p&gt;


&lt;h2&gt;
  
  
  Quick Reference
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Concern&lt;/th&gt;
&lt;th&gt;Recommendation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;URL naming&lt;/td&gt;
&lt;td&gt;Nouns, plural, lowercase, hyphen-separated&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HTTP methods&lt;/td&gt;
&lt;td&gt;Match semantics: &lt;code&gt;GET&lt;/code&gt; reads, &lt;code&gt;POST&lt;/code&gt; creates, &lt;code&gt;PUT&lt;/code&gt; replaces, &lt;code&gt;PATCH&lt;/code&gt; partially updates, &lt;code&gt;DELETE&lt;/code&gt; removes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Status codes&lt;/td&gt;
&lt;td&gt;Use the most specific code; avoid overusing &lt;code&gt;200&lt;/code&gt; for everything&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versioning&lt;/td&gt;
&lt;td&gt;Include from day one (&lt;code&gt;/api/v1/&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auth&lt;/td&gt;
&lt;td&gt;JWT or OAuth 2.0 — no cookies or sessions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;Use &lt;code&gt;ETag&lt;/code&gt; or &lt;code&gt;Last-Modified&lt;/code&gt; headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rate limiting&lt;/td&gt;
&lt;td&gt;Return &lt;code&gt;429&lt;/code&gt; with &lt;code&gt;X-Ratelimit-*&lt;/code&gt; headers&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pagination&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;?page=&amp;amp;size=&lt;/code&gt; query params on collection endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;OpenAPI/Swagger, kept current, with examples&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Error format&lt;/td&gt;
&lt;td&gt;Consistent JSON structure across all endpoints&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Reference:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AI tools such as ChatGpt and Claude&lt;/li&gt;
&lt;li&gt;

&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://www.packtpub.com/en-us/product/modern-api-development-with-spring-and-spring-boot-9781800562479" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbookimages.packtpub.com%2Fproduct-images%2FB16561%2Fpage_453.jpg" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://www.packtpub.com/en-us/product/modern-api-development-with-spring-and-spring-boot-9781800562479" rel="noopener noreferrer" class="c-link"&gt;
            Modern API Development with Spring and Spring Boot | Web Development | Paperback
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Design highly scalable and maintainable APIs with REST, gRPC, GraphQL, and the reactive paradigm. 12 customer reviews. Top rated Web Development products.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.packtpub.com%2Ffavicon.ico"&gt;
          packtpub.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;




&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>restapi</category>
      <category>api</category>
      <category>java</category>
      <category>design</category>
    </item>
    <item>
      <title>Java Virtual Threads — Quick Guide</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sun, 01 Feb 2026 12:12:55 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/java-virtual-threads-quick-guide-1jca</link>
      <guid>https://dev.to/ankitdevcode/java-virtual-threads-quick-guide-1jca</guid>
      <description>&lt;h2&gt;
  
  
  Java Virtual Threads — Quick Guide
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Java 21+ · Spring Boot 3.2+ · Project Loom&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A concise, production-focused guide to Java &lt;strong&gt;Virtual Threads&lt;/strong&gt; — what they are, how to enable them, when to use them, and the real-world pitfalls that can silently hurt performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  01 · What Are Virtual Threads
&lt;/h2&gt;

&lt;p&gt;Before Project Loom, there is only one type of threads in Java, which is called platform thread in Project Loom. Platform threads are typically mapped 1:1 to kernel threads scheduled by the operating system. In Project Loom, virtual threads are introduced as a new type of threads.&lt;/p&gt;

&lt;p&gt;Virtual threads are typically user-mode threads scheduled by the Java runtime rather than the operating system. Virtual threads are mapped M:N to kernel threads.&lt;/p&gt;

&lt;p&gt;Platform and virtual threads are both represented using java.lang.Thread&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely lightweight compared to platform (OS) threads.&lt;/li&gt;
&lt;li&gt;Millions of virtual threads can be created safely.&lt;/li&gt;
&lt;li&gt;Allow developers to write simple, blocking-style code while remaining highly scalable.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How to create virtual threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The first approach to create virtual threads is using the &lt;code&gt;Thread.ofVirtual&lt;/code&gt; method.&lt;/p&gt;

&lt;p&gt;In the code below, a new virtual thread is created and started. The return value is an instance of &lt;code&gt;java.lang.Thread&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var thread = Thread.ofVirtual().name("My virtual thread")
    .start(() -&amp;gt; System.out.println("I'm running"))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second approach is using &lt;code&gt;Thread.startVirtualThread(Runnable task)&lt;/code&gt; method. This is the same as calling &lt;code&gt;Thread.ofVirtual().start(task)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The third approach is using &lt;code&gt;ThreadFactory&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var factory = Thread.ofVirtual().factory();
var thread = factory.newThread(() -&amp;gt; System.out.println("Create in factory"));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;How to check if a thread is virtual?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The new &lt;code&gt;isVirtual()&lt;/code&gt; method in &lt;code&gt;java.lang.Thread&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; is this thread is a virtual thread.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads be non-daemon threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Virtual threads are always daemon threads. So they cannot prevent JVM from terminating. Calling &lt;code&gt;setDaemon(false)&lt;/code&gt; on a virtual thread will throw an &lt;code&gt;IllegalArgumentException&lt;/code&gt; exception.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Should virtual threads be pooled?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No. Virtual threads are light-weight. There is no need to pool them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads support thread-local variables?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Virtual threads support both thread-local variables (&lt;code&gt;ThreadLocal&lt;/code&gt;) and inheritable thread-local variables (&lt;code&gt;InheritableThreadLocal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can virtual threads support thread-local variables?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Yes. Virtual threads support both thread-local variables (&lt;code&gt;ThreadLocal&lt;/code&gt;) and inheritable thread-local variables (&lt;code&gt;InheritableThreadLocal&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can &lt;code&gt;ExecutorService&lt;/code&gt; use virtual threads?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An &lt;code&gt;ExecutorService&lt;/code&gt; can start a virtual thread for each task. This kind of &lt;code&gt;ExecutorService&lt;/code&gt;s can be created using &lt;code&gt;Executors.newVirtualThreadPerTaskExecutor()&lt;/code&gt; or &lt;code&gt;Executors.newThreadPerTaskExecutor(ThreadFactory threadFactory)&lt;/code&gt; methods. The number of virtual threads created by the &lt;code&gt;Executor&lt;/code&gt; is unbounded.&lt;/p&gt;

&lt;p&gt;In the code below, a new &lt;code&gt;ExecutorService&lt;/code&gt; is created to use virtual threads. 10000 tasks are submitted to this &lt;code&gt;ExecutorService&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
  IntStream.range(0, 10_00).forEach(i -&amp;gt; executor.submit(() -&amp;gt; {
    Thread.sleep(Duration.ofSeconds(1));
    return i;
  }));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Blocking comparison
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;OS Thread blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;RestTemplate&lt;/code&gt; blocks an OS thread&lt;/li&gt;
&lt;li&gt;Thread is idle during I/O&lt;/li&gt;
&lt;li&gt;Under load → thread exhaustion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Virtual Thread blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JVM suspends the virtual thread&lt;/li&gt;
&lt;li&gt;Carrier thread is released immediately&lt;/li&gt;
&lt;li&gt;Scales safely under high concurrency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah"&gt;Why we need virtual threads?&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Enable in Spring Boot
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;One property. No code changes.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# application.yml&lt;/span&gt;
&lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;servlet&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;threads&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;virtual-threads-enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Java 21+&lt;/strong&gt; (final, not preview)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spring Boot 3.2+&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What changes
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each HTTP request runs on a fresh virtual thread&lt;/li&gt;
&lt;li&gt;Controllers, services, &lt;code&gt;RestTemplate&lt;/code&gt; → unchanged&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What &lt;code&gt;virtual-threads-enabled: true&lt;/code&gt; Actually Does in Spring
&lt;/h2&gt;

&lt;p&gt;It replaces Tomcat’s entire servlet thread pool with a &lt;strong&gt;virtual-thread-per-request executor&lt;/strong&gt;. Every incoming HTTP request is immediately assigned a new virtual thread. There is no fixed pool size — Tomcat doesn’t cap anything. The JVM manages it all.&lt;/p&gt;

&lt;p&gt;This means your entire request lifecycle — from the moment the request hits the &lt;code&gt;DispatcherServlet&lt;/code&gt; to the moment the response is written — runs on a virtual thread. Every blocking call inside that chain (&lt;code&gt;RestTemplate&lt;/code&gt;, JDBC, &lt;code&gt;Thread.sleep()&lt;/code&gt;) is automatically cheap because it’s already on a virtual thread.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Manual Offload Approach
&lt;/h2&gt;

&lt;p&gt;Tomcat’s default OS thread pool handles accept and dispatch, then the work is explicitly handed off to a virtual thread executor. For example, &lt;code&gt;CompletableFuture.supplyAsync()&lt;/code&gt; offloads work to the virtual thread executor. The OS thread that accepted the request is released immediately.&lt;/p&gt;

&lt;p&gt;Spring MVC knows how to handle a returned &lt;code&gt;CompletableFuture&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It suspends servlet processing&lt;/li&gt;
&lt;li&gt;It resumes when the future completes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The key point: Spring MVC &lt;strong&gt;does not block&lt;/strong&gt; the servlet thread waiting for the future. It registers a callback internally and frees the thread immediately.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Service and Client Layers Remain Unchanged&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The service and client layers stay completely unchanged in both approaches.&lt;/p&gt;




&lt;h2&gt;
  
  
  Virtual Threads Adoption Strategy in Spring Boot
&lt;/h2&gt;

&lt;h2&gt;
  
  
  Context
&lt;/h2&gt;

&lt;p&gt;The existing Spring Boot microservice handles incoming HTTP requests using Spring MVC (Servlet stack) and communicates with multiple downstream services using blocking clients such as &lt;code&gt;RestTemplate&lt;/code&gt; and JDBC.&lt;/p&gt;

&lt;p&gt;Key constraints and characteristics:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The current implementation &lt;strong&gt;cannot be changed or rewritten&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The codebase contains:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;synchronized&lt;/code&gt; blocks and methods&lt;/li&gt;
&lt;li&gt;Heavy reliance on &lt;code&gt;ThreadLocal&lt;/code&gt; (SecurityContext, MDC, request attributes)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;The service performs &lt;strong&gt;I/O-heavy aggregation&lt;/strong&gt; across multiple downstream services.&lt;/li&gt;

&lt;li&gt;Scalability issues arise due to thread blocking under load.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;The goal is to improve concurrency and scalability using &lt;strong&gt;Java Virtual Threads&lt;/strong&gt;, without breaking existing behavior or introducing subtle runtime risks.&lt;/p&gt;

&lt;p&gt;Two approaches are available in Spring Boot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Property-based virtual threads (&lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Manual offloading to a virtual-thread executor using &lt;code&gt;CompletableFuture&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Option 1: Property-Based Virtual Threads (Global Enablement)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;Enabling:&lt;/p&gt;

&lt;p&gt;(&lt;code&gt;spring.threads.virtual.enabled=true&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;replaces Tomcat's servlet thread pool with a &lt;strong&gt;virtual-thread-per-request&lt;/strong&gt; executor.&lt;/p&gt;

&lt;p&gt;Each HTTP request:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is assigned a fresh virtual thread&lt;/li&gt;
&lt;li&gt;Runs entirely on that virtual thread (filters → controllers → services → response)&lt;/li&gt;
&lt;li&gt;Executes blocking calls cheaply (RestTemplate, JDBC, Thread.sleep)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Zero code changes&lt;/li&gt;
&lt;li&gt;Uniform behavior across the entire application&lt;/li&gt;
&lt;li&gt;Automatic scalability for blocking I/O&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Risks and Limitations
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Pinning Risk
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;synchronized&lt;/code&gt; blocks &lt;strong&gt;pin carrier threads&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Pinning is invisible and global&lt;/li&gt;
&lt;li&gt;Concurrent access can exhaust the small carrier thread pool
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Virtual Thread]

↓

synchronized block  ← carrier pinned

↓

blocking I/O
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If N concurrent requests enter pinned sections, N carrier threads are required. With only ~CPU-count carriers available, the application can stall.&lt;/p&gt;

&lt;h3&gt;
  
  
  ThreadLocal Assumptions Break
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are short-lived&lt;/li&gt;
&lt;li&gt;No thread reuse across requests&lt;/li&gt;
&lt;li&gt;ThreadLocal data does not persist beyond a single request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This breaks assumptions made by existing code that was written for pooled OS threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ThreadLocal Abuse:&lt;/strong&gt; If your project stores massive objects in ThreadLocal, you might run into memory issues because you could suddenly have 100,000 threads instead of 200.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lack of Control
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No isolation boundary&lt;/li&gt;
&lt;li&gt;No way to selectively exclude endpoints or code paths&lt;/li&gt;
&lt;li&gt;Fixing issues requires rewriting synchronized and ThreadLocal-dependent code&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Option 2: Manual Offload to Virtual Threads (Selective Adoption)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Tomcat continues to use its default &lt;strong&gt;OS-thread servlet pool&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Existing code runs unchanged on OS threads&lt;/li&gt;
&lt;li&gt;I/O-heavy logic is explicitly offloaded using:&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CompletableFuture.supplyAsync(task, virtualThreadExecutor)&lt;/p&gt;

&lt;p&gt;Spring MVC:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Natively supports &lt;code&gt;CompletableFuture&lt;/code&gt; return types&lt;/li&gt;
&lt;li&gt;Suspends request processing&lt;/li&gt;
&lt;li&gt;Releases the servlet thread immediately&lt;/li&gt;
&lt;li&gt;Resumes when the future completes&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  ThreadLocal Considerations
&lt;/h3&gt;

&lt;p&gt;Offloading creates a &lt;strong&gt;hard thread boundary&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Context such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SecurityContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;MDC tracing data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;is &lt;strong&gt;not propagated automatically&lt;/strong&gt; and must be captured and restored manually.&lt;/p&gt;

&lt;p&gt;This boundary is explicit and controlled.&lt;/p&gt;




&lt;h3&gt;
  
  
  Benefits
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Preserves existing assumptions (&lt;code&gt;synchronized&lt;/code&gt;, ThreadLocal)&lt;/li&gt;
&lt;li&gt;Avoids carrier-thread pinning in legacy code&lt;/li&gt;
&lt;li&gt;Allows targeted use of virtual threads only where beneficial&lt;/li&gt;
&lt;li&gt;Enables incremental migration&lt;/li&gt;
&lt;li&gt;Clear isolation between OS-thread and virtual-thread execution&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Trade-offs
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Slightly more boilerplate&lt;/li&gt;
&lt;li&gt;Requires explicit context propagation&lt;/li&gt;
&lt;li&gt;Virtual thread usage must be consciously applied&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Consequences
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Positive
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Improved scalability for I/O-heavy endpoints&lt;/li&gt;
&lt;li&gt;No need to refactor existing synchronized code&lt;/li&gt;
&lt;li&gt;Predictable runtime behavior&lt;/li&gt;
&lt;li&gt;Clear migration path&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Negative
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Additional boilerplate for context propagation&lt;/li&gt;
&lt;li&gt;Requires discipline to maintain offload boundaries&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;The property-based virtual thread approach is suitable only for codebases that are already virtual-thread-friendly.&lt;/p&gt;

&lt;p&gt;For this system, &lt;strong&gt;manual offloading is the safest and most effective strategy&lt;/strong&gt;, delivering the benefits of virtual threads while preserving correctness and operational stability.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pitfalls (Read Before Production)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;synchronized&lt;/code&gt; pins carrier threads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual thread becomes glued to the carrier&lt;/li&gt;
&lt;li&gt;9 concurrent requests + 8 carriers → deadlock&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: use &lt;code&gt;ReentrantLock&lt;/code&gt;&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="c1"&gt;// Pins carrier&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;synchronized&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;id&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;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/p/{id}"&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;class&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="o"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Safe&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;ReentrantLock&lt;/span&gt; &lt;span class="n"&gt;lock&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;ReentrantLock&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;Product&lt;/span&gt; &lt;span class="nf"&gt;fetch&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;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;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lock&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/p/{id}"&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;class&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="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unlock&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;
  
  
  ThreadLocal context loss during manual offload
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What breaks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;MDC&lt;/li&gt;
&lt;li&gt;SecurityContext&lt;/li&gt;
&lt;li&gt;RequestAttributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: capture &amp;amp; restore context&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="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;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;mdc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getCopyOfContextMap&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;SecurityContext&lt;/span&gt; &lt;span class="n"&gt;sec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getContext&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;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;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;mdc&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;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContextMap&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mdc&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setContext&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sec&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="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;MDC&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clear&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
        &lt;span class="nc"&gt;SecurityContextHolder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;clearContext&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="n"&gt;ioExecutor&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 This issue &lt;strong&gt;does not exist&lt;/strong&gt; when using &lt;code&gt;virtual-threads-enabled: true&lt;/code&gt; globally.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h3&gt;
  
  
  ThreadLocal leaks with pooled executors
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Someone replaces the executor with a fixed pool&lt;/li&gt;
&lt;li&gt;ThreadLocals leak across requests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fix: enforce correct executor&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;@Bean&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;Executor&lt;/span&gt; &lt;span class="nf"&gt;ioExecutor&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;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&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;
  
  
  Native (JNI) calls pin silently
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Some JDBC drivers&lt;/li&gt;
&lt;li&gt;Crypto libraries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Contain the damage&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;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;ExecutorService&lt;/span&gt; &lt;span class="no"&gt;NATIVE_POOL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newFixedThreadPool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;callNative&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;input&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="no"&gt;NATIVE_POOL&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;submit&lt;/span&gt;&lt;span class="o"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;nativeLib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;process&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&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;Enable pin logging (dev only):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-XX:+PrintVirtualThreadPinning
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  MVC + WebFlux together
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;If both starters are present, Spring chooses &lt;strong&gt;MVC&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;No warning is shown&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Rule&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual Threads → keep &lt;code&gt;spring-boot-starter-web&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Remove &lt;code&gt;starter-webflux&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  CPU-bound work on virtual threads
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Anti-pattern&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Heavy computation&lt;/li&gt;
&lt;li&gt;Image processing&lt;/li&gt;
&lt;li&gt;Crypto loops&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Correct split&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="c1"&gt;// I/O work&lt;/span&gt;
&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;restTemplate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getForObject&lt;/span&gt;&lt;span class="o"&gt;(...),&lt;/span&gt;
    &lt;span class="nc"&gt;Executors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;newVirtualThreadPerTaskExecutor&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// CPU work&lt;/span&gt;
&lt;span class="nc"&gt;CompletableFuture&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;supplyAsync&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;heavyComputation&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
    &lt;span class="nc"&gt;ForkJoinPool&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;commonPool&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Final Takeaway
&lt;/h2&gt;

&lt;p&gt;Virtual Threads are the best choice for blocking, I/O-heavy Spring Boot services you cannot rewrite.&lt;/p&gt;

&lt;p&gt;They give you scalability, simplicity, and production safety — without reactive complexity.&lt;/p&gt;

</description>
      <category>virtualthread</category>
      <category>java21</category>
      <category>concurrency</category>
      <category>programming</category>
    </item>
    <item>
      <title>Concurrency and Asynchronous Programming Part-1</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Fri, 30 Jan 2026 17:24:29 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah</link>
      <guid>https://dev.to/ankitdevcode/concurrency-and-asynchronous-programming-part-1-2jah</guid>
      <description>&lt;h2&gt;
  
  
  What is Parallel Programming?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tasks &lt;strong&gt;run at the same time&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Progress happens simultaneously at any given instant.&lt;/li&gt;
&lt;li&gt;Requires sufficient hardware resources (e.g., multiple CPU cores).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Walking and talking:

&lt;ul&gt;
&lt;li&gt;Both actions occur at the same time.&lt;/li&gt;
&lt;li&gt;At every moment, both are progressing.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is Concurrent Programming?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Tasks &lt;strong&gt;make progress over time&lt;/strong&gt;, but not at the same instant.&lt;/li&gt;
&lt;li&gt;The system switches between tasks.&lt;/li&gt;
&lt;li&gt;Over a time interval, all tasks move forward.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Talking and drinking:

&lt;ul&gt;
&lt;li&gt;You alternate between the two.&lt;/li&gt;
&lt;li&gt;At any moment, you’re doing one or the other, not both.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  What “Asynchronous” Really Means?
&lt;/h2&gt;

&lt;p&gt;Tasks &lt;strong&gt;don’t block&lt;/strong&gt; while waiting for something (I/O, timers, network).&lt;/p&gt;

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

&lt;p&gt;Asynchrony is about &lt;strong&gt;waiting efficiently&lt;/strong&gt;.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A task starts work&lt;/li&gt;
&lt;li&gt;It pauses when waiting&lt;/li&gt;
&lt;li&gt;Resumes later via callbacks, promises&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Common Misconception
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Asynchronous ≠ no waiting&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Tasks still wait for data.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Real Question
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the thread block while waiting?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking threads = poor scalability.&lt;/li&gt;
&lt;li&gt;Free threads = better resource utilization.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The twist: they’re not opposites
&lt;/h2&gt;

&lt;p&gt;You can have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent + synchronous&lt;/strong&gt; (multiple threads blocking)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Single-threaded + asynchronous&lt;/strong&gt; (Node.js style)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Concurrent + asynchronous&lt;/strong&gt; (modern web servers)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel&lt;/strong&gt; (true simultaneous execution on multiple cores)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Parallelism&lt;/strong&gt; = literally running at the same time&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Concurrency&lt;/strong&gt; = dealing with multiple things&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Asynchrony&lt;/strong&gt; = not blocking while waiting&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real-world analogy&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concurrency:&lt;/strong&gt; A chef cooking 3 dishes at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asynchronous:&lt;/strong&gt; Putting something in the oven and doing other prep instead of staring at it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Parallel:&lt;/strong&gt; Multiple chefs cooking at the same time&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Why threads are expensive?
&lt;/h2&gt;

&lt;p&gt;Threads are considered &lt;strong&gt;expensive&lt;/strong&gt; because each thread consumes &lt;strong&gt;significant system resources&lt;/strong&gt;, even when it’s idle.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Main reasons:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Memory usage (stack space)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Each thread has its own &lt;strong&gt;stack&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Typical stack size:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~1 MB per thread&lt;/strong&gt; (common default on 64-bit systems)&lt;/li&gt;
&lt;li&gt;Can range from &lt;strong&gt;256 KB to several MB&lt;/strong&gt;, depending on OS and JVM/runtime configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1,000 threads ≈ &lt;strong&gt;~1 GB of memory&lt;/strong&gt; just for stacks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context switching overhead&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The CPU must save and restore thread state when switching&lt;/li&gt;
&lt;li&gt;Frequent context switches reduce CPU efficiency&lt;/li&gt;
&lt;li&gt;Becomes costly at high thread counts&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling overhead&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The OS scheduler must manage runnable threads&lt;/li&gt;
&lt;li&gt;Large numbers of threads increase scheduling complexity and latency&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Synchronization costs&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Threads often require locks, mutexes, or other coordination mechanisms&lt;/li&gt;
&lt;li&gt;Leads to contention, deadlocks, and performance bottlenecks&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Why Blocking Threads Hurt Scalability
&lt;/h2&gt;

&lt;p&gt;When threads block on I/O:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They sit idle.&lt;/li&gt;
&lt;li&gt;More threads are created to compensate.&lt;/li&gt;
&lt;li&gt;Threads are limited by:

&lt;ul&gt;
&lt;li&gt;CPU cores.&lt;/li&gt;
&lt;li&gt;Memory.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;More machines.&lt;/li&gt;
&lt;li&gt;More architectural complexity.&lt;/li&gt;
&lt;li&gt;Higher costs (and environmental impact).&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Evolution of Java Multithreading
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Java 5 – &lt;code&gt;ExecutorService&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduced thread pools.&lt;/li&gt;
&lt;li&gt;Solved:

&lt;ul&gt;
&lt;li&gt;Uncontrolled thread creation.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;New problem:

&lt;ul&gt;
&lt;li&gt;Thread-pool–induced deadlocks.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 7 – Fork/Join Framework
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Introduced &lt;strong&gt;work stealing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Reduced pool starvation issues.&lt;/li&gt;
&lt;li&gt;Well-suited for &lt;strong&gt;CPU-bound parallelism&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 8 – Expressive Concurrency
&lt;/h3&gt;

&lt;p&gt;Introduced:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parallel Streams&lt;/li&gt;
&lt;li&gt;&lt;code&gt;CompletableFuture&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Enabled:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More declarative parallelism.&lt;/li&gt;
&lt;li&gt;Better asynchronous composition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Java 20 / 21 – Virtual Threads
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Another major step forward.&lt;/li&gt;
&lt;li&gt;Makes blocking cheap and scalable.&lt;/li&gt;
&lt;li&gt;Simplifies concurrency for many workloads.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What Are Virtual Threads?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are a &lt;strong&gt;lightweight threading model&lt;/strong&gt; introduced with &lt;strong&gt;Project Loom&lt;/strong&gt; in Java.&lt;/li&gt;
&lt;li&gt;They are managed by the &lt;strong&gt;JVM&lt;/strong&gt;, not the operating system.&lt;/li&gt;
&lt;li&gt;Designed to support &lt;strong&gt;massive concurrency&lt;/strong&gt; with minimal resource usage.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Characteristics&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extremely lightweight compared to platform (OS) threads.&lt;/li&gt;
&lt;li&gt;Millions of virtual threads can be created safely.&lt;/li&gt;
&lt;li&gt;Allow developers to write &lt;strong&gt;simple, blocking-style code&lt;/strong&gt; while remaining highly scalable.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Problem Virtual Threads Aim to Solve
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Traditional Java Concurrency Issues
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Thread-per-request model:

&lt;ul&gt;
&lt;li&gt;Threads block during I/O (DB, network, file).&lt;/li&gt;
&lt;li&gt;Blocking threads consume memory and OS resources.&lt;/li&gt;
&lt;li&gt;Scalability is limited by thread count.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;To scale:

&lt;ul&gt;
&lt;li&gt;More threads → more memory.&lt;/li&gt;
&lt;li&gt;More machines → higher cost and complexity.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Rise of Reactive Programming
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reactive frameworks (Reactor, RxJava) emerged to:

&lt;ul&gt;
&lt;li&gt;Avoid blocking OS threads.&lt;/li&gt;
&lt;li&gt;Handle high concurrency using non-blocking I/O.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Downsides:

&lt;ul&gt;
&lt;li&gt;Complex APIs.&lt;/li&gt;
&lt;li&gt;Harder to read, debug, and reason about.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  How Virtual Threads Work?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads are &lt;strong&gt;scheduled by the JVM&lt;/strong&gt;, not the OS.&lt;/li&gt;
&lt;li&gt;When a virtual thread blocks on I/O:

&lt;ul&gt;
&lt;li&gt;It is &lt;strong&gt;unmounted&lt;/strong&gt; from its carrier (platform) thread.&lt;/li&gt;
&lt;li&gt;The carrier thread is reused for other work.&lt;/li&gt;
&lt;li&gt;The virtual thread is &lt;strong&gt;remounted&lt;/strong&gt; once I/O completes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Result:

&lt;ul&gt;
&lt;li&gt;Blocking no longer wastes threads.&lt;/li&gt;
&lt;li&gt;High scalability with familiar programming models.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Virtual Threads vs Reactive Programming
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Shared Goal&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Both aim to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maximize concurrency.&lt;/li&gt;
&lt;li&gt;Avoid wasting threads during I/O.&lt;/li&gt;
&lt;li&gt;Improve scalability of server-side applications.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Differences&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Virtual Threads&lt;/th&gt;
&lt;th&gt;Reactive Programming&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Blocking code is acceptable&lt;/td&gt;
&lt;td&gt;Requires non-blocking code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Simple, imperative style&lt;/td&gt;
&lt;td&gt;Functional, stream-based style&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Easier to read and debug&lt;/td&gt;
&lt;td&gt;Steeper learning curve&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Works with existing APIs&lt;/td&gt;
&lt;td&gt;Requires reactive-compatible APIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Core Argument
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads make &lt;strong&gt;thread-per-task&lt;/strong&gt; scalable again.&lt;/li&gt;
&lt;li&gt;This reduces the need for reactive frameworks in many common cases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Benefits of Virtual Threads
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Simplicity&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Write synchronous, blocking code.&lt;/li&gt;
&lt;li&gt;No need to manage callbacks or reactive pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Blocking no longer ties up OS threads.&lt;/li&gt;
&lt;li&gt;Supports very high concurrency with low overhead.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Works with existing Java libraries and APIs.&lt;/li&gt;
&lt;li&gt;No need to rewrite large codebases.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Does Virtual Threads Kill Reactive Programming?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Short Answer:&lt;/strong&gt; No — But It Shrinks Its Use Cases&lt;/p&gt;

&lt;p&gt;Reactive programming still matters when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-grained event streams are required.&lt;/li&gt;
&lt;li&gt;Backpressure control is critical.&lt;/li&gt;
&lt;li&gt;Complex data-flow transformations are needed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Virtual threads:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Do &lt;strong&gt;not&lt;/strong&gt; automatically provide:

&lt;ul&gt;
&lt;li&gt;Backpressure.&lt;/li&gt;
&lt;li&gt;Stream composition.&lt;/li&gt;
&lt;li&gt;Reactive operators.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;br&gt;
&lt;strong&gt;Why Reactive Programming Existed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To avoid blocking OS threads and improve scalability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What Changed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Virtual threads make blocking cheap and scalable.&lt;/li&gt;
&lt;li&gt;Structured concurrency brings better control and safety.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Practical Impact&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Many server-side workloads can return to:

&lt;ul&gt;
&lt;li&gt;Simple, imperative code.&lt;/li&gt;
&lt;li&gt;Thread-per-request style.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Reactive programming remains relevant for specialized scenarios, but is no longer mandatory for scalability.&lt;/li&gt;

&lt;/ul&gt;

</description>
      <category>asynchronous</category>
      <category>virtualthread</category>
      <category>reactive</category>
      <category>concurrency</category>
    </item>
    <item>
      <title>Advanced Java Data Structures and Their Best Use Cases</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Thu, 11 Sep 2025 17:06:48 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/advanced-java-data-structures-and-their-best-use-cases-hm</link>
      <guid>https://dev.to/ankitdevcode/advanced-java-data-structures-and-their-best-use-cases-hm</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;While Java’s standard collections like List, Set, and Map cover most needs, complex applications often require advanced data structures for efficiency, concurrency, and memory optimization.&lt;/p&gt;

&lt;p&gt;This guide explores key advanced Java data structures, their core operations, complexities, and best use cases to help developers build high-performance, scalable applications.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. PriorityQueue (Heap)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;PriorityQueue&lt;/code&gt; in Java (from &lt;code&gt;java.util&lt;/code&gt;) is a queue that arranges elements according to their priority.&lt;/p&gt;

&lt;p&gt;PriorityQueue in Java is a min-heap by default, implemented as a binary heap using an array. It maintains elements in a partially ordered tree where the smallest element is always at the root.&lt;/p&gt;

&lt;p&gt;A heap is a specialized binary tree-based data structure that satisfies the heap property:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;In a Min-Heap, the parent node is always smaller (or equal) than its children.&lt;/li&gt;
&lt;li&gt;In a Max-Heap, the parent node is always larger (or equal) than its children.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Heaps are typically implemented using arrays (not linked nodes) because the tree is always a complete binary tree (all levels filled except possibly the last, which is filled left to right).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Time Complexity: O(log n) for insertion and deletion, O(1) for peek&lt;/li&gt;
&lt;li&gt;Space Complexity: O(n)&lt;/li&gt;
&lt;li&gt;Not thread-safe&lt;/li&gt;
&lt;li&gt;Unbounded (grows dynamically)&lt;/li&gt;
&lt;li&gt;Natural ordering or custom Comparator&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax for &lt;code&gt;PriorityQueue&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PriorityQueue&amp;lt;Integer&amp;gt; minHeap = new PriorityQueue&amp;lt;&amp;gt;();
PriorityQueue&amp;lt;Integer&amp;gt; maxHeap = new PriorityQueue&amp;lt;&amp;gt;(Collections.reverseOrder());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;add(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inserts element &lt;code&gt;e&lt;/code&gt; into the queue.&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;offer(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Inserts element &lt;code&gt;e&lt;/code&gt; into the queue.&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;poll()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes and returns the top (priority) element.&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;peek()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the top element without removing it.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;remove()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes a specified element from the queue.&lt;/td&gt;
&lt;td&gt;O(n)&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;Returns the number of elements in the queue.&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;clear()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes all elements from the queue.&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;contains(e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Checks if the queue contains element &lt;code&gt;e&lt;/code&gt;.&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;K Largest/Smallest Elements&lt;/li&gt;
&lt;li&gt;Merge K Sorted Lists&lt;/li&gt;
&lt;li&gt;Dijkstra's Shortest Path&lt;/li&gt;
&lt;li&gt;Task Scheduler with Priority&lt;/li&gt;
&lt;li&gt;Top K Frequent Elements&lt;/li&gt;
&lt;li&gt;Sliding Window Maximum&lt;/li&gt;
&lt;li&gt;Meeting Rooms II (Minimum Conference Rooms)&lt;/li&gt;
&lt;li&gt;Kth Largest Element in Stream&lt;/li&gt;
&lt;li&gt;Memory Management&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;PriorityQueue&lt;/code&gt; to implement Heap Sort&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.PriorityQueue;
import java.util.Arrays;

public class HeapSortExample {
    public static void main(String[] args) {
        int[] nums = {20, 5, 15, 10};

        // Min-Heap for ascending order
        PriorityQueue&amp;lt;Integer&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;();

        // Add all elements to the heap
        for (int num : nums) {
            pq.add(num);
        }

        // Extract elements to get sorted order
        int index = 0;
        while (!pq.isEmpty()) {
            nums[index++] = pq.poll();
        }

        System.out.println("Sorted array: " + Arrays.toString(nums)); // [5, 10, 15, 20]
    }
}

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add all elements to a Min-Heap (PriorityQueue by default).&lt;/li&gt;
&lt;li&gt;Poll elements one by one → smallest element comes first.&lt;/li&gt;
&lt;li&gt;Store them back → array becomes sorted in ascending order.&lt;/li&gt;
&lt;li&gt;For descending order, use a Max-Heap: &lt;code&gt;PriorityQueue&amp;lt;Integer&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;(Collections.reverseOrder());&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. PriorityQueue with Objects&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.PriorityQueue;
import java.util.Comparator;

class Student {
    String name;
    int score;

    Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    @Override
    public String toString() {
        return name + " (" + score + ")";
    }
}

public class Main {
    public static void main(String[] args) {
        // Max-Heap: higher score = higher priority
        PriorityQueue&amp;lt;Student&amp;gt; pq = new PriorityQueue&amp;lt;&amp;gt;(Comparator.comparingInt((Student s) -&amp;gt; s.score).reversed());

        pq.add(new Student("Alice", 85));
        pq.add(new Student("Bob", 92));
        pq.add(new Student("Charlie", 78));

        System.out.println("Students served by priority:");
        while (!pq.isEmpty()) {
            System.out.println(pq.poll());
        }
    }
}

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

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Use Comparable for natural/default sorting.&lt;/li&gt;
&lt;li&gt;Use Comparator for custom/multiple sorting logic.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Comparable
class Student implements Comparable&amp;lt;Student&amp;gt; {
    int score;
    public int compareTo(Student s) { return this.score - s.score; }
}

// Comparator
Comparator&amp;lt;Student&amp;gt; byScoreDesc = (a, b) -&amp;gt; b.score - a.score;

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;PriorityQueue is the go-to choice when you need efficient access to the minimum/maximum element with frequent insertions and deletions, making it perfect for algorithms that require greedy selection based on priority.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Deque (ArrayDeque/LinkedDeque)
&lt;/h2&gt;

&lt;p&gt;Deque (pronounced "deck" is part of the &lt;code&gt;java.util&lt;/code&gt; package ) is a double-ended queue that allows insertion and deletion at both ends. Java provides two main implementations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ArrayDeque: Resizable array implementation&lt;/li&gt;
&lt;li&gt;LinkedList: Doubly-linked list implementation (also implements Deque)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Double-ended operations - Insert/remove from both front and rear&lt;/li&gt;
&lt;li&gt;No capacity restrictions - Dynamically resizable&lt;/li&gt;
&lt;li&gt;Not thread-safe - Requires external synchronization&lt;/li&gt;
&lt;li&gt;Null elements not allowed (ArrayDeque) vs Null allowed (LinkedList)&lt;/li&gt;
&lt;li&gt;Faster than Stack and LinkedList for stack/queue operations&lt;/li&gt;
&lt;li&gt;More memory efficient than LinkedList (ArrayDeque)&lt;/li&gt;
&lt;li&gt;Implements both Queue and Deque interfaces&lt;/li&gt;
&lt;li&gt;Supports both LIFO and FIFO operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax for &lt;code&gt;ArrayDeque&lt;/code&gt; and &lt;code&gt;LinkedList&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.Deque;
import java.util.ArrayDeque;
import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        // Using ArrayDeque
        Deque&amp;lt;Integer&amp;gt; arrayDeque = new ArrayDeque&amp;lt;&amp;gt;();
        arrayDeque.addFirst(10);
        arrayDeque.addLast(20);
        System.out.println("ArrayDeque peekFirst: " + arrayDeque.peekFirst()); // 10
        System.out.println("ArrayDeque peekLast: " + arrayDeque.peekLast());   // 20
        arrayDeque.removeFirst();
        arrayDeque.removeLast();

        // Using LinkedList
        Deque&amp;lt;Integer&amp;gt; linkedListDeque = new LinkedList&amp;lt;&amp;gt;();
        linkedListDeque.addFirst(30);
        linkedListDeque.addLast(40);
        System.out.println("LinkedList peekFirst: " + linkedListDeque.peekFirst()); // 30
        System.out.println("LinkedList peekLast: " + linkedListDeque.peekLast());   // 40
        linkedListDeque.removeFirst();
        linkedListDeque.removeLast();
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;ArrayDeque&lt;/th&gt;
&lt;th&gt;LinkedList&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;addFirst(e)&lt;/td&gt;
&lt;td&gt;O(1) amortized&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Insert at front&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;addLast(e)&lt;/td&gt;
&lt;td&gt;O(1) amortized&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Insert at rear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;removeFirst()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Remove from front&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;removeLast()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Remove from rear&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;peekFirst()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Get first without removing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;peekLast()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Get last without removing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pollFirst()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Remove first (null if empty)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;pollLast()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Remove last (null if empty)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;size()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Get number of elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;isEmpty()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Check if empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;contains(o)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Search for element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;remove(o)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Remove specific element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;clear()&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Remove all elements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;ArrayDeque vs LinkedList Performance&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;ArrayDeque&lt;/th&gt;
&lt;th&gt;LinkedList&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Lower overhead&lt;/td&gt;
&lt;td&gt;Higher overhead (node pointers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cache Performance&lt;/td&gt;
&lt;td&gt;Better (array locality)&lt;/td&gt;
&lt;td&gt;Worse (scattered nodes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add/Remove Ends&lt;/td&gt;
&lt;td&gt;Faster&lt;/td&gt;
&lt;td&gt;Slightly slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random Access&lt;/td&gt;
&lt;td&gt;Not supported&lt;/td&gt;
&lt;td&gt;Not efficient&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iterator&lt;/td&gt;
&lt;td&gt;Faster&lt;/td&gt;
&lt;td&gt;Slower&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory Allocation&lt;/td&gt;
&lt;td&gt;Bulk allocation&lt;/td&gt;
&lt;td&gt;Per-element allocation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sliding Window Maximum/Minimum - Maintain elements in window&lt;/li&gt;
&lt;li&gt;Palindrome Checking - Compare characters from both ends&lt;/li&gt;
&lt;li&gt;Undo/Redo Functionality - Stack-like operations with flexibility&lt;/li&gt;
&lt;li&gt;BFS with Level Tracking - Process nodes level by level&lt;/li&gt;
&lt;li&gt;Expression Evaluation - Handle nested brackets and operators&lt;/li&gt;
&lt;li&gt;Implementing Stack and Queue - Single data structure for both&lt;/li&gt;
&lt;li&gt;Monotonic Deque Problems - Maintain increasing/decreasing order&lt;/li&gt;
&lt;li&gt;Circular Buffer Implementation - Ring buffer with dynamic size&lt;/li&gt;
&lt;li&gt;Browser History Navigation - Forward/backward navigation&lt;/li&gt;
&lt;li&gt;Task Scheduling - Priority-based insertion at both ends&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://leetcode.com/problems/sliding-window-maximum/description/" rel="noopener noreferrer"&gt;sliding-window-maximum&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.*;

public class SlidingWindowMaximum {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length == 0 || k &amp;lt;= 0) {
            return new int[0];
        }

        Deque&amp;lt;Integer&amp;gt; deque = new ArrayDeque&amp;lt;&amp;gt;(); // Stores indices
        int[] result = new int[nums.length - k + 1];
        int resultIndex = 0;

        for (int i = 0; i &amp;lt; nums.length; i++) {
            // Remove indices that are out of current window
            while (!deque.isEmpty() &amp;amp;&amp;amp; deque.peekFirst() &amp;lt; i - k + 1) {
                deque.pollFirst();
            }

            // Remove indices whose corresponding values are smaller than current
            while (!deque.isEmpty() &amp;amp;&amp;amp; nums[deque.peekLast()] &amp;lt; nums[i]) {
                deque.pollLast();
            }

            // Add current index
            deque.offerLast(i);

            // If we have processed at least k elements, add to result
            if (i &amp;gt;= k - 1) {
                result[resultIndex++] = nums[deque.peekFirst()];
            }
        }

        return result;
    }

    public static void main(String[] args) {
        SlidingWindowMaximum solution = new SlidingWindowMaximum();
        int[] nums = {1, 3, -1, -3, 5, 3, 6, 7};
        int k = 3;

        int[] result = solution.maxSlidingWindow(nums, k);
        System.out.println("Sliding Window Maximum: " + Arrays.toString(result));
        // Output: [3, 3, 5, 5, 6, 7]
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use ArrayDeque for better performance and LinkedList when frequent insertions/removals in the middle are needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. BlockingQueue (ArrayBlockingQueue, LinkedBlockingQueue)
&lt;/h2&gt;

&lt;p&gt;BlockingQueue is a thread-safe queue interface that supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available when storing an element. It's part of the &lt;code&gt;java.util.concurrent&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread-safe - Built-in synchronization for concurrent access&lt;/li&gt;
&lt;li&gt;Blocking operations - Threads wait when queue is full/empty&lt;/li&gt;
&lt;li&gt;Producer-Consumer pattern - Perfect for multi-threaded scenarios&lt;/li&gt;
&lt;li&gt;No null elements - Null values are not permitted&lt;/li&gt;
&lt;li&gt;Capacity management - Can be bounded or unbounded&lt;/li&gt;
&lt;li&gt;Multiple implementations - ArrayBlockingQueue, LinkedBlockingQueue, etc.&lt;/li&gt;
&lt;li&gt;Timeout support - Operations can have time limits&lt;/li&gt;
&lt;li&gt;Collection interface - Implements Queue and Collection interfaces&lt;/li&gt;
&lt;li&gt;Atomic operations - All operations are atomic and thread-safe&lt;/li&gt;
&lt;li&gt;Fair access - Optional fairness policy for thread scheduling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax for &lt;code&gt;ArrayBlockingQueue&lt;/code&gt; and &lt;code&gt;LinkedBlockingQueue&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // ArrayBlockingQueue
        BlockingQueue&amp;lt;Integer&amp;gt; arrayQueue = new ArrayBlockingQueue&amp;lt;&amp;gt;(3);
        arrayQueue.put(1);
        arrayQueue.put(2);
        System.out.println("ArrayBlockingQueue peek: " + arrayQueue.peek()); // 1
        System.out.println("ArrayBlockingQueue removed: " + arrayQueue.take()); // 1

        // LinkedBlockingQueue
        BlockingQueue&amp;lt;Integer&amp;gt; linkedQueue = new LinkedBlockingQueue&amp;lt;&amp;gt;(3);
        linkedQueue.put(10);
        linkedQueue.put(20);
        System.out.println("LinkedBlockingQueue peek: " + linkedQueue.peek()); // 10
        System.out.println("LinkedBlockingQueue removed: " + linkedQueue.take()); // 10
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation / Method&lt;/th&gt;
&lt;th&gt;ArrayBlockingQueue&lt;/th&gt;
&lt;th&gt;LinkedBlockingQueue&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Insert (Block) &lt;code&gt;put(e)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Blocks until space available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert (Timeout) &lt;code&gt;offer(e, time, unit)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Waits for specified time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert (No Block) &lt;code&gt;offer(e)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns false if full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Insert (Exception) &lt;code&gt;add(e)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Throws exception if full&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove (Block) &lt;code&gt;take()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Blocks until element available&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove (Timeout) &lt;code&gt;poll(time, unit)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Waits for specified time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove (No Block) &lt;code&gt;poll()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns null if empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove (Exception) &lt;code&gt;remove()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Throws exception if empty&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Examine &lt;code&gt;peek()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns head without removing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size &lt;code&gt;size()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Current number of elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity &lt;code&gt;remainingCapacity()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Available space&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains &lt;code&gt;contains(o)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Search for element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove Specific &lt;code&gt;remove(o)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Remove specific element&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Drain &lt;code&gt;drainTo(collection)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Transfer all elements to collection&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;ArrayBlockingQueue vs LinkedBlockingQueue features:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;ArrayBlockingQueue&lt;/th&gt;
&lt;th&gt;LinkedBlockingQueue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Storage&lt;/td&gt;
&lt;td&gt;Fixed-size array&lt;/td&gt;
&lt;td&gt;Dynamic linked nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Capacity&lt;/td&gt;
&lt;td&gt;Fixed at creation&lt;/td&gt;
&lt;td&gt;Fixed or unbounded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Pre-allocated&lt;/td&gt;
&lt;td&gt;Allocated on demand&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Locks&lt;/td&gt;
&lt;td&gt;Single lock&lt;/td&gt;
&lt;td&gt;Two locks (put/take)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Better for bounded queues&lt;/td&gt;
&lt;td&gt;Better for high throughput&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GC Pressure&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;td&gt;Higher (node allocation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Producer-Consumer Pattern - Multiple threads producing/consuming data&lt;/li&gt;
&lt;li&gt;Thread Pool Task Queues - Executor framework uses BlockingQueue&lt;/li&gt;
&lt;li&gt;Rate Limiting and Throttling - Control processing speed&lt;/li&gt;
&lt;li&gt;Batch Processing Systems - Accumulate work units for batch processing&lt;/li&gt;
&lt;li&gt;Event-Driven Architectures - Decouple event producers from consumers&lt;/li&gt;
&lt;li&gt;Message Passing Systems - Inter-thread communication&lt;/li&gt;
&lt;li&gt;Resource Pool Management - Manage limited resources among threads&lt;/li&gt;
&lt;li&gt;Data Pipeline Processing - Chain of processing stages&lt;/li&gt;
&lt;li&gt;Load Balancing - Distribute work across multiple worker threads&lt;/li&gt;
&lt;li&gt;Bounded Buffer Problem - Classic synchronization problem&lt;/li&gt;
&lt;li&gt;Work Stealing Algorithms - Task distribution in parallel computing&lt;/li&gt;
&lt;li&gt;Real-time Data Processing - Handle continuous data streams&lt;/li&gt;
&lt;li&gt;Microservices Communication - Async communication between services&lt;/li&gt;
&lt;li&gt;Cache Warming - Pre-load cache data in background threads&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

/**
 * Simple Producer-Consumer Example using BlockingQueue
 * 
 * Real scenario: Online Store Order Processing
 * - Customer places orders (Producer)
 * - Warehouse processes orders (Consumer)
 */
public class SimpleProducerConsumer {

    public static void main(String[] args) {
        // Shared queue between producer and consumer (max 5 orders)
        BlockingQueue&amp;lt;String&amp;gt; orderQueue = new ArrayBlockingQueue&amp;lt;&amp;gt;(5);

        // Producer: Customer placing orders
        Thread customer = new Thread(() -&amp;gt; {
            String[] orders = {
                "Order-1: iPhone 14", 
                "Order-2: MacBook Pro", 
                "Order-3: AirPods", 
                "Order-4: iPad", 
                "Order-5: Apple Watch",
                "Order-6: Magic Mouse",
                "Order-7: Keyboard"
            };

            try {
                for (String order : orders) {
                    System.out.println("Customer placing: " + order);
                    orderQueue.put(order); // Blocks if queue is full
                    Thread.sleep(500); // Customer thinks for 500ms before next order
                }
                System.out.println("Customer finished placing all orders");

            } catch (InterruptedException e) {
                System.out.println("Customer interrupted");
            }
        });

        // Consumer: Warehouse processing orders
        Thread warehouse = new Thread(() -&amp;gt; {
            try {
                Thread.sleep(2000); // Warehouse starts working after 2 seconds

                while (true) {
                    String order = orderQueue.take(); // Blocks if queue is empty
                    System.out.println("Warehouse processing: " + order);
                    Thread.sleep(1000); // Takes 1 second to process each order
                    System.out.println("Warehouse completed: " + order);
                }

            } catch (InterruptedException e) {
                System.out.println("Warehouse stopped working");
            }
        });

        // Start both threads
        customer.start();
        warehouse.start();

        // Let them run for 10 seconds, then stop
        try {
            Thread.sleep(10000);
            warehouse.interrupt(); // Stop the warehouse
            customer.join(); // Wait for customer to finish
            System.out.println("\n Store closed for the day!");

        } catch (InterruptedException e) {
            System.out.println("Main interrupted");
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BlockingQueue is the cornerstone of concurrent programming in Java, providing thread-safe, blocking operations that are essential for producer-consumer patterns. ArrayBlockingQueue offers better memory efficiency for bounded scenarios, while LinkedBlockingQueue provides higher throughput for demanding applications. Choose based on your specific concurrency needs, memory constraints, and performance requirements.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. BitSet
&lt;/h2&gt;

&lt;p&gt;BitSet is a resizable array of bits that provides efficient storage and manipulation of boolean values. Each bit can be set (1) or cleared (0), making it extremely memory-efficient for representing large sets of boolean flags or performing set operations on integers.It's part of the &lt;code&gt;java.util&lt;/code&gt; package.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Memory efficient - Uses only 1 bit per boolean value (vs 8 bits for boolean array)&lt;/li&gt;
&lt;li&gt;Dynamically resizable - Grows automatically as needed&lt;/li&gt;
&lt;li&gt;Fast bitwise operations - Hardware-optimized bit manipulation&lt;/li&gt;
&lt;li&gt;Set operations support - AND, OR, XOR, NOT operations&lt;/li&gt;
&lt;li&gt;Thread-unsafe - Requires external synchronization for concurrent access&lt;/li&gt;
&lt;li&gt;Zero-based indexing - Bits are numbered from 0 to size-1&lt;/li&gt;
&lt;li&gt;Automatic bit clearing - Unset bits are automatically 0 (false)&lt;/li&gt;
&lt;li&gt;Efficient iteration - Special methods to iterate over set bits only&lt;/li&gt;
&lt;li&gt;Serializable - Can be serialized/deserialized&lt;/li&gt;
&lt;li&gt;Cloneable - Supports deep copying&lt;/li&gt;
&lt;li&gt;No null values - Only boolean states (set/clear)&lt;/li&gt;
&lt;li&gt;Compact representation - Internally uses long arrays for storage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax for &lt;code&gt;BitSet&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.BitSet;

public class Main {
    public static void main(String[] args) {
        // Create a BitSet
        BitSet bits = new BitSet();

        // Set bits
        bits.set(0);          // set bit at index 0
        bits.set(2);          // set bit at index 2
        bits.set(4, 7);       // set bits from index 4 to 6 (7 exclusive)

        // Get bits
        System.out.println("Bit at index 2: " + bits.get(2)); // true
        System.out.println("Bit at index 3: " + bits.get(3)); // false

        // Clear bits
        bits.clear(2);        // clear bit at index 2

        // Flip bits
        bits.flip(0);         // flip bit at index 0

        // Size and length
        System.out.println("BitSet length: " + bits.length());
        System.out.println("BitSet size: " + bits.size());

        // Print BitSet
        System.out.println("BitSet: " + bits);
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;th&gt;Space Complexity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Set Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set(int bitIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) amortized&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Set bit at index to 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set Range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set(int from, int to)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Set multiple bits in range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clear(int bitIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Set bit at index to 0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear Range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clear(int from, int to)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Clear multiple bits in range&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear All&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clear()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Clear all bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get(int bitIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Get bit value at index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get Range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get(int from, int to)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Get BitSet subset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flip Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;flip(int bitIndex)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Toggle bit at index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flip Range&lt;/td&gt;
&lt;td&gt;&lt;code&gt;flip(int from, int to)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Toggle multiple bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AND Operation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;and(BitSet set)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Logical AND with another BitSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OR Operation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;or(BitSet set)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Logical OR with another BitSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;XOR Operation&lt;/td&gt;
&lt;td&gt;&lt;code&gt;xor(BitSet set)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Logical XOR with another BitSet&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AND-NOT&lt;/td&gt;
&lt;td&gt;&lt;code&gt;andNot(BitSet set)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;AND with complement of set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Number of bits of space used&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Length&lt;/td&gt;
&lt;td&gt;&lt;code&gt;length()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Highest set bit index + 1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cardinality&lt;/td&gt;
&lt;td&gt;&lt;code&gt;cardinality()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Number of set bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is Empty&lt;/td&gt;
&lt;td&gt;&lt;code&gt;isEmpty()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Check if no bits are set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next Set Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextSetBit(int from)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) avg&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Find next set bit from index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next Clear Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;nextClearBit(int from)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) avg&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Find next clear bit from index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous Set Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;previousSetBit(int from)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) avg&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Find previous set bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Previous Clear Bit&lt;/td&gt;
&lt;td&gt;&lt;code&gt;previousClearBit(int from)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) avg&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Find previous clear bit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Intersects&lt;/td&gt;
&lt;td&gt;&lt;code&gt;intersects(BitSet set)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Check if any bits are common&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clone&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clone()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Create deep copy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;To Byte Array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;toByteArray()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Convert to byte array&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;To Long Array&lt;/td&gt;
&lt;td&gt;&lt;code&gt;toLongArray()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Convert to long array&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;BitSet vs Alternatives&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;BitSet&lt;/th&gt;
&lt;th&gt;boolean[]&lt;/th&gt;
&lt;th&gt;Set&lt;/th&gt;
&lt;th&gt;EnumSet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory Efficiency&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;Very Poor&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set Operations&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range Operations&lt;/td&gt;
&lt;td&gt;Native&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Limited&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Thread Safety&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Depends&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sparse Data&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Poor&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sieve of Eratosthenes - Finding prime numbers efficiently&lt;/li&gt;
&lt;li&gt;Bloom Filters - Probabilistic membership testing&lt;/li&gt;
&lt;li&gt;Set Operations - Union, intersection, difference of integer sets&lt;/li&gt;
&lt;li&gt;Graph Algorithms - Visited node tracking in BFS/DFS&lt;/li&gt;
&lt;li&gt;Permutation Generation - Track used elements in backtracking&lt;/li&gt;
&lt;li&gt;Bit Manipulation Problems - Subset generation, bit counting&lt;/li&gt;
&lt;li&gt;Database Indexing - Bitmap indexes for fast queries&lt;/li&gt;
&lt;li&gt;Compiler Design - Register allocation, live variable analysis&lt;/li&gt;
&lt;li&gt;Image Processing - Binary image representation and operations&lt;/li&gt;
&lt;li&gt;Game Development - Collision detection, state flags&lt;/li&gt;
&lt;li&gt;Network Protocols - Flag management, packet filtering&lt;/li&gt;
&lt;li&gt;Cryptography - One-time pad, stream ciphers&lt;/li&gt;
&lt;li&gt;Data Compression - Run-length encoding preprocessing&lt;/li&gt;
&lt;li&gt;Memory Management - Free/allocated block tracking&lt;/li&gt;
&lt;li&gt;Scheduling Algorithms - Resource availability tracking&lt;/li&gt;
&lt;li&gt;Machine Learning - Feature selection, sparse data representation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Finding Primes Using BitSet&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.BitSet;

public class SieveBitSet {
    public static void main(String[] args) {
        int N = 50;
        BitSet primes = new BitSet(N + 1);

        // Initially assume all numbers &amp;gt;=2 are prime
        primes.set(2, N + 1); // set bits 2..N

        for (int i = 2; i * i &amp;lt;= N; i++) {
            if (primes.get(i)) {
                // Mark multiples of i as non-prime
                for (int j = i * i; j &amp;lt;= N; j += i) {
                    primes.clear(j);
                }
            }
        }

        // Print primes
        System.out.println("Primes up to " + N + ":");
        for (int i = 2; i &amp;lt;= N; i++) {
            if (primes.get(i)) {
                System.out.print(i + " ");
            }
        }
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;BitSet is a specialized, highly efficient data structure for boolean operations and set manipulation. It shines in mathematical algorithms, large-scale boolean flag management, and memory-constrained environments. Choose BitSet when you need fast bitwise operations on large datasets, especially for algorithms like sieve methods, graph traversal state tracking, or any scenario involving set theory operations on integers. Its memory efficiency (1 bit per boolean vs 8 bits for boolean arrays) makes it invaluable for applications handling millions of boolean flags.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. CopyOnWriteArrayList/CopyOnWriteArraySet
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; is a thread-safe variant of ArrayList (in &lt;code&gt;java.util.concurrent&lt;/code&gt;) where all mutative operations (add, set, remove, etc.) create a fresh copy of the underlying array. Its iterators work on an immutable snapshot, so they never throw &lt;code&gt;ConcurrentModificationException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;CopyOnWriteArraySet&lt;/code&gt; is a thread-safe Set that uses CopyOnWriteArrayList internally. It enforces uniqueness with addIfAbsent, and iterators also operate on a snapshot.&lt;/p&gt;

&lt;p&gt;Both are ideal for read-heavy, infrequently modified data—iterations are fast and lock-free, while writes are costly due to array copying. Common use cases include listener registries, configuration snapshots, and caches. They are not suitable for write-heavy workloads because of high write overhead and GC pressure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Thread-safe, suitable for concurrent read-heavy scenarios.&lt;/li&gt;
&lt;li&gt;All mutative operations (add, set, remove) create a fresh copy of the underlying array.&lt;/li&gt;
&lt;li&gt;Iterators are weakly consistent: do not throw ConcurrentModificationException.&lt;/li&gt;
&lt;li&gt;Reads are lock-free, writes are expensive due to array copying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Basic Syntax for &lt;code&gt;CopyOnWriteArrayList&lt;/code&gt; and &lt;code&gt;CopyOnWriteArraySet&lt;/code&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;

public class Main {
    public static void main(String[] args) {
        // -------------------------
        // CopyOnWriteArrayList
        // -------------------------
        CopyOnWriteArrayList&amp;lt;String&amp;gt; list = new CopyOnWriteArrayList&amp;lt;&amp;gt;();
        list.add("Apple");
        list.add("Banana");
        list.add("Orange");

        System.out.println("CopyOnWriteArrayList elements:");
        for (String fruit : list) {
            System.out.println(fruit);
        }

        list.remove("Banana");
        System.out.println("After removal: " + list);

        // -------------------------
        // CopyOnWriteArraySet
        // -------------------------
        CopyOnWriteArraySet&amp;lt;String&amp;gt; set = new CopyOnWriteArraySet&amp;lt;&amp;gt;();
        set.add("Apple");
        set.add("Banana");
        set.add("Apple"); // duplicate ignored

        System.out.println("\nCopyOnWriteArraySet elements:");
        for (String fruit : set) {
            System.out.println(fruit);
        }

        set.remove("Banana");
        System.out.println("After removal: " + set);
    }
}


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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add&lt;/td&gt;
&lt;td&gt;&lt;code&gt;add(E e)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Adds element at the end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Remove&lt;/td&gt;
&lt;td&gt;&lt;code&gt;remove(Object o)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Removes first occurrence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Get&lt;/td&gt;
&lt;td&gt;&lt;code&gt;get(int index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Retrieves element at index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set(int index, E element)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Replaces element at index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains&lt;/td&gt;
&lt;td&gt;&lt;code&gt;contains(Object o)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Checks if element exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;&lt;code&gt;size()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns number of elements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iterator&lt;/td&gt;
&lt;td&gt;&lt;code&gt;iterator()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns snapshot iterator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clear&lt;/td&gt;
&lt;td&gt;&lt;code&gt;clear()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Removes all elements&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import java.util.concurrent.CopyOnWriteArrayList;

// Subscriber interface
interface Subscriber {
    void notify(String message);
}

// Event Bus
class EventBus {
    private CopyOnWriteArrayList&amp;lt;Subscriber&amp;gt; subscribers = new CopyOnWriteArrayList&amp;lt;&amp;gt;();

    // Add a subscriber
    public void subscribe(Subscriber subscriber) {
        subscribers.add(subscriber);
    }

    // Remove a subscriber
    public void unsubscribe(Subscriber subscriber) {
        subscribers.remove(subscriber);
    }

    // Publish an event to all subscribers
    public void publish(String message) {
        for (Subscriber s : subscribers) {
            s.notify(message);
        }
    }
}

// Demo
public class Main {
    public static void main(String[] args) {
        EventBus bus = new EventBus();

        // Add subscribers
        bus.subscribe(msg -&amp;gt; System.out.println("Subscriber 1 received: " + msg));
        bus.subscribe(msg -&amp;gt; System.out.println("Subscriber 2 received: " + msg));

        // Publish events
        bus.publish("Event A");
        bus.publish("Event B");

        // Remove a subscriber
        bus.unsubscribe(msg -&amp;gt; System.out.println("Subscriber 2 received: " + msg));

        // Publish another event
        bus.publish("Event C");
    }
}

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

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Multiple threads can publish events (read/iterate) safely while subscribers are added/removed.&lt;/li&gt;
&lt;li&gt;Iteration is snapshot-based, so no ConcurrentModificationException occurs.&lt;/li&gt;
&lt;li&gt;Writes (adding/removing subscribers) are rare, which suits the cost of copying the array.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event listener lists where reads are frequent but modifications are rare.&lt;/li&gt;
&lt;li&gt;Caching read-heavy data structures.&lt;/li&gt;
&lt;li&gt;Pub-sub systems where many threads read concurrently.&lt;/li&gt;
&lt;li&gt;Iterating safely without worrying about ConcurrentModificationException.&lt;/li&gt;
&lt;li&gt;Maintaining snapshots in concurrent applications.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;High memory and CPU overhead if writes are frequent (copying entire array on every write).&lt;/li&gt;
&lt;li&gt;Not suitable for write-heavy workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use CopyOnWriteArrayList / CopyOnWriteArraySet when reads far exceed writes and you need thread-safe, snapshot-style iteration without extra synchronization. Avoid them for write-heavy workloads due to the high cost of copying the array on every modification.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Disjoint Set Union (DSU) / Union-Find in Java
&lt;/h2&gt;

&lt;p&gt;A Disjoint Set Union (DSU) is a data structure that keeps track of a partition of a set into disjoint (non-overlapping) subsets. It is especially useful in problems involving connectivity, merging groups, and cycle detection.&lt;/p&gt;

&lt;p&gt;Supports two main operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Find&lt;/strong&gt;: Determine which subset a particular element belongs to.&lt;/li&gt;
&lt;li&gt;   &lt;strong&gt;Union&lt;/strong&gt;: Merge two subsets into a single subset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Optimizations include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Path Compression in &lt;code&gt;find()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Union by Rank / Size&lt;code&gt;in&lt;/code&gt;union()`
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Efficient for &lt;strong&gt;connectivity problems&lt;/strong&gt; in graphs (like detecting cycles, Kruskal’s MST algorithm).
&lt;/li&gt;
&lt;li&gt;Each element belongs to exactly one subset.
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;dynamic connectivity&lt;/strong&gt; queries.
&lt;/li&gt;
&lt;li&gt;Almost constant time per operation: &lt;strong&gt;O(α(n))&lt;/strong&gt;, where α(n) is the inverse Ackermann function.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Find&lt;/td&gt;
&lt;td&gt;&lt;code&gt;find(int x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(α(n))&lt;/td&gt;
&lt;td&gt;Returns the representative (root) of element x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Union&lt;/td&gt;
&lt;td&gt;&lt;code&gt;union(int x, int y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(α(n))&lt;/td&gt;
&lt;td&gt;Merges the sets containing x and y&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Connected&lt;/td&gt;
&lt;td&gt;&lt;code&gt;connected(int x, int y)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(α(n))&lt;/td&gt;
&lt;td&gt;Checks if x and y belong to the same set&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Size&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;size()&lt;/code&gt; (optional)&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;Returns the number of disjoint sets&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;&lt;strong&gt;Classic Use Cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Kruskal’s Minimum Spanning Tree algorithm&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Detecting &lt;strong&gt;cycles in undirected graphs&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dynamic connectivity&lt;/strong&gt; problems (e.g., network connectivity).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Union of sets&lt;/strong&gt; in clustering algorithms or percolation problems.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
public class DSU {
    private int[] parent;
    private int[] rank;

    // Initialize n elements
    public DSU(int n) {
        parent = new int[n];
        rank = new int[n];
        for (int i = 0; i &amp;lt; n; i++) {
            parent[i] = i; // each element is its own parent
            rank[i] = 0;
        }
    }

    // Find with path compression
    public int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }

    // Union by rank
    public void union(int x, int y) {
        int rootX = find(x);
        int rootY = find(y);
        if (rootX == rootY) return;

        if (rank[rootX] &amp;lt; rank[rootY]) {
            parent[rootX] = rootY;
        } else if (rank[rootX] &amp;gt; rank[rootY]) {
            parent[rootY] = rootX;
        } else {
            parent[rootY] = rootX;
            rank[rootX]++;
        }
    }

    // Check if two elements are connected
    public boolean connected(int x, int y) {
        return find(x) == find(y);
    }
}

// Demo
public class Main {
    public static void main(String[] args) {
        DSU dsu = new DSU(5);

        dsu.union(0, 1);
        dsu.union(1, 2);

        System.out.println(dsu.connected(0, 2)); // true
        System.out.println(dsu.connected(0, 3)); // false
    }
}

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

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use Disjoint Set Union for connectivity problems and graph algorithms.&lt;br&gt;
Highly efficient due to path compression and union by rank, almost constant time per operation. Ideal for Kruskal’s MST, dynamic connectivity, clustering, and percolation problems.&lt;/p&gt;


&lt;h2&gt;
  
  
  7. Fenwick Tree (Binary Indexed Tree) in Java
&lt;/h2&gt;

&lt;p&gt;A &lt;strong&gt;Fenwick Tree&lt;/strong&gt;, also called &lt;strong&gt;Binary Indexed Tree (BIT)&lt;/strong&gt;, is a data structure that efficiently supports:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prefix sum queries&lt;/strong&gt; (sum of first &lt;code&gt;k&lt;/code&gt; elements)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Point updates&lt;/strong&gt; (update a single element)

&lt;ul&gt;
&lt;li&gt;Time complexity: &lt;strong&gt;O(log n)&lt;/strong&gt; for both query and update.
&lt;/li&gt;
&lt;li&gt;Space complexity: &lt;strong&gt;O(n)&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Ideal for scenarios where you have frequent &lt;strong&gt;prefix sum or cumulative frequency queries&lt;/strong&gt; with dynamic updates.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Efficient for &lt;strong&gt;range sum queries&lt;/strong&gt; on mutable arrays.
&lt;/li&gt;
&lt;li&gt;Uses &lt;strong&gt;binary representation of indices&lt;/strong&gt; to traverse parent/child relationships.
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;additive operations&lt;/strong&gt; (sum, XOR, etc.).
&lt;/li&gt;
&lt;li&gt;Not suitable for arbitrary range updates unless combined with &lt;strong&gt;advanced variants&lt;/strong&gt; (like range update BIT).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Update / Add&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update(index, value)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;Adds value to element at index&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Query / PrefixSum&lt;/td&gt;
&lt;td&gt;&lt;code&gt;query(index)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(log n)&lt;/td&gt;
&lt;td&gt;Returns sum of elements [0..index]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Build Tree&lt;/td&gt;
&lt;td&gt;Constructor / &lt;code&gt;build(arr)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;Initializes tree from array&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Range sum queries&lt;/strong&gt; with dynamic updates (e.g., cumulative frequency tables).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Counting inversions&lt;/strong&gt; in an array.
&lt;/li&gt;
&lt;li&gt;Competitive programming problems involving &lt;strong&gt;prefix sums, frequencies, or 2D variants&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Efficiently implementing &lt;strong&gt;histograms or cumulative statistics&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
public class FenwickTree {
    private int[] bit; // Binary Indexed Tree array (1-based indexing)
    private int size;  // Size of the original array

    /**
     * Constructs a Fenwick Tree for an array of a given size.
     * The tree is initialized with all zeros.
     *
     * @param size The size of the original array for which the Fenwick Tree is built.
     */
    public FenwickTree(int size) {
        this.size = size;
        this.bit = new int[size + 1]; // 1-based indexing, so size + 1
    }

    /**
     * Updates the value at a specific index in the original array by adding 'delta'.
     * This update propagates through the Fenwick Tree.
     *
     * @param index The 0-based index in the original array to update.
     * @param delta The value to add to the element at the given index.
     */
    public void update(int index, int delta) {
        // Convert to 1-based indexing for BIT operations
        index++; 
        while (index &amp;lt;= size) {
            bit[index] += delta;
            // Move to the next parent node by adding the rightmost set bit
            index += (index &amp;amp; -index); 
        }
    }

    /**
     * Queries the prefix sum up to a specific index (inclusive) in the original array.
     *
     * @param index The 0-based index in the original array up to which to calculate the sum.
     * @return The sum of elements from index 0 to 'index' in the original array.
     */
    public int query(int index) {
        int sum = 0;
        // Convert to 1-based indexing for BIT operations
        index++; 
        while (index &amp;gt; 0) {
            sum += bit[index];
            // Move to the next parent node by subtracting the rightmost set bit
            index -= (index &amp;amp; -index); 
        }
        return sum;
    }

    /**
     * Calculates the sum of elements within a given range [left, right] (inclusive, 0-based).
     *
     * @param left The 0-based starting index of the range.
     * @param right The 0-based ending index of the range.
     * @return The sum of elements from 'left' to 'right'.
     */
    public int rangeSum(int left, int right) {
        if (left &amp;gt; right) {
            throw new IllegalArgumentException("Left index cannot be greater than right index.");
        }
        return query(right) - (left &amp;gt; 0 ? query(left - 1) : 0);
    }

    // Example Usage
    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5};
        FenwickTree ft = new FenwickTree(arr.length);

        // Initialize Fenwick Tree with values from the array
        for (int i = 0; i &amp;lt; arr.length; i++) {
            ft.update(i, arr[i]);
        }

        System.out.println("Prefix sum up to index 2 (0-based): " + ft.query(2)); // Expected: 1 + 2 + 3 = 6
        System.out.println("Prefix sum up to index 4 (0-based): " + ft.query(4)); // Expected: 1 + 2 + 3 + 4 + 5 = 15

        // Update value at index 2 (change 3 to 10)
        ft.update(2, 7); // Add 7 to the existing value at index 2 (3 + 7 = 10)
        System.out.println("Prefix sum up to index 2 after update: " + ft.query(2)); // Expected: 1 + 2 + 10 = 13

        System.out.println("Range sum from index 1 to 3 (0-based): " + ft.rangeSum(1, 3)); // Expected: 2 + 10 + 4 = 16
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use Fenwick Tree for dynamic prefix sums or cumulative frequency queries.&lt;br&gt;
Efficient alternative to segment trees for point updates and prefix queries. Time complexity per operation: O(log n), space complexity: O(n).&lt;br&gt;
Ideal for competitive programming, range queries, and real-time cumulative statistics.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Trie (Prefix Tree) in Java
&lt;/h2&gt;

&lt;p&gt;A Trie, also known as a Prefix Tree, is a tree-like data structure used to store a dynamic set of strings efficiently, particularly for operations involving prefixes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There is &lt;strong&gt;no built-in Trie in Java&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Typically implemented manually using &lt;strong&gt;custom classes&lt;/strong&gt; with &lt;code&gt;Map&lt;/code&gt; or arrays.
&lt;/li&gt;
&lt;li&gt;Can use &lt;code&gt;java.util.Map&lt;/code&gt; or &lt;code&gt;java.util.HashMap&lt;/code&gt; for child nodes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key Properties&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tree-based data structure storing &lt;strong&gt;strings or sequences&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Each node represents a &lt;strong&gt;single character&lt;/strong&gt; or part of the key.
&lt;/li&gt;
&lt;li&gt;Supports &lt;strong&gt;fast prefix-based searches&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Root node represents &lt;strong&gt;empty string&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Efficient for &lt;strong&gt;word lookup, autocomplete, and dictionary operations&lt;/strong&gt;.
&lt;/li&gt;
&lt;li&gt;Can store &lt;strong&gt;boolean flag&lt;/strong&gt; to mark end of a word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Core Operations&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Method/Description&lt;/th&gt;
&lt;th&gt;Time Complexity&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Insert&lt;/td&gt;
&lt;td&gt;&lt;code&gt;insert(String word)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;Add a word of length L&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Search Exact&lt;/td&gt;
&lt;td&gt;&lt;code&gt;search(String word)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;Returns true if word exists&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starts With&lt;/td&gt;
&lt;td&gt;&lt;code&gt;startsWith(String prefix)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;Returns true if any word starts with given prefix&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delete&lt;/td&gt;
&lt;td&gt;&lt;code&gt;delete(String word)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(L)&lt;/td&gt;
&lt;td&gt;Removes a word&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Traverse&lt;/td&gt;
&lt;td&gt;&lt;code&gt;traverse()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(N*L)&lt;/td&gt;
&lt;td&gt;Traverse all words stored (N words of avg length L)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Classic Use Cases &amp;amp; Problems&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use Cases:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Autocomplete / Search suggestions
&lt;/li&gt;
&lt;li&gt;Spell checking
&lt;/li&gt;
&lt;li&gt;IP routing (storing prefixes)
&lt;/li&gt;
&lt;li&gt;Dictionary / word frequency problems
&lt;/li&gt;
&lt;li&gt;Longest prefix matching
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Problems / Considerations:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;High &lt;strong&gt;memory usage&lt;/strong&gt; for large alphabets if implemented naïvely.
&lt;/li&gt;
&lt;li&gt;Insertion and search depend on &lt;strong&gt;word length&lt;/strong&gt;, not number of words.
&lt;/li&gt;
&lt;li&gt;Typically implemented with &lt;strong&gt;arrays (fixed alphabet) or HashMaps&lt;/strong&gt; for children.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;&lt;/p&gt;


&lt;pre&gt;&lt;code&gt;&lt;br&gt;
import java.util.HashMap;&lt;br&gt;
import java.util.Map;

&lt;p&gt;class TrieNode {&lt;br&gt;
    Map children = new HashMap&amp;lt;&amp;gt;();&lt;br&gt;
    boolean isEndOfWord = false;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;class Trie {&lt;br&gt;
    private TrieNode root;&lt;br&gt;
    public Trie() {&lt;br&gt;
        root = new TrieNode();&lt;br&gt;
    }&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Insert a word
public void insert(String word) {
    TrieNode node = root;
    for (char ch : word.toCharArray()) {
        node.children.putIfAbsent(ch, new TrieNode());
        node = node.children.get(ch);
    }
    node.isEndOfWord = true;
}

// Search for exact word
public boolean search(String word) {
    TrieNode node = root;
    for (char ch : word.toCharArray()) {
        node = node.children.get(ch);
        if (node == null) return false;
    }
    return node.isEndOfWord;
}

// Search for prefix
public boolean startsWith(String prefix) {
    TrieNode node = root;
    for (char ch : prefix.toCharArray()) {
        node = node.children.get(ch);
        if (node == null) return false;
    }
    return true;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;}&lt;br&gt;
&lt;code&gt;&lt;/code&gt;&lt;code&gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;
// Demo&lt;br&gt;
public class Main {&lt;br&gt;
    public static void main(String[] args) {&lt;br&gt;
        Trie trie = new Trie();&lt;br&gt;
        trie.insert("apple");&lt;br&gt;
        trie.insert("app");&lt;br&gt;
        trie.insert("banana");&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    System.out.println(trie.search("apple"));  // true
    System.out.println(trie.search("app"));    // true
    System.out.println(trie.search("appl"));   // false
    System.out.println(trie.startsWith("ap")); // true
    System.out.println(trie.startsWith("ba")); // true
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;}&lt;br&gt;
&lt;/p&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use Trie when you need efficient prefix searches or word lookups.&lt;br&gt;
Ideal for autocomplete, dictionary, spell check, and IP prefix matching.&lt;br&gt;
Efficient in search time (O(L)) but can be memory-intensive for large datasets.&lt;br&gt;
Use HashMap children for dynamic alphabets, or arrays for fixed small alphabets.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>java</category>
      <category>datastructures</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Spring Boot Testing: A Comprehensive Best Practices Guide</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Tue, 02 Sep 2025 17:12:44 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/spring-boot-testing-a-comprehensive-best-practices-guide-1do6</link>
      <guid>https://dev.to/ankitdevcode/spring-boot-testing-a-comprehensive-best-practices-guide-1do6</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;In modern software development, testing is not a one-size-fits-all activity. Different kinds of tests serve different purposes — from quickly verifying small units of code to validating entire system flows under real-world conditions.&lt;br&gt;
To design an efficient and maintainable test strategy, it’s important to understand the various types of tests, their scope, and their trade-offs in terms of speed, reliability, and coverage&lt;/p&gt;

&lt;p&gt;The table below provides an overview to helps teams balance their testing strategy according to the Test Pyramid principle — having more fast, cheap unit tests at the base, fewer integration tests in the middle, and only a handful of slow, expensive tests (E2E, performance) at the top.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The golden rule is to write lots of unit tests, some integration tests, and few end-to-end tests.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A typical ratio:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;70–80% unit tests (fast, isolated, cover edge cases)&lt;/li&gt;
&lt;li&gt;15–20% integration tests (cross-layer correctness)&lt;/li&gt;
&lt;li&gt;5–10% end-to-end (E2E) (real dependencies, full workflow)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37jqn86f1wv7qwb97m7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F37jqn86f1wv7qwb97m7d.png" alt=" " width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Testing Strategy Guidelines
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;When to Use Each Test Type&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unit Tests:&lt;/strong&gt; Use for business logic, algorithms, utility functions, and any code that can be tested in isolation. Aim for high coverage of your core business logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integration Tests:&lt;/strong&gt; Use for testing interactions between your application components, database operations, and internal service communication.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component Tests:&lt;/strong&gt; Use when you need to test a specific layer or module with some real dependencies but want faster execution than full integration tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E2E Tests:&lt;/strong&gt; Use sparingly for critical user workflows that span multiple systems. Focus on high-value scenarios that would cause significant business impact if broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Functional Tests:&lt;/strong&gt; Use to verify that features meet business requirements. Often written in collaboration with product owners and business analysts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contract Tests:&lt;/strong&gt; Use when your application communicates with other services, especially in microservices architectures where service boundaries are critical.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance Tests:&lt;/strong&gt; Use when you have specific performance requirements or when you need to understand system behavior under load.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Smoke Tests:&lt;/strong&gt; Use for quick validation after deployments or major changes. Include in your deployment pipeline for immediate feedback.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regression Tests:&lt;/strong&gt; Maintain as part of your overall test suite to prevent reintroduction of fixed bugs and ensure stable functionality.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security Tests:&lt;/strong&gt; Integrate into your development and deployment process to identify vulnerabilities early and ensure security controls function correctly.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This article highlights a few important test types, rather than covering every possible category.&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  Unit Testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Unit tests verify that individual classes and methods work correctly in isolation, without any external dependencies like databases, web services, or file systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mock External Dependencies: Replace all external dependencies with mocks or stubs to ensure tests run fast and reliably. This includes databases, HTTP clients, file systems, and third-party services.&lt;/li&gt;
&lt;li&gt;Follow the AAA Pattern: Structure tests with clear Arrange, Act, and Assert sections. Set up test data and mocks, execute the method under test, then verify the results and interactions.&lt;/li&gt;
&lt;li&gt;Test Edge Cases: Don't just test the happy path. Include tests for null inputs, empty collections, boundary values, and error conditions. These edge cases often reveal bugs that normal usage might miss.&lt;/li&gt;
&lt;li&gt;Use Descriptive Test Names: Test names should clearly describe what scenario is being tested and what outcome is expected. Avoid generic names like "testMethod1" or "shouldWork".&lt;/li&gt;
&lt;li&gt;Keep Tests Independent: Each test should be able to run independently and in any order. Avoid shared state between tests and ensure proper setup and teardown.&lt;/li&gt;
&lt;li&gt;Test One Thing at a Time: Each test should verify a single behavior or scenario. If you need multiple assertions, make sure they're all related to the same logical outcome.&lt;/li&gt;
&lt;li&gt;Use Test Data Builders: Create builder patterns for test data to make tests more readable and maintainable. This approach makes it easy to create variations of test objects.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s assume we want to write a unit test (using JUnit 5 + Mockito) for the following &lt;code&gt;UserService&lt;/code&gt; class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public String getUserName(Long id) {
        return userRepository.findById(id)
                .map(User::getName)
                .orElseThrow(() -&amp;gt; new RuntimeException("User not found"));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The class under test&lt;/strong&gt;→ &lt;code&gt;UserService&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Its dependency&lt;/strong&gt; → &lt;code&gt;UserRepository&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Without Annotations (Manual Mocks, No Extension)&lt;/strong&gt;&lt;br&gt;
No @ExtendWith needed because we’re manually creating mocks with mock().&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserServiceTest {

    private final UserRepository userRepository = mock(UserRepository.class);
    private final UserService userService = new UserService(userRepository);

    @Test
    void shouldReturnUserNameWhenUserExists() {
        when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "Alice")));

        String result = userService.getUserName(1L);

        assertEquals("Alice", result);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to Use @ExtendWith(MockitoExtension.class)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you want to use Mockito annotations like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@Mock → creates mock objects&lt;/li&gt;
&lt;li&gt;@InjectMocks → injects mocks into the class under test
then you need @ExtendWith(MockitoExtension.class) on your test class.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository userRepository;

    @InjectMocks
    private UserService userService;

    @Test
    void shouldReturnUserNameWhenUserExists() {
        when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "Alice")));

        String result = userService.getUserName(1L);

        assertEquals("Alice", result);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What is @InjectMocks?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@InjectMocks&lt;/code&gt; is a Mockito annotation that creates an instance of the class under test and automatically injects the required dependencies (the ones you marked with @Mock) into it.&lt;/p&gt;

&lt;p&gt;So instead of you manually writing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UserRepository userRepository = mock(UserRepository.class);
UserService userService = new UserService(userRepository);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can let Mockito handle it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Mock
private UserRepository userRepository;

@InjectMocks
private UserService userService;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why use @InjectMocks?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Less boilerplate — no need to manually new-up UserService.&lt;/li&gt;
&lt;li&gt;Clearer intent — it’s obvious which class is being tested (userService).&lt;/li&gt;
&lt;li&gt;Works with constructor, setter, or field injection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;👉 Follow the method naming pattern:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;should&amp;lt;ExpectedBehavior&amp;gt;When&amp;lt;Condition&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;shouldReturnUnknownWhenUserDoesNotExist&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;shouldThrowExceptionWhenUserAlreadyExists&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use @ParameterizedTest for multiple inputs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Example without ParameterizedTest&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
void shouldReturnAliceWhenIdIs1() {
    when(userRepository.findById(1L)).thenReturn(Optional.of(new User(1L, "Alice")));
    assertEquals("Alice", userService.getUserName(1L));
}

@Test
void shouldReturnUnknownWhenIdDoesNotExist() {
    when(userRepository.findById(99L)).thenReturn(Optional.empty());
    assertEquals("Unknown", userService.getUserName(99L));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Example  with ParameterizedTest&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ParameterizedTest
    @CsvSource({
        "1, Alice",    // user exists
        "2, Bob",      // user exists
        "3, Unknown"   // user does not exist
    })
    void shouldReturnUserNameOrUnknownBasedOnExistence(Long id, String expectedName) {
        // Arrange
        if (!expectedName.equals("Unknown")) {
            when(userRepository.findById(id))
                .thenReturn(Optional.of(new User(id, expectedName)));
        } else {
            when(userRepository.findById(id)).thenReturn(Optional.empty());
        }

        // Act
        String result = userService.getUserName(id);

        // Assert
        assertEquals(expectedName, result);
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Verify interactions with mocks&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
void shouldDeleteUser() {
    userService.deleteUser(1L);
    verify(userRepository, times(1)).deleteById(1L);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Test with ArgumentCaptor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The use of ArgumentCaptor in unit testing (with Mockito) is to capture arguments that are passed to mocked methods, so you can make assertions on them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Useful when you want to inspect properties of the passed object.&lt;/li&gt;
&lt;li&gt;Can capture multiple values if the method is called multiple times.&lt;/li&gt;
&lt;li&gt;Makes assertions more fine-grained than just verifying method calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Capture argument for verification&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Simple Example: please note &lt;code&gt;createUser&lt;/code&gt; doesn't return anything here.&lt;br&gt;
Without ArgumentCaptor, you can only check if save() was called.But you don’t know what object was passed. ArgumentCaptor lets you inspect the actual argument (User) and assert its fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class UserService {
    private final UserRepository repo;

    UserService(UserRepository repo) {
        this.repo = repo;
    }

    void createUser(String name) {
        User user = new User(null, name);
        repo.save(user);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; @ExtendWith(MockitoExtension.class)
class UserServiceTest {

    @Mock
    private UserRepository repo;

    @InjectMocks
    private UserService service;

    @Test
    void shouldCaptureUserPassedToRepository() {
        // Given
        ArgumentCaptor&amp;lt;User&amp;gt; captor = ArgumentCaptor.forClass(User.class);

        // When
        service.createUser("Alice");

        // Then
        Mockito.verify(repo).save(captor.capture());
        assertEquals("Alice", captor.getValue().getName()); // we can check input
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Answer / custom return behavior&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is very useful when the stubbed method needs to do something dynamic based on the input, not just return a fixed value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Basic Example (Fixed Return)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;when(repo.save(any(User.class))).thenReturn(new User(1L, "Alice"));&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Here the save() method always returns the same user, regardless of what was passed.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;thenReturn() → good for simple, static returns.&lt;/li&gt;
&lt;li&gt;thenAnswer() → powerful when return depends on input or custom logic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Using Answer for Dynamic Return&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sometimes you want the return value to depend on the argument passed. That’s where Answer comes in.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;when(repo.findById(anyLong()))
    .thenAnswer(invocation -&amp;gt; {
        Long id = invocation.getArgument(0);
        if (id == 0) throw new IllegalArgumentException("Invalid ID");
        return new User(id, "User" + id);
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When To Use Answer&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When you want to simulate DB behavior (e.g., assigning IDs).&lt;/li&gt;
&lt;li&gt;When the return should depend on input values.&lt;/li&gt;
&lt;li&gt;When you need to throw exceptions for specific inputs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Void Methods (doNothing / doThrow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Since void methods can’t be stubbed with when(...).thenReturn(...), Mockito gives us:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;doNothing() → clarify intention when void method should just pass.&lt;/li&gt;
&lt;li&gt;doThrow() → test error handling for void methods.&lt;/li&gt;
&lt;li&gt;doAnswer() → add side-effects like logging, counters, or capturing arguments.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Test
void shouldRegisterUserAndSendEmail() {
    // void method -&amp;gt; doNothing
    doNothing().when(emailService).sendWelcomeEmail(any(User.class));

    service.register("Alice");

    verify(emailService).sendWelcomeEmail(any(User.class));
}

@Test
void shouldHandleEmailFailure() {
    doThrow(new RuntimeException("SMTP error"))
        .when(emailService).sendWelcomeEmail(any(User.class));

    assertThrows(RuntimeException.class, () -&amp;gt; service.register("Bob"));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What is BDDMockito?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;BDD = Behavior-Driven Development&lt;/li&gt;
&lt;li&gt;Mockito provides BDD style methods: given(), willReturn(), willThrow(), then().&lt;/li&gt;
&lt;li&gt;The behavior is the same as standard Mockito, but it improves readability and aligns with BDD conventions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w4rvrjhcvz24kf3c1ov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w4rvrjhcvz24kf3c1ov.png" alt=" " width="620" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test in BDD Style&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    @Test
    void shouldRegisterUserAndSendEmail() {
        // Given
        User user = new User(null, "Alice", "alice@example.com");
        given(repo.save(any(User.class))).willAnswer(inv -&amp;gt; {
            User u = inv.getArgument(0);
            u.setId(101L); // mimic DB
            return u;
        });

        // When
        User saved = service.register("Alice", "alice@example.com");

        // Then
        then(repo).should().save(any(User.class));
        then(email).should().sendWelcomeEmail("alice@example.com");

        assertEquals(101L, saved.getId());
        assertEquals("Alice", saved.getName());
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Advantages of BDDMockito&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improves readability: clearly separates Given, When, Then.&lt;/li&gt;
&lt;li&gt;Aligns tests with BDD style requirements.&lt;/li&gt;
&lt;li&gt;Makes test intentions obvious for new team members.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Parallel Test Execution&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JUnit 5 supports &lt;code&gt;junit.jupiter.execution.parallel.enabled=true.&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Combine with Stateless test design (no shared mutable state).&lt;/li&gt;
&lt;li&gt;Be careful with shared resources&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Controlling Test Lifecycle with @TestInstance&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;By default, JUnit creates a new test class instance per test method (PER_METHOD).&lt;br&gt;
With @TestInstance(PER_CLASS), JUnit creates only one instance of the test class for all methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import org.junit.jupiter.api.*;

@TestInstance(TestInstance.Lifecycle.PER_CLASS)
class UserServiceIntegrationTest {

    private UserService userService;

    @BeforeAll
    void setUpAll() {
        System.out.println("Init once for the whole class");
        userService = new UserService();
    }

    @BeforeEach
    void setUpEach() {
        System.out.println("Runs before each test");
    }

    @Test
    void testCreateUser() {
        System.out.println("Test 1");
        // uses same UserService instance
    }

    @Test
    void testFindUser() {
        System.out.println("Test 2");
        // still same UserService instance
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why Use PER_CLASS&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance optimization

&lt;ul&gt;
&lt;li&gt;If your test class has expensive setup (e.g., loading Spring beans manually, external resource initialization), using a single test instance avoids redoing that work for every test method.&lt;/li&gt;
&lt;li&gt;Example: Large @BeforeEach → becomes @BeforeAll.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;State sharing between tests

&lt;ul&gt;
&lt;li&gt;With PER_CLASS, you can keep shared state across test methods (e.g., Testcontainers instance, mock server, expensive cached objects).&lt;/li&gt;
&lt;li&gt;Useful when multiple test methods need to reuse the same resource.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Cleaner lifecycle management

&lt;ul&gt;
&lt;li&gt;You can use non-static @BeforeAll and @AfterAll methods (normally they must be static in JUnit)..&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caveats&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Shared state across tests can lead to test pollution if not carefully managed.&lt;/li&gt;
&lt;li&gt;Best paired with clear teardown logic or immutable test data to avoid flaky tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Follow FIRST principles&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast → runs quickly&lt;/li&gt;
&lt;li&gt;Isolated → doesn’t depend on DB/network&lt;/li&gt;
&lt;li&gt;Repeatable → same result every run&lt;/li&gt;
&lt;li&gt;Self-validating → uses assertions, not console output&lt;/li&gt;
&lt;li&gt;Timely → written close to when code is written&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Component Testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Component testing (sometimes called &lt;strong&gt;slice testing&lt;/strong&gt;) is:&lt;br&gt;
Testing a single Spring-managed component or a set of components together without starting the full application.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Spring Boot Test Slices: Leverage annotations like WebMvcTest, DataJpaTest, and JsonTest to load only the relevant parts of the Spring context for faster test execution.&lt;/li&gt;
&lt;li&gt;Test Layer Boundaries: Focus on testing how different layers of your application interact. For example, test how controllers handle requests and responses, or how repositories interact with the database.&lt;/li&gt;
&lt;li&gt;Mock Collaborators: Mock services and components that aren't part of the slice being tested. This keeps tests focused and fast while still testing real integration points.&lt;/li&gt;
&lt;li&gt;Test Error Handling: Verify that your components properly handle and propagate errors. Test validation, exception handling, and error responses.&lt;/li&gt;
&lt;li&gt;Validate Serialization: Test JSON serialization and deserialization, especially for REST APIs. Ensure that your DTOs and entities are correctly mapped.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Slice Annotations When Possible&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Spring Boot provides slice testing annotations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@WebMvcTest   Test controllers with MockMvc, auto-configures MVC beans&lt;/li&gt;
&lt;li&gt;@DataJpaTest  Test repositories with in-memory DB (H2)&lt;/li&gt;
&lt;li&gt;@RestClientTest Test REST clients like WebClient&lt;/li&gt;
&lt;li&gt;@JsonTest Test JSON serialization/deserialization&lt;/li&gt;
&lt;li&gt;@SpringBootTest Full context (use sparingly for component integration)&lt;/li&gt;
&lt;li&gt;@TestConfiguration For custom test configurations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Mock External Dependencies&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choose the Right Test Slice: Use specific annotations like &lt;code&gt;@WebMvcTest&lt;/code&gt; for controllers, &lt;code&gt;@DataJpaTest&lt;/code&gt; for repositories, rather than always using &lt;code&gt;@SpringBootTest&lt;/code&gt; which loads the entire context.&lt;br&gt;
Mock Dependencies Properly: Use &lt;code&gt;@MockBean&lt;/code&gt; for Spring-managed beans and &lt;code&gt;@Mock&lt;/code&gt; for regular dependencies. This keeps tests fast and isolated.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Testing Controller Components&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@WebMvcTest(UserController.class)
class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Autowired
    private ObjectMapper objectMapper;

    @Test
    void shouldCreateUserAndReturnCreated() throws Exception {
        // Arrange
        CreateUserRequest request = new CreateUserRequest("john@example.com", "John Doe");
        UserResponse response = new UserResponse(1L, "john@example.com", "John Doe");

        when(userService.createUser(any(CreateUserRequest.class))).thenReturn(response);

        // Act &amp;amp; Assert
        mockMvc.perform(post("/api/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.id").value(1))
                .andExpect(jsonPath("$.email").value("john@example.com"))
                .andExpect(jsonPath("$.name").value("John Doe"));

        verify(userService).createUser(any(CreateUserRequest.class));
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing Repository Components&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For repositories: @DataJpaTest + H2 in-memory DB.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@DataJpaTest
class UserRepositoryTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldSaveAndFindUser() {
        User user = userRepository.save(new User(null, "Alice"));
        assertTrue(userRepository.findById(user.getId()).isPresent());
    }
}

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

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing Service Components&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For service layer, use @ExtendWith(MockitoExtension.class)&lt;/li&gt;
&lt;li&gt;Mock dependencies to test service logic without DB or external calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have already seen a couple of examples in the previous section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Spring Profiles for Tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Using Spring Profiles for tests is a best practice in Spring applications to isolate test configurations from production configurations.&lt;/p&gt;

&lt;p&gt;Spring Boot automatically looks for configuration files in the &lt;code&gt;src/main/resources&lt;/code&gt; and &lt;code&gt;src/test/resources&lt;/code&gt; folders.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test Configuration (&lt;code&gt;src/test/resources/application-test.yml&lt;/code&gt;)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driverClassName: org.h2.Driver
    username: sa
    password:
  jpa:
    hibernate:
      ddl-auto: create-drop
    show-sql: true
  logging:
    level:
      org.springframework.web: DEBUG
      com.yourpackage: DEBUG

app:
  cache:
    enabled: false
  external-api:
    base-url: http://localhost:8080/mock

# Disable unnecessary features in tests
management:
  endpoints:
    enabled-by-default: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Activate Test Profile in Component Tests&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Use @ActiveProfiles on your test class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ExtendWith(MockitoExtension.class)
@ActiveProfiles("test")
@WebMvcTest(UserController.class)
class UserControllerTest {
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This loads beans from application-test.yml instead of application.yml.&lt;/p&gt;

&lt;p&gt;By default, Spring Boot always loads application.yml (and then applies overrides from profile-specific files like application-local.yml). &lt;br&gt;
If you put &lt;code&gt;application-test.yml&lt;/code&gt; only under &lt;code&gt;src/test/resources&lt;/code&gt;, and not under &lt;code&gt;src/main/resources&lt;/code&gt;, then in test runs only the test file exists and Since there’s no &lt;code&gt;application.yml&lt;/code&gt; in classpath, Spring won’t load or merge it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Use Test-Specific Configuration Classes&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Only loaded when @ActiveProfiles("test") is set.&lt;/li&gt;
&lt;li&gt;Keeps test beans separate from production beans.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should create &lt;code&gt;TestConfig&lt;/code&gt; in the &lt;code&gt;src/test&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
@Profile("test")
public class TestConfig {

    @Bean
    public EmailService emailService() {
        // return a mock or dummy email service for tests
        return Mockito.mock(EmailService.class);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can be imported in your test classes with &lt;code&gt;@Import(TestConfig.class)&lt;/code&gt; if needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@WebMvcTest(UserController.class)
@Import(TestConfig.class)
class UserControllerTest {

    @Autowired
    private EmailService emailService; // comes from TestConfig
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Use @TestConfiguration for Inline Test Beans&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Spring Boot provides @TestConfiguration for test-only bean definitions:&lt;br&gt;
This keeps test beans scoped to this test class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@WebMvcTest(UserController.class)
class UserControllerTest {

    @TestConfiguration
    static class Config {
        @Bean
        public EmailService emailService() {
            return Mockito.mock(EmailService.class);
        }
    }

    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private UserService userService;

    @Autowired
    private EmailService emailService;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;When to Use Spring Profiles for Tests&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Separate Test-Specific Configuration&lt;/li&gt;
&lt;li&gt;Use a dedicated profile (e.g., test) to load beans, properties, or configurations that are only relevant during testing.&lt;/li&gt;
&lt;li&gt;Example: different database (H2 in-memory DB) for tests instead of production DB.&lt;/li&gt;
&lt;li&gt;Avoid Interference with Production Configuration&lt;/li&gt;
&lt;li&gt;Running tests with a separate profile prevents accidental use of production services or endpoints.&lt;/li&gt;
&lt;li&gt;You can define test-specific beans that mock external services (e.g., payment gateway, email service) under the test profile.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Integration Testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Integration tests verify that multiple components work together correctly, including interactions between services, databases, and external systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Integration Tests Matter&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bridging the Gap: Unit tests validate logic, but only integration tests confirm that layers (controller ↔ service ↔ repo ↔ DB) actually work together.&lt;/li&gt;
&lt;li&gt;Catch Environment Gaps: Many bugs in production come from misconfigured beans, serialization issues, or DB quirks (H2 ≠ Postgres!) that unit tests miss.&lt;/li&gt;
&lt;li&gt;CI/CD Gatekeeper: In most mature teams, integration tests run on every pull request → preventing broken builds from merging.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use Real Databases When Possible: While H2 in-memory databases are fast, they don't always behave like production databases. Consider using containers with real database engines for more accurate testing.&lt;/li&gt;
&lt;li&gt;Test Actual Data Flow: Verify that data correctly flows through your application layers. Test that controllers properly call services, services interact with repositories, and data is correctly persisted and retrieved.&lt;/li&gt;
&lt;li&gt;Isolate External Dependencies: Use test doubles for external services while keeping internal components real. This provides confidence in your integration while maintaining test reliability.&lt;/li&gt;
&lt;li&gt;Use Transaction Rollback: Configure tests to rollback database transactions after each test to maintain clean state between tests.&lt;/li&gt;
&lt;li&gt;Test Configuration: Verify that your Spring configuration works correctly by testing that beans are properly wired and configurations are applied.&lt;/li&gt;
&lt;li&gt;Focus on Critical Paths: Integration tests are more expensive than unit tests, so focus on testing the most critical business workflows and integration points.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use @SpringBootTest Wisely&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@SpringBootTest loads the full context (all beans).&lt;/li&gt;
&lt;li&gt;Useful when testing end-to-end within the app.&lt;/li&gt;
&lt;li&gt;But it’s heavy → avoid for every test class.&lt;/li&gt;
&lt;li&gt;Prefer narrower slices (@WebMvcTest, @DataJpaTest) when possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;** Choose the Right Testing Strategy based on use case**&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class UserIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldCreateUserSuccessfully() {
        // Test full request flow
        CreateUserRequest request = new CreateUserRequest("john@example.com", "John");

        ResponseEntity&amp;lt;User&amp;gt; response = restTemplate.postForEntity(
            "/api/users", request, User.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);
        assertThat(userRepository.findByEmail("john@example.com")).isPresent();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;What @SpringBootTest Does&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Loads the entire Spring ApplicationContext (all beans, configs, filters, interceptors, DB connections, etc.).&lt;br&gt;
Boots your app in an embedded container (Tomcat/Jetty/Undertow, depending on your setup).&lt;br&gt;
You can then hit your controller endpoints just like a real client would.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Profiles for Testing&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As discussed in detail in the previous section.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep application-test.yml with test-specific configs and separate profile i.e &lt;code&gt;@ActiveProfiles("integration-test")&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Example: lower logging level, test DB credentials, disable real integrations (e.g., email).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Performance and Optimization&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;integration tests in Spring Boot can easily become slow if not optimized. Let’s talk about performance best practices with focus on context reuse and related tricks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context Reuse (Spring TestContext Framework)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By default, Spring caches the application context between test classes, so it doesn’t restart for every test.&lt;/li&gt;
&lt;li&gt;This is one of the biggest optimizations: context startup is the slowest part of integration testing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How Context Caching Works in Spring Boot Tests&lt;/strong&gt;&lt;br&gt;
Spring Boot optimizes test execution by caching the application context when possible. This avoids expensive reinitialization and speeds up test suites.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dnnmnx71mumie2mfrzw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3dnnmnx71mumie2mfrzw.png" alt=" " width="551" height="139"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reusing Application Context using Base class&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; // Base class to share context
@SpringBootTest
@Testcontainers
abstract class BaseIntegrationTest {

}

// Concrete test classes extend base class
class UserIntegrationTest extends BaseIntegrationTest {
    // Test methods
}

class OrderIntegrationTest extends BaseIntegrationTest {
    // Test methods
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Best Practices:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Group tests by profile to maximize reuse.&lt;/li&gt;
&lt;li&gt;Use @DirtiesContext only when necessary—it forces context reload.&lt;/li&gt;
&lt;li&gt;Avoid unnecessary profile changes unless isolation is required.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Using MockMvc inside @SpringBootTest (mock servlet layer, faster)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can also inject MockMvc when using @SpringBootTest.&lt;br&gt;
This does not use a real HTTP server but still goes through Spring MVC dispatching.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk48wtc5n2c2aj3ox6x4v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk48wtc5n2c2aj3ox6x4v.png" alt=" " width="800" height="196"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So with @SpringBootTest, you’re not limited — you can test controllers via MockMvc (mock HTTP) or TestRestTemplate (real HTTP).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two directions of HTTP in Spring Boot&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inbound HTTP calls → when clients call your service.

&lt;ul&gt;
&lt;li&gt;Example: curl &lt;a href="http://my-service/api/users/1" rel="noopener noreferrer"&gt;http://my-service/api/users/1&lt;/a&gt; hits your controller.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Outbound HTTP calls → when your service calls another service.

&lt;ul&gt;
&lt;li&gt;Example: Your UserService calls &lt;a href="http://orders-service/api/orders/123" rel="noopener noreferrer"&gt;http://orders-service/api/orders/123&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Which tool is for which direction?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuc6s541oel2590pv85j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvuc6s541oel2590pv85j.png" alt=" " width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inbound (your service)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test /api/users/1 returns JSON.&lt;/li&gt;
&lt;li&gt;Use MockMvc (fast, no real server) OR TestRestTemplate (full stack, real HTTP).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Outbound (your service calls another)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest
class ExternalServiceIntegrationTest {

    private MockWebServer mockWebServer;

    @BeforeEach
    void setUp() throws IOException {
        mockWebServer = new MockWebServer();
        mockWebServer.start();
    }

    @AfterEach
    void tearDown() throws IOException {
        mockWebServer.shutdown();
    }

    @Test
    void shouldHandleExternalServiceResponse() {
        // Given
        mockWebServer.enqueue(new MockResponse()
            .setBody("{\"status\":\"success\"}")
            .addHeader("Content-Type", "application/json"));

        String baseUrl = String.format("http://localhost:%s", mockWebServer.getPort());

        // When
        ExternalServiceResponse response = externalService.callService(baseUrl);

        // Then
        assertThat(response.getStatus()).isEqualTo("success");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your service thinks it called a real API.&lt;/li&gt;
&lt;li&gt;But actually, MockWebServer intercepted and returned your fake response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;WireMock for Complex External APIs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;WireMock is like a "supercharged" version of MockWebServer.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Testing outbound HTTP calls (your app → external services)&lt;/li&gt;
&lt;li&gt;Just like MockWebServer, you don’t want your tests to depend on real APIs.&lt;/li&gt;
&lt;li&gt;WireMock lets you stub those APIs so your service thinks it’s calling the real thing.&lt;/li&gt;
&lt;li&gt;WireMock shines when the mocked responses are not just static.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest
class PaymentServiceIntegrationTest {

    @RegisterExtension
    static WireMockExtension wireMock = WireMockExtension.newInstance()
            .options(wireMockConfig().port(8089))
            .build();

    @Test
    void shouldProcessPaymentSuccessfully() {
        // Given
        wireMock.stubFor(post(urlEqualTo("/api/payments"))
            .willReturn(aResponse()
                .withStatus(200)
                .withHeader("Content-Type", "application/json")
                .withBody("{\"transactionId\":\"123\",\"status\":\"approved\"}")));

        // When
        PaymentResult result = paymentService.processPayment(100.0);

        // Then
        assertThat(result.isSuccessful()).isTrue();
        assertThat(result.getTransactionId()).isEqualTo("123");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Features WireMock adds (compared to MockWebServer):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Request matching (by URL, headers, body, query params).&lt;/li&gt;
&lt;li&gt;Dynamic responses (different responses for same endpoint depending on input).&lt;/li&gt;
&lt;li&gt;Fault injection (timeouts, connection reset → great for resilience testing).&lt;/li&gt;
&lt;li&gt;Scenarios (simulate stateful APIs, e.g., first call = pending, second call = success).&lt;/li&gt;
&lt;li&gt;Recording &amp;amp; playback (record real API responses and replay them).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv329s80f13t4ipqs5pjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv329s80f13t4ipqs5pjy.png" alt=" " width="605" height="269"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Use MockWebServer if you just need lightweight static mocks.&lt;/li&gt;
&lt;li&gt;Use WireMock when your tests need realistic API simulation (matching, state, errors).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Testing Security&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class SecurityIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void shouldRequireAuthenticationForProtectedEndpoints() {
        ResponseEntity&amp;lt;String&amp;gt; response = restTemplate.getForEntity(
            "/api/users/profile", String.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.UNAUTHORIZED);
    }

    @Test
    @WithMockUser(roles = "USER")
    void shouldAllowAccessWithValidRole() {
        ResponseEntity&amp;lt;UserProfile&amp;gt; response = restTemplate.getForEntity(
            "/api/users/profile", UserProfile.class);

        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testing with TestContainers (Database Integration)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Test containers ?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It uses Docker containers to spin up temporary instances of real services.&lt;/li&gt;
&lt;li&gt;You can use it in JUnit 5, JUnit 4, and Spock tests.&lt;/li&gt;
&lt;li&gt;Containers are started before tests and stopped after tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why use Testcontainers?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mocks and in-memory DBs (like H2) are not 100% identical to real services.&lt;/li&gt;
&lt;li&gt;Testcontainers gives you production-like testing environments.&lt;/li&gt;
&lt;li&gt;Makes integration tests reliable and closer to reality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test with PostgreSQL Container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest
@Testcontainers
class UserServiceIntegrationTest {

    @Container
    static PostgreSQLContainer&amp;lt;?&amp;gt; postgres = new PostgreSQLContainer&amp;lt;&amp;gt;("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
    }

    @Autowired
    private UserService userService;

    @Autowired
    private UserRepository userRepository;

    @Test
    void shouldPersistUserToDatabase() {
        // Arrange
        CreateUserRequest request = new CreateUserRequest("integration@example.com", "Integration Test");

        // Act
        UserResponse result = userService.createUser(request);

        // Assert
        assertThat(result.getId()).isNotNull();

        Optional&amp;lt;User&amp;gt; savedUser = userRepository.findById(result.getId());
        assertThat(savedUser).isPresent();
        assertThat(savedUser.get().getEmail()).isEqualTo("integration@example.com");
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;ul&gt;
&lt;li&gt;Docker Dependency- Docker must be installed and running on your machine (or CI/CD runner).&lt;/li&gt;
&lt;li&gt;Slower Test Execution&lt;/li&gt;
&lt;li&gt;Heavier Resource Usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not always necessary (sometimes mocks/in-memory are enough).&lt;br&gt;
When you need confidence that your application works correctly with the real dependencies (DBs, brokers, caches, search engines), Testcontainers is the most reliable way to test.&lt;/p&gt;

&lt;p&gt;It hits the sweet spot between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mocks/in-memory (too fake → risk of prod bugs).&lt;/li&gt;
&lt;li&gt;E2E with full infra (too heavy → slow &amp;amp; fragile).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In short:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your app depends on anything external (Postgres, Kafka, Redis, etc.), Testcontainers is the best balance of speed, realism, and maintainability.&lt;/p&gt;


&lt;h2&gt;
  
  
  End-to-End Testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E2E (End-to-End) testing is where you validate the entire Spring Boot application stack, often including databases, messaging, external APIs, and even UI (if applicable). These tests are slower but high value because they simulate production usage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integration tests ask:&lt;/strong&gt; "Do my components work together correctly?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E2E tests ask:&lt;/strong&gt; "Does the whole system behave correctly from the outside?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Test Critical User Journeys: Focus on the most important user workflows that directly impact business value. Don't try to test every possible path through your application.&lt;/li&gt;
&lt;li&gt;Use Real Infrastructure: Test against production-like environments with real databases, message queues, and external services when possible.&lt;/li&gt;
&lt;li&gt;Implement Proper Setup and Teardown: Ensure tests start with a known state and clean up after themselves. Use database migrations, test data scripts, and proper cleanup procedures.&lt;/li&gt;
&lt;li&gt;Handle Asynchronous Operations: Many real applications involve asynchronous processing. Use appropriate waiting strategies and timeouts to handle eventual consistency.&lt;/li&gt;
&lt;li&gt;Keep Tests Stable: E2E tests are prone to flakiness. Use robust waiting strategies, proper timeouts, and retry mechanisms for operations that might be timing-dependent.&lt;/li&gt;
&lt;li&gt;Run in CI/CD Pipeline: Automate E2E tests as part of your deployment pipeline, but consider running them less frequently than unit and integration tests due to their cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Test Only Critical Paths&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t aim for 100% E2E coverage — it’s slow, brittle, and expensive.&lt;/li&gt;
&lt;li&gt;Focus on happy paths and business-critical flows (e.g., “user signup → email sent → user can log in”).&lt;/li&gt;
&lt;li&gt;Leave edge cases to unit/integration tests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Real Dependencies Where Feasible&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefer Testcontainers for real databases (Postgres, MySQL, Mongo, Kafka, Redis, etc.).&lt;/li&gt;
&lt;li&gt;This ensures tests are realistic and not tied to in-memory substitutes like H2 (which can behave differently).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Run on Random Ports&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use webEnvironment = RANDOM_PORT with @SpringBootTest so tests don’t conflict in CI/CD when run in parallel.&lt;/li&gt;
&lt;li&gt;Access app endpoints via TestRestTemplate or WebTestClient.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Stub External Services&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Don’t hit real third-party APIs in E2E tests.&lt;/li&gt;
&lt;li&gt;Use WireMock or MockWebServer to mock external APIs.&lt;/li&gt;
&lt;li&gt;This isolates your tests while still simulating realistic HTTP responses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scenario&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We’re building a user registration flow:&lt;/li&gt;
&lt;li&gt;User sends a POST /api/register request.&lt;/li&gt;
&lt;li&gt;App stores user in the real database (H2/Postgres via Testcontainers).&lt;/li&gt;
&lt;li&gt;App calls an external email service to send a welcome email.&lt;/li&gt;
&lt;li&gt;Returns 201 Created if everything succeeds.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Technologies in Test&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;@SpringBootTest(webEnvironment = RANDOM_PORT) → run full app with real HTTP layer.&lt;/li&gt;
&lt;li&gt;TestRestTemplate → to call our real API.&lt;/li&gt;
&lt;li&gt;WireMock → mock the external email service.&lt;/li&gt;
&lt;li&gt;Testcontainers (optional) → real DB, no mocks.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@AutoConfigureWireMock(port = 0) // WireMock on random port
@Testcontainers
class UserRegistrationE2ETest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Container
    static PostgreSQLContainer&amp;lt;?&amp;gt; postgres =
        new PostgreSQLContainer&amp;lt;&amp;gt;("postgres:15")
            .withDatabaseName("testdb")
            .withUsername("test")
            .withPassword("test");

    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
        registry.add("email.api.url", () -&amp;gt; "http://localhost:" + WireMockServerRunner.port());
    }

    @Test
    void shouldRegisterUserAndSendWelcomeEmail() {
        // Stub external Email API
        stubFor(post("/send-email")
                .willReturn(aResponse()
                    .withStatus(200)
                    .withBody("{\"status\":\"sent\"}")));

        // Call real API endpoint
        UserRequest newUser = new UserRequest("alice", "password123", "alice@example.com");
        ResponseEntity&amp;lt;String&amp;gt; response = restTemplate
                .postForEntity("/api/register", newUser, String.class);

        // Assert response
        assertThat(response.getStatusCode()).isEqualTo(HttpStatus.CREATED);

        // Verify Email API was called with expected payload
        verify(postRequestedFor(urlEqualTo("/send-email"))
                .withRequestBody(matchingJsonPath("$.to", equalTo("alice@example.com"))));
    }
}

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

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;What This Test Covers&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real HTTP call → TestRestTemplate hits POST /api/register.&lt;/li&gt;
&lt;li&gt;Real DB interaction → user stored in Postgres (via Testcontainers).&lt;/li&gt;
&lt;li&gt;External dependency → email API is faked by WireMock, so test doesn’t hit the real email server.&lt;/li&gt;
&lt;li&gt;End-to-End flow validated → request travels full stack: controller → service → repo → DB → external call → response.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Alternatives to TestRestTemplate&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;WebTestClient (Reactive-style, but works in Servlet apps too)&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Originally built for Spring WebFlux, but works with Spring MVC too.&lt;/li&gt;
&lt;li&gt;Provides a fluent API that’s easier to chain and assert.&lt;/li&gt;
&lt;li&gt;Can test reactive responses (e.g., streaming JSON).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RestAssured&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;A third-party library popular for API testing.&lt;/li&gt;
&lt;li&gt;Syntax is very expressive and close to BDD (Given/When/Then).&lt;/li&gt;
&lt;li&gt;Good for contract tests and multi-service E2E testing.&lt;/li&gt;
&lt;li&gt;Downside: needs app running on a real port (not just mock HTTP layer).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Contract Testing
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Purpose&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Contract tests ensure that services can communicate correctly by verifying that the producer provides what the consumer expects.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Best Practices&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define Clear Contracts: Establish explicit contracts between services that specify request and response formats, error codes, and behavior expectations.&lt;/li&gt;
&lt;li&gt;Test Producer and Consumer Sides: Verify that the service producing data matches the contract, and that consuming services can properly handle the provided data.&lt;/li&gt;
&lt;li&gt;Version Your Contracts: When contracts change, ensure backward compatibility or coordinate changes between teams. Use semantic versioning for API changes.&lt;/li&gt;
&lt;li&gt;Automate Contract Verification: Run contract tests automatically when either producer or consumer code changes to catch breaking changes early.&lt;/li&gt;
&lt;li&gt;Use Schema Validation: Validate that JSON schemas, API specifications, and message formats are correctly implemented by both sides of the contract.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;What is Contract Testing?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A way to ensure compatibility between a service provider (e.g., UserService API) and its consumer (e.g., OrderService that calls UserService).&lt;/p&gt;

&lt;p&gt;Instead of running heavy E2E tests across multiple services, contract testing verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The provider returns responses that match what the consumer expects.&lt;/li&gt;
&lt;li&gt;The consumer sends requests that the provider can understand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why It’s Needed&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microservices scale problem – E2E tests get slow and brittle as the number of services grows.&lt;/li&gt;
&lt;li&gt;Independent deployments – teams should be able to deploy services without waiting on each other.&lt;/li&gt;
&lt;li&gt;Shift-left testing – catch breaking API changes early, during CI/CD, before hitting staging or prod.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Use Consumer-Driven Contracts (CDC)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Consumers define expectations, providers validate against them.&lt;/li&gt;
&lt;li&gt;Ensures consumers never get surprises after provider changes.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Adopt a Contract Testing Framework&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Spring Cloud Contract (most popular in Spring Boot).&lt;/li&gt;
&lt;li&gt;Others: Pact (language agnostic, widely adopted).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Keep Contracts in Version Control&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store contracts (JSON/YAML/Groovy DSL) in the same repo as the provider OR shared contract repo.&lt;/li&gt;
&lt;li&gt;Use them in CI/CD pipelines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Version Your Contracts&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Treat them like APIs. Breaking changes should follow semantic versioning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Real example of Pact contract testing with Spring Boot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine a retail platform with microservices:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order Service (Consumer) → Places an order, needs user details.&lt;/li&gt;
&lt;li&gt;User Service (Provider) → Manages users, exposes /api/users/{id}.&lt;/li&gt;
&lt;li&gt;Pact Broker → Central hub where contract files are shared&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Pact Test (Consumer-Driven Contract)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@ExtendWith(PactConsumerTestExt.class)
public class OrderServicePactTest {

    @Pact(consumer = "order-service", provider = "user-service")
    public RequestResponsePact createPact(PactDslWithProvider builder) {
        return builder
            .given("User with ID 100 exists")
            .uponReceiving("Request to fetch user 100")
                .path("/api/users/100")
                .method("GET")
            .willRespondWith()
                .status(200)
                .headers(Map.of("Content-Type", "application/json"))
                .body("{\"id\":100,\"name\":\"Alice\"}")
            .toPact();
    }

    @Test
    void verifyUserApi(MockServer mockServer) throws IOException {
        URL url = new URL(mockServer.getUrl() + "/api/users/100");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("GET");

        String response = new BufferedReader(new InputStreamReader(conn.getInputStream()))
                .lines().collect(Collectors.joining("\n"));

        assertTrue(response.contains("Alice"));
    }
}

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

&lt;/div&gt;



&lt;p&gt;This generates a Pact contract:&lt;br&gt;
&lt;code&gt;order-service-user-service.json&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Defines request expectations (path, method, headers).&lt;/li&gt;
&lt;li&gt;Defines response expectations (status, JSON body).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Industry Practice&lt;/p&gt;

&lt;p&gt;Pact file is automatically uploaded to a Pact Broker via CI (GitHub Actions, Jenkins, GitLab).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Provider Side (User Service)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Provider("user-service")
@PactBroker(host = "pact-broker.mycompany.com", port = "80")
public class UserServiceContractTest {

    @LocalServerPort
    private int port;

    @BeforeEach
    void setup(PactVerificationContext context) {
        context.setTarget(new HttpTestTarget("localhost", port));
    }

    @TestTemplate
    @ExtendWith(PactVerificationInvocationContextProvider.class)
    void verifyPacts(PactVerificationContext context) {
        context.verifyInteraction();
    }
}

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

&lt;/div&gt;



&lt;p&gt;This does not use a mock server. It runs against the real Spring Boot app and verifies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API paths&lt;/li&gt;
&lt;li&gt;Request/response schema&lt;/li&gt;
&lt;li&gt;Status codes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If provider response changes (e.g., name → fullName), the verification fails → contract broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD Flow&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;🔹 Consumer pipeline (Order Service)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run Pact tests → generate pact file.&lt;/li&gt;
&lt;li&gt;Publish pact to Pact Broker.&lt;/li&gt;
&lt;li&gt;Tag it with branch/environment (dev, staging, prod).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;🔹 Provider pipeline (User Service)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fetch contract from Pact Broker.&lt;/li&gt;
&lt;li&gt;Run Pact verification tests against real app.&lt;/li&gt;
&lt;li&gt;If passes ✅ → Provider can be deployed.&lt;/li&gt;
&lt;li&gt;If fails ❌ → Provider cannot break consumer expectations.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;References &amp;amp; Credits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;AI tools were used to assist in research and writing but final content was reviewed and verified by the author.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>unittest</category>
      <category>integrationtest</category>
      <category>testing</category>
    </item>
    <item>
      <title>Parking Lot System Design (LLD in Action)</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Sun, 31 Aug 2025 15:21:15 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/parking-lot-system-design-lld-in-action-part-1-6f0</link>
      <guid>https://dev.to/ankitdevcode/parking-lot-system-design-lld-in-action-part-1-6f0</guid>
      <description>&lt;h2&gt;
  
  
  What is Low-Level Design (LLD)?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Low-Level Design (LLD)&lt;/strong&gt; is the process of translating High-Level Design (HLD) into concrete implementation (like class diagrams, interfaces, object relationships, and design patterns) that can be directly implemented in code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Components of LLD
&lt;/h2&gt;

&lt;p&gt;You can find a more in-depth information of this concept in my earlier post &lt;a href="https://dev.to/jungledev/object-oriented-programming-systemoops-3eip"&gt;Object-oriented programming System(OOPs)&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Classes and Objects&lt;/li&gt;
&lt;li&gt;Interfaces and Abstractions&lt;/li&gt;
&lt;li&gt;Relationships Between Classes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Key Considerations for LLD
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Scope &amp;amp; Requirements&lt;/li&gt;
&lt;li&gt;Clear Data Models&lt;/li&gt;
&lt;li&gt;Core Design Principles (SOLID &amp;amp; Beyond)&lt;/li&gt;
&lt;li&gt;System Robustness &amp;amp; Scalability&lt;/li&gt;
&lt;li&gt;Design Patterns &amp;amp; Reusability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Pitfalls to Avoid
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;God classes that handle too many responsibilities.&lt;/li&gt;
&lt;li&gt;Inconsistent Data Models&lt;/li&gt;
&lt;li&gt;Overuse of inheritance, leading to fragile hierarchies.&lt;/li&gt;
&lt;li&gt;Ignoring non-functional requirements until it’s too late.&lt;/li&gt;
&lt;li&gt;Overengineering: Don’t use complex patterns unless justified—keep it lean.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Parking Lot System
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Problem Statement&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Design a parking lot management system for a multi-story parking garage that can handle different types of vehicles, dynamic pricing, real-time availability tracking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Focus:&lt;/strong&gt; Low-Level Design (LLD), Object-Oriented Programming, System Architecture&lt;/p&gt;

&lt;h3&gt;
  
  
  Essential Questions to Ask:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;What types of vehicles should the system support?&lt;/li&gt;
&lt;li&gt;How many floors will the parking lot have?&lt;/li&gt;
&lt;li&gt;What are the different parking spot sizes?&lt;/li&gt;
&lt;li&gt;How should pricing work - hourly, flat rate, or both?&lt;/li&gt;
&lt;li&gt;Do we need real-time availability tracking?&lt;/li&gt;
&lt;li&gt;Should the system handle reservations?&lt;/li&gt;
&lt;li&gt;What payment methods are supported?&lt;/li&gt;
&lt;li&gt;Do we need administrative features?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;❌ Red Flags - Poor Requirements Gathering&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jumping straight to code without understanding requirements&lt;/li&gt;
&lt;li&gt;Making assumptions without asking questions&lt;/li&gt;
&lt;li&gt;Not clarifying scope - building too much or too little&lt;/li&gt;
&lt;li&gt;Ignoring edge cases early in the discussion&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  How to approach the problem statement ?
&lt;/h2&gt;

&lt;p&gt;When approaching system design, there are two common strategies:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Top-down approach&lt;/strong&gt; → Start with the big picture. Break the larger problem into smaller, manageable parts, and then continue refining each part step by step.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended during the analysis and design phase.&lt;/li&gt;
&lt;li&gt;Starts with the big picture of the system.&lt;/li&gt;
&lt;li&gt;Breaks the system into smaller, manageable sub-systems step by step.&lt;/li&gt;
&lt;li&gt;Focus is on defining responsibilities and interactions at higher levels before going into details.&lt;/li&gt;
&lt;li&gt;Useful for requirements gathering and system architecture design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Bottom-up approach&lt;/strong&gt; → Start small. Focus first on solving the smaller, individual problems, and then integrate them together to form the complete solution.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recommended during the implementation phase.&lt;/li&gt;
&lt;li&gt;Uses structure diagrams (e.g., class diagrams) to represent system components.&lt;/li&gt;
&lt;li&gt;Focuses on breaking the problem statement into smaller components without deep details initially.&lt;/li&gt;
&lt;li&gt;Behavior diagrams (e.g., sequence diagrams) are used alongside to show how the system functions overall.&lt;/li&gt;
&lt;li&gt;Helps in understanding both the structure and the functionality of the system in its early stages.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Good Approach - Identify Core Components
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------+       1..* +---------------------+       1..* +---------------------+
|      ParkingLot     |&amp;lt;&amp;gt;----------|    ParkingFloor     |&amp;lt;&amp;gt;----------|    ParkingSpot      |
+---------------------+            +---------------------+            +---------------------+
| - id: String        |            | - floorNumber: int  |            | - spotId: String    |
| - levels: List      |            | - spots: Map        |            | - isOccupied: bool  |
| - gates: Map        |            | - freeSpots: Map    |            | - vehicle: Vehicle  |
| - tickets: Map      |            +---------------------+            | - type: SpotType    |
+---------------------+            | + findSpot(vType)   |            +---------------------+
| + parkVehicle()     |            | + parkVehicle(v)    |                  |
| + unparkVehicle()   |            | + unparkVehicle(t)  |                  | 1
| + processPayment()  |            | + getFreeSpots()    |                  |
+---------------------+            | + notifyObserver()  |                  |
                                   +---------------------+                  |
                                                                            | 1..*
+-----------------+     +-----------------+     +-----------------+         |
|      Vehicle    |     |   ParkingTicket |     |      Payment    |         |
+-----------------+     +-----------------+     +-----------------+         |
| - license: String |   | - ticketId: int |     | - amount: double|         |
| - type: VehicleType | | - entryTime: Date |   | - method: String|         |
+-----------------+     | - spot: ParkingSpot|  | - status: String|         |
| + getType()     |     +-----------------+     +-----------------+         |
+-----------------+     | + getDuration() |                                 |
                        +-----------------+                                 |
                                                                            |
                                                                            |
                                                                            |
+------------------+     +-----------------------+                          |
|   PaymentProcessor|&amp;lt;-----|  DefaultPaymentProcessor|                      |
+------------------+     +-----------------------+                          |
| + process() |                                                             |
+------------------+                                                        |
                                                                            |
+------------------+     +-----------------------+                          |
|   PricingStrategy|&amp;lt;-----|  HourlyPricingStrategy|                         |
+------------------+     +-----------------------+                          |
| + calculateFee() |                                                        |
+------------------+                                                        |
                                                                            |
+------------------+                                                        |
|  ParkingObserver |&amp;lt;------------------------------------------------------+
+------------------+
| + update()       |
+------------------+ 

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

&lt;/div&gt;






&lt;p&gt;&lt;strong&gt;Parking Lot LLD – Component Summary&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. ParkingLot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The central manager of the entire parking system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Responsibilities:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps track of multiple floors.&lt;/li&gt;
&lt;li&gt;Handles parking and unparking.&lt;/li&gt;
&lt;li&gt;Interacts with Payment and PricingStrategy.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key Methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;parkVehicle(vehicle):&lt;/code&gt; Finds an available spot and issues a ParkingTicket&lt;br&gt;
&lt;code&gt;unparkVehicle(ticket):&lt;/code&gt; Frees the spot and processes payment&lt;br&gt;
&lt;code&gt;processPayment(ticket, paymentMethod):&lt;/code&gt; Calculates fee and completes payment&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. ParkingFloor&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Manages spots on a single floor.&lt;/p&gt;

&lt;p&gt;Responsibilities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keeps track of all parking spots on the floor.&lt;/li&gt;
&lt;li&gt;Notifies observers when a spot becomes occupied or free.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Key Methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;notifyObserver():&lt;/code&gt; Updates DisplayBoard or other systems&lt;br&gt;
&lt;code&gt;getAvailableSpot(vehicleType):&lt;/code&gt; Returns a free spot suitable for the vehicle&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. ParkingSpot&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Represents a single parking slot.&lt;/p&gt;

&lt;p&gt;Attributes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;spotNumber, spotType (COMPACT, LARGE, ELECTRIC, HANDICAPPED), isOccupied, vehicle&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;assignVehicle(vehicle)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;removeVehicle()&lt;/code&gt;&lt;br&gt;
&lt;code&gt;isOccupied()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. ParkingTicket&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tracks a vehicle’s parking session.&lt;/p&gt;

&lt;p&gt;Attributes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ticketId, vehicle, spot, entryTime, exitTime&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;getDuration(): Time elapsed since entry&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Payment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Handles payment processing.&lt;/p&gt;

&lt;p&gt;Attributes:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;amount, method (CASH, CARD), status (PENDING, COMPLETED)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Methods:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;process(): Completes the payment&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;6. PricingStrategy&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Defines how fees are calculated.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;calculateFee(duration): returns fee&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;HourlyPricingStrategy, FlatRatePricingStrategy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;7. ParkingObserver&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Used to update displays or other systems when a spot changes.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;update(): Called when spot becomes free/occupied&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example&lt;/strong&gt;: DisplayBoard implements ParkingObserver to show available spots.&lt;/p&gt;


&lt;h3&gt;
  
  
  UML Diagram Analysis - Parking Lot System
&lt;/h3&gt;

&lt;p&gt;The UML class diagram illustrates a comprehensive parking lot management system that demonstrates multiple design patterns and object-oriented principles. Let me break down each section:&lt;/p&gt;

&lt;p&gt;Please find full diagram on &lt;a href="https://kroki.io/mermaid/svg/eNrNWdtu4zYQfe9XCCkWkDd2gL4awQKKbG8MyLFhaxfYp4CRaEeILAqUnNbdNt_eIXUbXmQb2yLbPBjQcGZ4OHM4HDJRSopikpAdJ_tfHPj78MGZZod9IT8iMep8pc9JlNLwmFPnu5SLv9tbCnqUkzJh2adPrXyxDJdr_5sfTFuRv1ysPD9svwNv_bkbnQZTP1zPfSn4W_6iyVeEvyTZbpOz8l0AiL9772Ey973VajppZdc7Wm6SP6k7cJKsPIt1U5LyUFyA1vvqzQPvDoFd-v6X1RxNvZ5upuuvSLD8Ej4uZ4_L9WS67kNy3NPschSrKaz44bMSsGAaojlnAFPBNPvyMKkFfdMvaPnM4gum973Nffexnk7m4aPvrbvpJtM7XbRY3gGix5X3bTF9CHUcQGPvqSg5icqGv44vsJm8VvCR2giB-zVNIpoVdJWSko6dTckhx91oCawc4z3SDUUsZdywuK51XZvfoWP4GzqKn4FCyQD5AGrqU4GGcAEjNoBi2Be-LZYRyWZJOc8Emd0CfsaY3oOPzhNjKSWZJfA-yyJOS6oGnuLQL1jJeHRUo3_dSXti0xeIM2D7sFZYfMIxCPh8z9lDfohe8PxS8J4IpimNwGGkxQGJ3wcNMKdWc4Seva4ihCPheh4b-2tU7SDt4OiGtykTsJsqLmWc_a5JoE49UV2tkPV0bBb6TiWHIRrXzG_3cTeesuhl7KwplEdOsjKAzy5yyK-rrq6pC9qqhk63mqHTrGLoIPQoMUnhvZIkJU-pqAg4DXJYQG9q06u2AIs6p3v2ShuDgbFUeWLKRZwsTH15kuYyuKqSFvDrolXrzc7AeWVJfIJvMxFEk3BSjBkng_1gZQZMVtwdQ5mkBcnfjEQFSVFi6dubYS3SLWyblGPtTpk0ORTyome2eVbSHeVvGvbgAvbJVbv6UhGNSBzrRNV2eBtuqb9NstjDoBt2hfpRpyS6c3DIYyg8iovC7dkPSVGLxg1jdTjAq0tcWfs8yRnZ2zibY1HSvdn0YL7klchWpMieHTKIW8wOT7g-7GXPNFZbKFsFQg0eKn3JnoLKPh87kFuSTiBuIchwkqWha0AbOiqkoWOFgniQcxbRomg8WkoEhHrVzGMvAp6cE8a0OGi7X1-sfRuHSfRCLQdHJceJKaXElpeTzd4InCa7jMYbnfOdithaRxHzvhSM6B9JeVKhzk0b-W4EKnhUJq90bMZaWaurL3Do2M9wc_Oic5zt8xT6uHrU1WFZNlZYT2tJdgPdPdUAgBHA2x1hirKkPHO2UIBX0IU0ruq8VpJWG3fwsGsp35KIohYeWpI0OoiVC0vq9uVo6PTkZuj0lyzEXKO3umcHnh774Y6epcIa5HUlVxr_iXTdFfFrqz938PMXOoM51_W0PUvd1ipGzfsJoIFqy6eC8lfKG6qZZaPVOEsvJnvdZRQd8oTG5onYAkT9lLp3Kg8zTm3mpnI9GIDJIU1de4NTLWaSFHlKjneM8PiiXuYaW5zqA_6fq4bULkiStYdCYLtKBMpN4va2ABmUOuVRYpRkcJRmUdcaBLjOZ2RvOyDimMOZaA7IOIIct4Gy08KdnSyQVQ0trK1gNfamH2T34JTx42UmrKa1BqZhO9LM1c081uuueuuRATrRXsLxMK8j6uLgQeOhBm1gi_eZ24myWNQ5ZtjMPBRz3NronY55xgJQ1B2PlXuCTlfQbULqNiEf66VFN4LLjF7ci77o99J_BjwCNlgKWx2IRgHvgEOZpEl5VA5NTmGiNnjmu9QlzwLKtVDFeXdI0vhUAYb8Nzrf__XGM69rK8pnVR4vu0KJ7DzA5Ap7ByZYxcCrcLknOG4zk8AKF4E_aQHeBXCfZdtkd6jeV_tuSFHV5Z9z-SQErrEXtX4NWAwkcAJypNyawEbju6VYlNbKem0Yu3aLwcniEP4gX3sqiWg9_rM60nXK2ZYZ7noxdDdXuVchOeeJq72Jx3s4GhckIzva3iz0lFVKP5QwaXo-XTuaiX8C0DXNGS9dvQruwUa2FodyuV1yoKX2GtZb-tY0ldwvnpO8uik2b9C3f41G6NnZHPMJN4XyMdYUo7fRbvpGYzT6ZLy2yx_8jCmUbC9fJ3TQ9VfXaqYeO8rzozF19aB19duV8xFWcbUn2fHKuMViA9En2dW7J7OzqojGSFeuztJPaO6Y4a45Ow2w9TVfC1uvRne1rlWq5xs0iB5fLKMoH5WKdvW5_evmxn4B7FfvuUYZS22vJtIKN-wGH5vD_uYG86Q62wst4s1pK3RRHsaOPAwKGz2a-o6iHjA92VVFUVX-AWMARAs" rel="noopener noreferrer"&gt;kroki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Class Structure:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vehicle (Abstract)
├── Motorcycle
├── Car  
├── Truck
└── ElectricCar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;UML Relationships Explained:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inheritance (Generalization) - Solid Line with Empty Triangle:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Vehicle &amp;lt;|-- Motorcycle
Vehicle &amp;lt;|-- Car
Vehicle &amp;lt;|-- Truck
Vehicle &amp;lt;|-- ElectricCar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffeqanpwk8drvk998p77h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffeqanpwk8drvk998p77h.png" alt=" " width="617" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Composition and Aggregation Relationships&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Strong Composition (Filled Diamond):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ParkingLot "has many" ParkingFloor&lt;/li&gt;
&lt;li&gt;ParkingFloor "has many" ParkingSpot&lt;/li&gt;
&lt;li&gt;ParkingSpot "has one" Vehicle
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ParkingLot "1" *-- "many" ParkingFloor
ParkingFloor "1" *-- "many" ParkingSpot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhlntnx9jr9yez7vk73p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwhlntnx9jr9yez7vk73p.png" alt=" " width="230" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Parking floors cannot exist without the parking lot&lt;/li&gt;
&lt;li&gt;Parking spots cannot exist without their floor&lt;/li&gt;
&lt;li&gt;Lifecycle dependency: Child objects destroyed when parent is destroyed&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Weak Aggregation (Empty Diamond):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ParkingLot "1" o-- "many" ParkingObserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dc49bo2h4zt7mpa834v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6dc49bo2h4zt7mpa834v.png" alt=" " width="268" height="261"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Observers can exist independently of the parking lot&lt;/li&gt;
&lt;li&gt;Loose coupling: Observers can be shared across multiple parking lots&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Simple Association (Solid Line):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ParkingSpot --&amp;gt; Vehicle : parkedVehicle
ParkingTicket --&amp;gt; ParkingSpot
ParkingTicket --&amp;gt; Payment
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fn0vapyauoowhfxaaub.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4fn0vapyauoowhfxaaub.png" alt=" " width="588" height="241"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Objects reference each other but are independent&lt;/li&gt;
&lt;li&gt;Temporary relationships: Vehicle can move to different spots&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Design Pattern Implementations&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Strategy Pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PricingStrategy (Interface)
├── HourlyPricingStrategy
└── FlatRatePricingStrategy

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

&lt;/div&gt;



&lt;p&gt;UML Notation&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PricingStrategy &amp;lt;|.. HourlyPricingStrategy
PricingStrategy &amp;lt;|.. FlatRatePricingStrategy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb0sk2mnvtevmtv7u8h2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgb0sk2mnvtevmtv7u8h2.png" alt=" " width="627" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interface implementation relationship&lt;/li&gt;
&lt;li&gt;Enables runtime strategy switching&lt;/li&gt;
&lt;li&gt;Open/Closed Principle: Add new strategies without modifying existing code&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Observer Pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Relationship: ParkingLot "1" o-- "many" ParkingObserver
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Loose coupling: ParkingLot doesn't know concrete observer types&lt;/li&gt;
&lt;li&gt;Event-driven: Automatic notifications on state changes&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Factory Pattern:&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Factory creates Vehicle instances but doesn't store references&lt;/li&gt;
&lt;li&gt;Encapsulates creation logic: Hides vehicle instantiation complexity&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Singleton Pattern:&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Single instance guarantee: Only one parking lot can exist&lt;/li&gt;
&lt;li&gt;Global access point: Available throughout the application&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Builder Pattern:&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Purpose: Constructs complex ParkingLot objects step by step&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fluent interface: Method chaining for easy configuration&lt;/li&gt;
&lt;li&gt;Complex construction: Handles multi-floor setup with different spot types&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Key Relationships Analysis&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;Vehicle ↔ ParkingSpot&lt;/code&gt; Interaction&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bidirectional relationship: Each knows about the other when parked&lt;/li&gt;
&lt;li&gt;Validation logic: Vehicle determines if it can fit in spot&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2. ParkingLot as Central Coordinator&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqneo6ndaihtz45xsl43a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqneo6ndaihtz45xsl43a.png" alt=" " width="800" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hub pattern: Central point for all parking operations&lt;/li&gt;
&lt;li&gt;Dependency injection: Strategy and observers injected at runtime&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Interface vs Abstract Class Decisions&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Interfaces Used:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;PricingStrategy: Behavior-only contract (no shared data)&lt;/li&gt;
&lt;li&gt;ParkingObserver: Event handling contract (no shared implementation)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Abstract Classes Used:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Vehicle: Shared data (licensePlate, color) + abstract behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Decision Criteria:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Interface when: Pure behavior contract, no shared data&lt;br&gt;
Abstract class when: Shared data + some abstract behaviors&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Encapsulation and Access Modifiers&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Field Visibility Patterns:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Private Fields (-):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All internal state in ParkingSpot, Payment, ParkingTicket&lt;/li&gt;
&lt;li&gt;Information hiding: Implementation details not exposed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Protected Fields (#):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Vehicle class fields accessible to subclasses&lt;/li&gt;
&lt;li&gt;Inheritance support: Subclasses can access parent data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Public Methods (+):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All service methods and getters&lt;/li&gt;
&lt;li&gt;Clear API: Well-defined public interface&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Thread Safety Considerations&lt;/strong&gt;&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;ParkingSpot has individual locks for fine-grained control&lt;/li&gt;
&lt;li&gt;ParkingFloor has floor-level locks for coordination&lt;/li&gt;
&lt;li&gt;ParkingLot has system-wide locks for complex operations&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Thread-Safe Collections:&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;Maps shown as ConcurrentHashMap implementations&lt;/li&gt;
&lt;li&gt;Concurrent access: Multiple threads can safely access shared data&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧩 LLD Implementation
&lt;/h2&gt;

&lt;p&gt;What You’ll Find in the Repository - &lt;a href="https://github.com/AnkitDevCode/ParkingLot/" rel="noopener noreferrer"&gt;ParkingLot implementation&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;All core components, including:&lt;/li&gt;
&lt;li&gt;Vehicle and its subclasses (e.g., Car, Bike, Truck)&lt;/li&gt;
&lt;li&gt;ParkingSpot, ParkingFloor&lt;/li&gt;
&lt;li&gt;ParkingTicket&lt;/li&gt;
&lt;li&gt;PricingStrategy with a sample HourlyPricingStrategy&lt;/li&gt;
&lt;li&gt;Payment&lt;/li&gt;
&lt;li&gt;PaymentProcessor&lt;/li&gt;
&lt;li&gt;ParkingLot as the central controller&lt;/li&gt;
&lt;li&gt;Basic Observer pattern implementation with ParkingObserver and DisplayBoard&lt;/li&gt;
&lt;li&gt;Simple and clean code, designed to be easy to understand and extend for beginners.&lt;/li&gt;
&lt;li&gt;README documentation that explains how to set up, run the project, and test the functionality.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;📌 Note&lt;/strong&gt;&lt;br&gt;
This repository is a work in progress. The current design and implementation may have some limitations or areas that can be improved. I will continue to update and optimize the design over time to make it more robust and extensible.&lt;/p&gt;

&lt;p&gt;For now, consider this as a good starting point to understand the Parking Lot LLD in Java. It demonstrates the core concepts and relationships clearly, while leaving room for enhancements and refinements.&lt;/p&gt;




&lt;h2&gt;
  
  
  Summary: Good vs Bad Approaches
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;✅ What Makes This Solution Strong:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Clear Separation of Concerns: Each class has a single responsibility&lt;/li&gt;
&lt;li&gt;Proper Abstraction: Abstract classes and interfaces used appropriately&lt;/li&gt;
&lt;li&gt;Thread Safety: Comprehensive concurrency handling&lt;/li&gt;
&lt;li&gt;Design Patterns: Multiple patterns used correctly (Strategy, Factory, Singleton, Observer)&lt;/li&gt;
&lt;li&gt;Extensibility: Easy to add new features without breaking existing code&lt;/li&gt;
&lt;li&gt;Error Handling: Graceful handling of edge cases&lt;/li&gt;
&lt;li&gt;Type Safety: Enums prevent invalid states and types&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;🚩 Common Red Flags to Avoid:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;God Class Anti-pattern: Single class doing everything&lt;/li&gt;
&lt;li&gt;Primitive Obsession: Using strings/ints instead of proper types&lt;/li&gt;
&lt;li&gt;No Thread Safety: Ignoring concurrent access issues&lt;/li&gt;
&lt;li&gt;Tight Coupling: Classes knowing too much about each other&lt;/li&gt;
&lt;li&gt;No Error Handling: Not handling edge cases&lt;/li&gt;
&lt;li&gt;Hard-coded Values: No flexibility for configuration&lt;/li&gt;
&lt;li&gt;Missing Abstractions: Not using inheritance where appropriate&lt;/li&gt;
&lt;li&gt;Poor Naming: Unclear or misleading class/method names&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  References &amp;amp; Credits
&lt;/h2&gt;

&lt;p&gt;AI tools were used to assist in research and writing but final content was reviewed and verified by the author.&lt;/p&gt;

</description>
      <category>lld</category>
      <category>designsystem</category>
      <category>designpatterns</category>
      <category>parkinglot</category>
    </item>
    <item>
      <title>Transactions : Explained</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Wed, 27 Aug 2025 15:27:54 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/transactions-explained-an</link>
      <guid>https://dev.to/ankitdevcode/transactions-explained-an</guid>
      <description>&lt;h2&gt;
  
  
  What is a transaction?
&lt;/h2&gt;

&lt;p&gt;A transaction defines a logical unit of work that either completely succeeds or produces no result at all. There are two types of transactions - local and distributed transaction.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why we need transaction management?
&lt;/h2&gt;

&lt;p&gt;A database transaction, by definition, must be atomic. In order to maintain consistency in a database, before and after the transaction, few properties are followed. These are called ACID properties. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;strong&gt;Atomicity&lt;/strong&gt;- It ensures that either all or none of the operations needs to be performed.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Consistency&lt;/strong&gt;- It ensures that either all operations are rolled back and set to the state back from where the transaction was started or the changes were successfully made and reflected.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Isolation&lt;/strong&gt;- It ensures that transactions performed are only reflected after a commit.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Durability&lt;/strong&gt;- It ensures that the committed transactions are persisted.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What are Transactional databases?
&lt;/h2&gt;

&lt;p&gt;A transactional database is a DBMS that provides the ACID properties. Not having ACID properties means that the database works well on clusters because they are horizontally scalable. NoSQL follows a model known as the BASE (Basically Available, Soft state, Eventual consistency).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types of Transactions&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Local Transaction&lt;/li&gt;
&lt;li&gt;Distributed Transaction&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  1. Local Transaction:
&lt;/h2&gt;

&lt;p&gt;Local transactions are those that affect only one transaction resource.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fije0v0d7jnf7p2b3cs5p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fije0v0d7jnf7p2b3cs5p.png" alt=" " width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JDBC API&lt;/strong&gt; is auto-commit by default. To enable the transaction, set auto-commit to false. Transactions are managed at the connection level.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local transaction in hibernate&lt;/strong&gt;&lt;br&gt;
In hibernate, a transaction is associated with a Session and is usually instantiated by a call to &lt;code&gt;Session.beginTransaction()&lt;/code&gt;. A single session might span multiple transactions. However, it is intended that there be at most one uncommitted Transaction associated with a particular Session at any time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local transaction in spring&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Spring declarative transaction model uses AOP proxy. in spring by default propagation level is &lt;code&gt;REQUIRED&lt;/code&gt; which means the inner transaction will be part of the same outer transaction, hence if the inner transaction fails the whole transaction will get rollback. it's important to know that Rollback works for only Runtime exceptions by default. For checked Exceptions, we have to specify that explicitly &lt;code&gt;@Transcational(rollbackFor = Exception.class)&lt;/code&gt; Mixing the database I/O with other types of I/O like web service call , in a transactional context is a code smell and may lead to connection exhaust.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note :&lt;/strong&gt; Spring supports distributed JTA transactions across multiple XA resources by using Transaction Manager like Atomikos, JBossTS or Bitronix etc. JTA transactions are also supported when deploying to a suitable Java EE Application Server. we will cover it in distributed transaction section.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/AnkitDevCode/transactions" rel="noopener noreferrer"&gt;GitHub repo link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before getting into the &lt;strong&gt;distributed transactions&lt;/strong&gt; details, let's understand JTA-&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JTA (Java transaction API) :&lt;/strong&gt;&lt;br&gt;
JTA defines a high-level transaction management interface intended for resource managers and transactional applications in DTP (distributed transaction processing) environments.&lt;/p&gt;

&lt;p&gt;It allows applications to perform distributed transactions, that is, transactions that access and update data on two or more networked computer resources. The JTA specifies standard Java interfaces between a transaction manager and the parties involved in a distributed transaction system.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1-UserTransaction—&lt;/strong&gt; The javax.transaction.UserTransaction interface provides the application the ability to control transaction boundaries programmatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2—Transaction Manager—&lt;/strong&gt; The javax.transaction.TransactionManager interface allows the application server to control transaction boundaries on behalf of the application being managed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3—XAResource—The javax.transaction.xa.XAResource&lt;/strong&gt; interface is a Java mapping of the industry standard XA interface based on the X/Open CAE Specification (Distributed Transaction Processing: The XA Specification).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XA Protocol :&lt;/strong&gt;&lt;br&gt;
The XA interface specifies communication between a transaction manager (TM) and a resource manager (RM). To communicate with the transactional resources, the transaction monitor must speak a common protocol with transactional resources. Usually, this protocol is a specification called XA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note :&lt;/strong&gt; Java EE application servers support JTA out of the box, and there are third party, standalone implementations of JTA that you can use to avoid being trapped on a Java EE application server.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Distributed Transaction:
&lt;/h2&gt;

&lt;p&gt;Global transactions are those that span one or more transactional resources, and enlist them all in a single transaction. Therefore, it must be coordinated among those resources.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq1cbh6t9gwqsf3mgj1o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvq1cbh6t9gwqsf3mgj1o.png" alt=" " width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are two popular approaches for distributed transactions: &lt;br&gt;
Two Phase Commit Protocol and Eventual Consistency and Compensation also known as Saga pattern.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Problem :&lt;/strong&gt;&lt;br&gt;
In a monolithic system, we have a database system to ensure ACID properties. however, The microservice-based system does not have a global transaction coordinator by default. how do we manage transaction in micro service architecture?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Possible solutions:&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1.Two-Phase Commit Protocol
&lt;/h3&gt;

&lt;p&gt;This mechanism is designed initially for distributed systems. 2pc is widely used in database systems. As Microservices architecture inherently distributed systems in nature, we can use the Two-phase commit protocol (or 2PC) as one of the approaches. Primary drivers in a distributed transaction management are the message broker/transaction coordinator.&lt;/p&gt;

&lt;p&gt;The distributed transaction contains two steps:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prepare phase&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Commit or Rollback phase&lt;br&gt;
Prepare Phase:&lt;/p&gt;

&lt;p&gt;All the participants of a transaction in this phase will be prepared for the commit and inform the transaction coordinator/message broker that they are ready for completing the transaction. it guarantee that the transaction is atomic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Commit or Rollback phase:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this phase, transaction coordinator will issue one of the commands they are a commit or a rollback to all the participants.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Benefits of using 2pc&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;2pc is a very strong consistency protocol and mainly used in DBMS system.&lt;br&gt;
2pc allows read-write isolation. This means the changes on a field are not visible until the coordinator commits the changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Disadvantages of using 2pc&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;The main issue with the 2PC approach is that it is a bit slow compared to the time for the operation on a single Microservice because it has to coordinate the transaction between services even if all the microservices are on the same network, still operation will be slow. So we need to be careful while implementing this for high demand services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It is not really recommended for many microservice-based systems because 2pc is synchronous (blocking).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requires remote communication to each participating system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Requires each participating system to support two-phase commit.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Eventual Consistency and Compensation (SAGA)
&lt;/h3&gt;

&lt;p&gt;Eventual Consistency can be achieved by sagas. SAGA is one of the best way to ensure the consistency of the data in a distributed architecture without having a single ACID transaction. it maintains consistency (but not atomicity) across multiple services without requiring a transaction manager system.. A series of local transactions is SAGA. Saga pattern is an alternative to XA when resources are not XA capable or XA is undesired&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advantages of SAGA&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Maintains consistency (but not atomicity) across multiple services without requiring a transaction manager system.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Uses a sequence of local TXs&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Failure of a local TX executes a series of compensating transactions to undo the previous successful TXs&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;There are two types of sagas:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choreography : each local transaction publishes domain events that trigger local transactions in other services.&lt;/li&gt;
&lt;li&gt;Orchestration : an orchestrator (object) tells the participants what local transactions to execute.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choreography-Based Saga&lt;/strong&gt;&lt;br&gt;
In this approach, there is no central orchestrator. Each service participating in the Saga performs their transaction and publish events. The other services act upon those events and perform their transactions. Also, they may or not publish other events based on the situation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Orchestration-Based Saga&lt;/strong&gt;&lt;br&gt;
In this approach, there is a Saga orchestrator that manages all the transactions and directs the participant services to execute local transactions based on events. This orchestrator can also be though of as a Saga Manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary of advanced topics in distributed transactions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;CAP Theorem – trade-offs between consistency, availability, partition tolerance.&lt;/li&gt;
&lt;li&gt;Two-Phase Commit (2PC) – classic XA transactions, but heavy &amp;amp; blocking.&lt;/li&gt;
&lt;li&gt;Saga Pattern – break into steps with compensating actions (choreography/orchestration).&lt;/li&gt;
&lt;li&gt;TCC (Try-Confirm-Cancel) – reserve → confirm → cancel, used in payments.&lt;/li&gt;
&lt;li&gt;Idempotency &amp;amp; Retries – ensure safe retries in distributed systems.&lt;/li&gt;
&lt;li&gt;Consensus Protocols – Paxos, Raft, ZAB for agreement in clusters.&lt;/li&gt;
&lt;li&gt;Distributed Locking – Redis RedLock, ZooKeeper, DB locks.&lt;/li&gt;
&lt;li&gt;Modern Tools – Seata, Temporal.io, Camunda, Kafka transactions.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>transaction</category>
      <category>saga</category>
      <category>distributedsystems</category>
      <category>microservices</category>
    </item>
    <item>
      <title>Getting Started with PL/SQL Blocks and Calling Them from Spring Boot JPA</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Wed, 27 Aug 2025 14:57:14 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/getting-started-with-plsql-blocks-and-calling-them-from-spring-boot-jpa-3192</link>
      <guid>https://dev.to/ankitdevcode/getting-started-with-plsql-blocks-and-calling-them-from-spring-boot-jpa-3192</guid>
      <description>&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;Before we begin, here are the requirements for following along with this guide:&lt;/p&gt;

&lt;p&gt;Database: Oracle Database (since PL/SQL is Oracle-specific).&lt;/p&gt;

&lt;p&gt;The syntax, examples, and stored procedures/functions in this tutorial are based on Oracle PL/SQL only.&lt;/p&gt;

&lt;p&gt;Other databases (e.g., MySQL, PostgreSQL) have different procedural languages and syntax.&lt;/p&gt;

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

&lt;p&gt;PL/SQL (Procedural Language / SQL) is Oracle’s extension of SQL.&lt;br&gt;
It adds programming features (like loops, conditions, procedures, and error handling) on top of SQL. It allows you to combine SQL statements with procedural logic.&lt;/p&gt;
&lt;h2&gt;
  
  
  PL/SQL Block
&lt;/h2&gt;

&lt;p&gt;A block is a basic unit of code (procedures, functions, and anonymous blocks) that make up the PL/SQL program logical. A PL/SQL block includes three sections: declaration, executable, and exception-handling sections. The executable section is mandatory, while others are optional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Types of PL/SQL Blocks&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anonymous Blocks

&lt;ul&gt;
&lt;li&gt;Not stored in the database.&lt;/li&gt;
&lt;li&gt;Written and executed directly (like test scripts).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Named Blocks

&lt;ul&gt;
&lt;li&gt;Stored in the database for reuse.&lt;/li&gt;
&lt;li&gt;Examples: Procedures, Functions, Packages, Triggers.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  An anonymous block
&lt;/h2&gt;

&lt;p&gt;A block without a name is an anonymous block. An anonymous block is not saved in the Oracle Database server, so it is just for one-time use. However, PL/SQL anonymous blocks can be useful for testing purposes.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SET SERVEROUTPUT ON;
declare
p_emp table_emp%rowtype;
begin
select * into p_emp from table_emp where emp_id='123456';  
dbms_output.put_line('Result:'|| p_emp.name || p_emp.emp_id);
exception
when NO_DATA_FOUND
then 
dbms_output.put_line('No Result:');
end;

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

&lt;/div&gt;



&lt;p&gt;/&lt;br&gt;
The slash (/) executes the block.&lt;/p&gt;
&lt;h2&gt;
  
  
  Functions or Procedures (named Block)
&lt;/h2&gt;

&lt;p&gt;Functions or Procedures is an example of a named block. A named block is stored in the Oracle Database server and can be reused later.&lt;/p&gt;
&lt;h3&gt;
  
  
  Create Procedure Example
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;create or replace procedure getEmpById(id in varchar2)    
AS 
p_emp table_emp%rowtype;
begin    
select * into p_emp from table_emp where emp_id=id;  
dbms_output.put_line('Result:'|| p_emp.name || p_emp.emp_id);
end ;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Execute procedure&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;begin
getEmpById('12345');
end;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OR&lt;/p&gt;

&lt;p&gt;&lt;code&gt;exec getEmpById('12345');&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Delete procedure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop procedure getEmpById;&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Function Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; create or replace function getEmpById(id in varchar2) 
   return varchar2 
   IS
   p_name varchar2(255);
   begin 
     select name into p_name from table_emp where emp_id=id;  
      return(p_name); 
    end;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Execute function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;select getEmpById('123456') from dual;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;OR&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;declare
num number := &amp;amp;emp_id;
e_name varchar2(255);
begin
e_name := getEmpById(num);
 dbms_output.put_line('the employee name '|| e_name);
end;
/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Delete function&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drop function getEmpById;&lt;/code&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Calling a Stored Procedure with Spring Data JPA
&lt;/h1&gt;

&lt;p&gt;Stored procedures with Spring Data JPA (or any framework) come with pros and cons. Here’s a balanced view:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b6imd4416cezvlbkb3k.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9b6imd4416cezvlbkb3k.png" alt=" " width="515" height="205"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;@Procedure Annotation&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface EmployeeRepository extends JpaRepository&amp;lt;Employee, Long&amp;gt; {

    @Procedure(procedureName = "increase_salary")
    void increaseSalary(@Param("p_emp_id") Long empId, @Param("p_percent") Double percent);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;@NamedStoredProcedureQuery&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Entity
@NamedStoredProcedureQuery(
    name = "Employee.increaseSalary",
    procedureName = "increase_salary",
    parameters = {
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "p_emp_id", type = Long.class),
        @StoredProcedureParameter(mode = ParameterMode.IN, name = "p_percent", type = Double.class)
    }
)
public class Employee {
    @Id
    private Long empId;
    private String name;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public interface EmployeeRepository extends JpaRepository&amp;lt;Employee, Long&amp;gt; {
    @Procedure(name = "Employee.increaseSalary")
    void increaseSalary(@Param("p_emp_id") Long empId, @Param("p_percent") Double percent);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Main Use Cases of PL/SQL Blocks
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Data Processing – update/modify data in bulk.&lt;/li&gt;
&lt;li&gt;Business Logic &amp;amp; Validation – enforce rules beyond SQL constraints.&lt;/li&gt;
&lt;li&gt;Error Handling – gracefully manage runtime errors.&lt;/li&gt;
&lt;li&gt;Automation via triggers – run logic on INSERT/UPDATE/DELETE.&lt;/li&gt;
&lt;li&gt;Reusable Code – stored procedures &amp;amp; functions.&lt;/li&gt;
&lt;li&gt;Batch Jobs &amp;amp; Scheduling – cleanup, reporting, automation tasks.&lt;/li&gt;
&lt;li&gt;Complex Transactions – ensure atomic operations (commit/rollback).&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;Use stored procedures for heavy DB operations, bulk processing, and centralized business rules.&lt;/li&gt;
&lt;li&gt;Use JPA queries for simple, CRUD-like operations and portability.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>plsql</category>
      <category>jpa</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>AnkitDevCode</dc:creator>
      <pubDate>Tue, 26 Aug 2025 18:22:35 +0000</pubDate>
      <link>https://dev.to/ankitdevcode/-2m0l</link>
      <guid>https://dev.to/ankitdevcode/-2m0l</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://dev.to/ankitdevcode/observability-part-2-metrics-dashboards-with-prometheus-and-grafana-fib" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fudk7lqf3fxr7m62bv6w4.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://dev.to/ankitdevcode/observability-part-2-metrics-dashboards-with-prometheus-and-grafana-fib" rel="noopener noreferrer" class="c-link"&gt;
            Observability Part 2 – Metrics &amp;amp; Dashboards with Prometheus and Grafana - DEV Community
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Introduction   Quick recap of previous article:  Showed how to use OpenTelemetry &amp;amp;amp;...
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8j7kvp660rqzt99zui8e.png"&gt;
          dev.to
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>prometheus</category>
      <category>grafana</category>
      <category>observability</category>
      <category>jaeger</category>
    </item>
  </channel>
</rss>
