Skip to main content

๐Ÿงฉ Avoid Mixed Orders! Thread-Based Transaction Handling in Spring Boot Explained

Handling Transactions in Thread-Based Environments (Java + Spring Boot)

๐Ÿง  How Do You Handle Transactions in a Thread-Based Environment?

(The ultimate fresher-friendly explanation — with jokes, questions & real-life pain ๐Ÿ˜‚)

1️⃣ First — Why This Question Comes in EVERY Interview?

Interviewer thinking:

“If this candidate can’t handle concurrent transactions, I’ll hire him and later he’ll break my production database.” ๐Ÿ˜ญ

You thinking:

“Why this fellow always asking this tough question?” ๐Ÿ˜ซ

Reality:

  • ๐Ÿ‘‰ In real applications multiple threads hit the system.
  • ๐Ÿ‘‰ Multiple users click at the same time.
  • ๐Ÿ‘‰ If your code is not transaction-safe, DB becomes “Kaboom ๐Ÿ’ฅ”.

That’s why interviewers ask:

“How do you handle transactions in multi-threaded scenarios?”

Because they want to check:

  • ✔ Do you understand data consistency?
  • ✔ Do you know why @Transactional exists?
  • ✔ Can you prevent overwriting data?
  • ✔ Do you know what happens when 2 threads modify same row?

2️⃣ Next — Do You REALLY Need to Know This?

You may think:

“Bro, I don’t want all this DB locking tension… I write simple API only.”
“Why should I learn? I don’t even like DB!” ๐Ÿคฃ

Let me answer:

  • ✔ YES, you 100% need it.

Because even simple APIs will one day face multiple requests at the same time.

If you don’t know it…

  • ⚠️ Data gets mixed
  • ⚠️ Duplicate records
  • ⚠️ Inventory becomes negative
  • ⚠️ Users get wrong bill
  • ⚠️ DB throws deadlock
  • ⚠️ Customer shouting
  • ⚠️ Manager shouting
  • ⚠️ You crying ๐Ÿ˜ญ

And interview next time again asking this ๐Ÿ˜‚ So yes… Please learn it… ๐Ÿ˜‚๐Ÿ˜‚๐Ÿ˜‚

๐Ÿงจ What FRESHERS Usually Do (Old School Thinking)

They apply:

synchronized saveOrder() { ... }

Or:

LOCK the whole method

This means:

  • ๐Ÿ‘‰ Only ONE waiter can take orders at a time
  • ๐Ÿ‘‰ All other customers must wait outside the restaurant like fools

๐Ÿคฃ “Bro this is not a barber shop, this is a REST API.”

synchronized = very old Java solution → NOT scalable in Spring Boot world

๐Ÿฝ️ Handling Transactions in Thread-Based Environments (Java + Spring Boot)

The most confusing topic explained like a story for freshers — with real code, real scenarios, and zero boring stuff!

๐Ÿง  1. Why Interviewers LOVE This Question?

Every interview:

  • ➡️ “How do you handle transactions in a thread-based environment?”
  • ➡️ “What happens if two requests update same record?”
  • ➡️ “Do you know optimistic/pessimistic locks?”

And we think: “Bro… threads? environment? I just wrote CRUD ๐Ÿ˜ญ”

But here is the real truth: if you don't understand this… you will create bugs, data will mix, you will overwrite other user’s data — and yes, you will get scolded in production. So… let’s learn it properly once and for all.

๐Ÿง  2. Before Everything — Understand The Real Problem

❓ What is a non-thread-based environment?

➡️ Everything runs one-by-one — only one waiter taking orders; no parallel tasks.

public class NonThreadEnv {
    public static void main(String[] args) {
        placeOrder("Customer A");
        placeOrder("Customer B");
    }

    static void placeOrder(String customer) {
        System.out.println("Order from: " + customer);
    }
}

Output: Order from: Customer A then Order from: Customer B. No mixing. Life is peaceful.

❓ What is a thread-based environment?

➡️ Multiple requests, multiple threads, parallel execution — many waiters taking orders at the same time.

public class ThreadEnv {

    static int orderNumber = 1;

    public static void main(String[] args) {
        Runnable t = () -> placeOrder("Thread-" + Thread.currentThread().getId());

        for (int i = 0; i < 5; i++) {
            new Thread(t).start();
        }
    }

    static void placeOrder(String name) {
        System.out.println(name + " placing order no: " + orderNumber);
        orderNumber++; // shared data!
    }
}

