Most transaction bugs in Spring Boot are not SQL bugs—they’re transaction boundary bugs. Today’s focus is a deep dive into @Transactional: propagation, isolation, and rollback rules. If you only use the default settings everywhere, you may accidentally create hidden data inconsistencies or unexpected commits. Example: @Service public class PaymentService { @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void processPayment(Order order) { paymentRepository.save(new Payment(order.getId(), order.getTotal())); inventoryService.reserve(order.getItems()); } } Key idea: REQUIRED joins an existing transaction or starts a new one, REQUIRES_NEW creates a separate one, and isolation controls visibility of concurrent changes. By default, rollback happens for unchecked exceptions, so checked exceptions often need explicit rollbackFor. Treat @Transactional as an architectural decision, not just an annotation. #Java #SpringBoot #BackendDevelopment
Jānis Ošs’ Post
More Relevant Posts
-
Most transaction bugs in Spring Boot are not SQL bugs—they’re transaction boundary bugs. Today’s focus is a deep dive into @Transactional: propagation, isolation, and rollback rules. If you only use the default settings everywhere, you may accidentally create hidden data inconsistencies or unexpected commits. Example: @Service public class PaymentService { @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) public void processPayment(Order order) { paymentRepository.save(new Payment(order.getId(), order.getTotal())); inventoryService.reserve(order.getItems()); } } Key idea: REQUIRED joins an existing transaction or starts a new one, REQUIRES_NEW creates a separate one, and isolation controls visibility of concurrent changes. By default, rollback happens for unchecked exceptions, so checked exceptions often need explicit rollbackFor. Treat @Transactional as an architectural decision, not just an annotation. #Java #SpringBoot #BackendDevelopment
To view or add a comment, sign in
-
-
Is your @Transactional annotation actually doing what you think? 🧐 In Spring Boot, data integrity is everything. But I often see a common trap: Self-invocation. If you call a transactional method from another method within the same class, the Spring Proxy is bypassed. 📉 The result? No transaction starts, and your data might end up inconsistent without any error message. Check: ✅ The Proxy Rule: Spring uses AOP proxies. External calls go through the proxy; internal calls don't. ✅ The Fix: Move the transactional logic to a separate Service or use AspectJ if complexity scales. ✅ Bonus: Always use readOnly = true for fetch operations to improve performance and avoid unnecessary flush calls. It’s not just about using the framework; it’s about understanding the "Magic" behind it. 🚀 Have you ever faced a "phantom" database bug because of this? Let's swap stories! 👇 hashtag #Java #SpringBoot #Backend #Database #CleanCode #SoftwareEngineering #JavaDeveloper
To view or add a comment, sign in
-
-
Is your @Transactional annotation actually doing what you think? 🧐 In Spring Boot, data integrity is everything. But I often see a common trap: Self-invocation. If you call a transactional method from another method within the same class, the Spring Proxy is bypassed. 📉 The result? No transaction starts, and your data might end up inconsistent without any error message. Check: ✅ The Proxy Rule: Spring uses AOP proxies. External calls go through the proxy; internal calls don't. ✅ The Fix: Move the transactional logic to a separate Service or use AspectJ if complexity scales. ✅ Bonus: Always use readOnly = true for fetch operations to improve performance and avoid unnecessary flush calls. It’s not just about using the framework; it’s about understanding the "Magic" behind it. 🚀 Have you ever faced a "phantom" database bug because of this? Let's swap stories! 👇 #Java #SpringBoot #Backend #Database #CleanCode #SoftwareEngineering #JavaDeveloper
To view or add a comment, sign in
-
-
I didn’t expect duplicate data to become this tricky. Recently, while working on a backend feature, I noticed something off, the same data was getting stored multiple times in the database. When I tried to fetch it, I was getting duplicate records. At first, I thought it was just a one-time issue. But after checking further, it turned out to be happening consistently in certain cases. The root cause was multiple requests hitting the same flow, and there were no proper checks or validations to prevent duplicate inserts. To fix this, I made a few changes: - Added validation before inserting data - Introduced unique constraints at the database level - Handled edge cases where repeated requests could happen After that, the duplicates stopped, and the data became more reliable. It was a good reminder for me: Relying only on application logic is not enough. Both validation and the database should enforce rules where it matters. Sometimes, clean data is not just about writing correct code. It’s about designing the system to prevent mistakes. #Java #BackendDevelopment #Database #SystemDesign #SpringBoot #LearningInPublic
To view or add a comment, sign in
-
-
Are you still loading everything into memory? Let me ask you something. How many times have you seen this in a codebase? repository.findAll() ☠️ Looks harmless, right? Until it isn’t. That single line can: • Pull millions of records into memory • Fill your Hibernate context • Trigger massive GC pressure • And eventually… crash your application This is not a performance issue. This is an architectural flaw. Most systems don’t fail because of complexity. They fail because of unbounded data processing. You are not controlling how much data your system loads, processes, or returns. And memory… has limits. So what’s the right approach? Stop thinking: "How do I get all the data?" Start thinking: "How do I guarantee I NEVER load too much?" The solution is simple (but often ignored): • Use pagination for APIs • Use streaming for large exports • Process data in controlled chunks • Return DTOs instead of heavy entities • Set hard limits Golden rule: Never load, process, or serialize everything at once. Always paginate, stream, or limit. Most production outages I’ve seen had one thing in common: Someone assumed the data would always be small. It never is. #SoftwareArchitecture #Java #SpringBoot #DistributedSystems #SoftwareEngineering #DesignPatterns
To view or add a comment, sign in
-
-
One loop. 500MB of memory. One string that should have been 2KB. The task was simple. Build a CSV export from a list of records and return it as a response. The code looped through every record and kept appending to a string using something like: result = result + row + “\n” Clean. Readable. Completely harmless. In development with 200 records, it ran instantly. In production with 800,000 records, the server started choking. Memory climbed to 500MB for a single request. Response times stretched to 40 seconds. Other requests started failing because there was no memory left. The code had not changed. The data had. Here is what was actually happening. Strings are immutable. Every time you append to a string, a new string is created in memory. Loop 1 creates a string of 1 row Loop 2 creates a new string of 2 rows Loop 3 creates a new string of 3 rows The previous strings don’t disappear immediately. They stay in memory until garbage collection runs. 800,000 iterations. Hundreds of thousands of intermediate strings. Massive memory pressure for something that should have been simple. The fix was one line. Use a StringBuilder or equivalent. Append to a buffer. Build the string once at the end. Memory dropped from 500MB to under 5MB. Response time dropped from 40 seconds to under 2 seconds. Same output. Same logic. Completely different performance. This is the kind of problem that never shows up in development. Because the issue is not the code. It is the combination of code and data scale. The most dangerous code is not complex code. It is simple code that looks too innocent to question. #SystemDesign #MemoryManagement #Java
To view or add a comment, sign in
-
-
🚨 One of the most dangerous Spring Boot traps — silent data loss with zero errors. @Transactional on a private method does absolutely nothing. No exception. No warning. Just broken data. ❌ Broken: @Service public class OrderService { public void placeOrder(Order order) { saveOrder(order); } @Transactional // ← silently ignored! private void saveOrder(Order order) { orderRepo.save(order); auditRepo.save(new AuditLog(order)); } } Why does this happen? Spring wraps beans in a proxy to intercept @Transactional calls. But private methods are invisible to the proxy — the call goes direct, completely bypassing transaction management. Result → partial saves, missing audit logs, inconsistent state. The fix: Keep transactional methods public. ✅ Fixed: @Service public class OrderService { @Transactional // ← proxy intercepts correctly public void saveOrder(Order order) { orderRepo.save(order); auditRepo.save(new AuditLog(order)); } } What makes this a production nightmare: → Unit tests pass cleanly → Logs show no errors → Only caught when data is already missing Spring won't warn you. The annotation just silently does nothing. Save this before it saves you. 🔖 Know any other silent Spring Boot traps? Drop them below 👇 #SpringBoot #Java #BackendDevelopment #SoftwareEngineering #CleanCode
To view or add a comment, sign in
-
-
How I improved API performance by ~40% 🚀 Problem: Slow response time due to heavy DB queries What I changed: • Optimized SQL queries • Added indexing • Reduced unnecessary joins • Used proper pagination Result: Faster APIs Better user experience Most performance issues are not in code, they’re in the database. #Backend #Java #Performance #Microservices #TechTips
To view or add a comment, sign in
-
#Post5 In the previous post, we saw how @RequestBody helps us handle request data. Now the next question is 👇 How do we get data from the URL? There are two common ways: • @PathVariable • @RequestParam Let’s understand 👇 👉 @PathVariable Used when the value is part of the URL path Example: GET /users/10 @GetMapping("/users/{id}") public String getUser(@PathVariable int id) { return "User id: " + id; } 👉 @RequestParam Used when the value comes as a query parameter Example: GET /users?id=10 @GetMapping("/users") public String getUser(@RequestParam int id) { return "User id: " + id; } 💡 Key difference: @PathVariable → part of URL @RequestParam → query parameter Key takeaway: Use the right approach based on how data is passed in the request 👍 In the next post, we will explore exception handling in Spring Boot 🔥 #Java #SpringBoot #BackendDevelopment #RESTAPI #LearnInPublic
To view or add a comment, sign in
-
Day 14. My API worked perfectly. Until it hit 100 users. I had this: List<Order> orders = orderRepository.findAll(); for (Order order : orders) { System.out.println(order.getUser().getName()); } Because of queries I didn’t even know existed. Looks clean. It’s not. Here’s what was actually happening: → 1 query to fetch all orders → 1 query per order to fetch the user → 100 orders = 101 queries hitting your database That’s the N+1 problem. And it hides in plain sight. Your code looks clean. Your database is suffering. And you probably have this in your codebase right now. The fix is simple: Fetch what you need. In one query. @Query("SELECT o FROM Order o JOIN FETCH o.user") List<Order> findAllWithUser(); One query. One JOIN. Everything loaded together. What actually changes: → Performance — 101 queries becomes 1 → Scalability — works at 100 rows, breaks at 100,000 → Visibility — you won’t notice until production The hard truth: → ORMs make it easy to write slow code → Lazy loading is convenient until it isn’t → You need to know what SQL your code is generating Writing code that works is easy. Writing code that doesn’t silently destroy your database is the real skill. Are you logging your SQL queries in development? If not — you should be. 👇 Drop it below #SpringBoot #Java #Hibernate #BackendDevelopment #JavaDeveloper
To view or add a comment, sign in
Explore content categories
- Career
- Productivity
- Finance
- Soft Skills & Emotional Intelligence
- Project Management
- Education
- Technology
- Leadership
- Ecommerce
- User Experience
- Recruitment & HR
- Customer Experience
- Real Estate
- Marketing
- Sales
- Retail & Merchandising
- Science
- Supply Chain Management
- Future Of Work
- Consulting
- Writing
- Economics
- Artificial Intelligence
- Employee Experience
- Workplace Trends
- Fundraising
- Networking
- Corporate Social Responsibility
- Negotiation
- Communication
- Engineering
- Hospitality & Tourism
- Business Strategy
- Change Management
- Organizational Culture
- Design
- Innovation
- Event Planning
- Training & Development