❗ Output (random & wrong):
Thread-14 placing order no: 1
Thread-16 placing order no: 1 <-- SAME NUMBER
Thread-15 placing order no: 2
Thread-12 placing order no: 3
Thread-13 placing order no: 3 <-- DUPLICATE

➡️ This is what happens inside Spring too — same bean instance, multiple threads modifying same fields. Boom → corrupted data.

๐Ÿฝ️ 3. Restaurant Story (The Easiest Explanation)

Imagine: you run a restaurant. Two customers come at the same time. Two waiters take orders. Both waiters write on the same paper.

What will happen?

  • ➡️ Idly + Dosa printed in same bill
  • ➡️ Wrong items, wrong table
  • ➡️ Customer angry — you lose your job ๐Ÿ˜‚

This is EXACTLY what happens in Java when there are shared objects, shared entities, shared lists, shared Order instances, or shared static fields.

๐Ÿ”ฅ 4. WRONG CODE (This causes mixing!)

@Service
public class OrderService {

    private Order order = new Order(); // SHARED object

    public void addItem(String item) {
        order.getItems().add(item);
        System.out.println("Items: " + order.getItems());
    }
}

Two users place orders: User1: Idly and User2: Dosa.
Result:
Items: [Idly]
Items: [Idly, Dosa] <-- mixed!

✨ 5. The Ultimate Goal — What Are We Trying To Solve?

  • ✔ Each user → separate order
  • ✔ Separate DB rows
  • ✔ No data overwrite
  • ✔ No mixing
  • ✔ Safe transactions
  • ✔ Thread-safe logic

๐Ÿงฉ Step 1 — NEW OBJECT PER REQUEST (Simple & Best)

This solves ~90% problems.

@Service
public class OrderService {

    public Order placeOrder(String item) {
        Order o = new Order();      // NEW instance
        o.addItem(item);
        return o;
    }
}

๐Ÿ’ก No shared data. Every request clean. No mixing.

๐Ÿงฉ Step 2 — @Transactional (Atomic Operation)

Why use @Transactional?

  • ➡️ Multiple DB operations → ONE unit
  • ➡️ If any step fails → rollback
  • ➡️ Prevent half-written data
@Transactional
public void placeOrder(OrderRequest req) {

    Order order = new Order();
    orderRepo.save(order);

    orderItemRepo.saveAll(req.getItems());

    inventoryRepo.reduceStock(req.getItems());
}

Q: Does @Transactional prevent thread collision?
A: No. It prevents partial DB save but not in-memory thread collision. That’s why steps address different layers.

๐Ÿงฉ Step 3 — Pessimistic Lock

Think: “Don’t touch this row until I finish.” Useful for stock updates, wallet/balance updates, booking systems.

@Lock(LockModeType.PESSIMISTIC_WRITE)
Order findById(Long id);

Use inside @Transactional to hold DB row locks while you update.

๐Ÿงฉ Step 4 — Optimistic Lock (@Version)

Important default for most cases — detects concurrent updates instead of blocking.

@Entity
public class Product {

    @Id
    private Long id;

    @Version
    private int version;

    private int stock;
}

➡️ Every update checks version — if changed, the update fails with OptimisticLockException. This prevents silent overwrite.

๐Ÿงฉ Step 5 — Isolation Levels

Strictest option:

@Transactional(isolation = Isolation.SERIALIZABLE)
public void placeOrderSerializable(OrderRequest req) {
    // DB guarantees serializable behavior; lowest anomalies
}

SERIALIZABLE is safe but can be slow. Use only for extremely critical operations.

๐Ÿงจ 11. Deadlocks — Can They Still Happen Today?

Yes. If two transactions wait for each other:

// Thread 1
lock orders;
wait for inventory;

// Thread 2
lock inventory;
wait for orders;

DB detects both waiting and kills one transaction — you will see Deadlock detected. Rolling back transaction.

๐ŸŸข 12. Full Clean Spring Boot Example (Optimistic Pattern)

Controller

@RestController
public class OrderController {

    @PostMapping("/orders")
    public String place(@RequestBody OrderRequest req) {
        service.placeOrder(req);
        return "Order saved!";
    }

    @Autowired
    private OrderService service;
}

Service

@Service
public class OrderService {

    @Autowired
    private OrderRepo orderRepo;

    @Autowired
    private StockRepo stockRepo;

    @Transactional
    public void placeOrder(OrderRequest req) {

        // 1. Create new order
        Order o = new Order();
        orderRepo.save(o);

        // 2. Add items
        for (String item : req.getItems()) {
            stockRepo.reduceItem(item); // locked or versioned
        }
    }
}

Repository (Optimistic Lock)

public interface StockRepo extends JpaRepository<Stock, Long> {

    @Lock(LockModeType.OPTIMISTIC)
    Stock findByName(String name);
}

Entity with @Version

@Entity
public class Stock {

    @Id
    private Long id;

    private String name;

    private int qty;

    @Version
    private int version;
}

๐ŸŽฏ 13. Summary Table (So You Never Forget)

ProblemReasonStep
Shared object overwrittenMultiple threads sharing same beanCreate new object per request
DB rows overwrittenTwo updates same timeOptimistic lock
Two threads need same rowOne must waitPessimistic lock
Multi-steps save must be atomicAny step failing corrupts data@Transactional
High concurrency DB corruptionIsolation not strictSerializable / higher isolation
Wrapping Up — Now you know why interviews ask this, how Spring handles threads, transactions, locks, and @Version magic. Keep practicing, break things, see DB behavior, and share your pain stories below! ๐Ÿ’ช

Comments

Popular posts from this blog

๐Ÿ” Is final Really Final in Java? The Truth May Surprise You ๐Ÿ˜ฒ

๐Ÿ’ฌ “When I was exploring what to do and what not to do in Java, one small keyword caught my eye — final . I thought it meant: locked, sealed, frozen — like my fridge when I forget to defrost it.”   But guess what? Java has its own meaning of final… and it’s not always what you expect! ๐Ÿ˜… Let’s break it down together — with code, questions, confusion, jokes, and everything in between. ๐ŸŽฏ The Confusing Case: You Said It's Final... Then It Changed?! ๐Ÿซ  final List<String> names = new ArrayList <>(); names.add( "Anand" ); names.add( "Rahul" ); System.out.println(names); // [Anand, Rahul] ๐Ÿคฏ Hold on... that’s final , right?! So how on earth is it still changing ? Time to dive deeper... ๐Ÿง  Why Is It Designed Like This? Here’s the key secret: In Java, final applies to the reference , not the object it points to . Let’s decode this like a spy mission ๐Ÿ•ต️‍♂️: Imagine This: final List<String> names = new ArrayList <>(); Be...

๐ŸŒŸ My Journey – From Zero to Senior Java Tech Lead ๐ŸŒŸ

 There’s one thing I truly believe… If I can become a Java developer, then anyone in the world can. ๐Ÿ’ฏ Sounds crazy? Let me take you back. ๐Ÿ•“ Back in 2015… I had zero coding knowledge . Not just that — I had no interest in coding either. But life has its own plans. In 2016, I got a chance to move to Bangalore and joined a Java course at a training center. That’s where it all started — Every day, every session made me feel like: "Ohhh! Even I can be a developer!" That course didn’t just teach Java — it gave me confidence . ๐Ÿงช Two Life-Changing Incidents 1️⃣ The Interview That Wasn't Planned Halfway through my course, I had to urgently travel to Chennai to donate blood to a family member. After that emotional rollercoaster, I found myself reflecting on my skills and the future. The next day, as I was preparing for my move to Bangalore to complete the remaining four months of my course, I randomly thought — "Let me test my skills... let me just see...

๐ŸŽข Java Loops: Fun, Fear, and ForEach() Fails

๐ŸŒ€ Oops, I Looped It Again! — The Ultimate Java Loop Guide You Won't Forget “I remember this question from one of my early interviews — I was just 2 years into Java and the interviewer asked, ‘Which loop do you prefer and why?’” At first, I thought, “Duh! for-each is cleaner.” But then he grilled me with cases where it fails. ๐Ÿ˜ต That led me to explore all loop types, their powers, and their pitfalls. Let’s deep-dive into every major Java loop with examples &  real-world guidance so you'll never forget again. ๐Ÿ” Loop Type #1: Classic For Loop — “The Old Reliable” ✅ When to Use: You need an index You want to iterate in reverse You want full control over loop mechanics ✅ Good Example: List<String> names = List.of("A", "B", "C"); for (int i = 0; i < names.size(); i++) { System.out.println(i + ": " + names.get(i)); } ๐Ÿ”ฅ Reverse + Removal Example: List<String> item...