🚀 Most developers use @Transactional… but don’t understand how it actually works. And that’s where bugs start. Let’s simplify it 👇 --- 👉 What does @Transactional do? It ensures that a group of database operations: ✔ Either ALL succeed ✔ Or ALL fail (rollback) --- 💡 Example: @Transactional public void placeOrder() { saveOrder(); makePayment(); } 👉 If makePayment() fails ❌ → saveOrder() will also be rolled back --- 🔥 But here’s the catch (VERY IMPORTANT): @Transactional works using Spring AOP (proxy) 👉 Spring creates a proxy around your class → That proxy manages transaction start/commit/rollback --- ⚠️ Common mistake (many developers miss this): @Service public class OrderService { @Transactional public void createOrder() { saveOrder(); // ❌ Transaction may NOT work } @Transactional public void saveOrder() { // DB logic } } 👉 Why? Because internal method calls bypass the proxy 😮 --- ❌ Result: Transaction might not be applied properly --- ✅ Fix: Option 1: Call method from another service (bean) Option 2: Design properly (separate responsibilities) --- 🔥 Real-world impact: In payment systems: ❌ Partial data saved ❌ Inconsistent state This can cause serious bugs 🚨 --- 📌 Key Takeaway: @Transactional is NOT magic → It depends on proxy behavior Understand this = avoid production bugs --- Follow for more real backend learnings 🚀 #SpringBoot #Java #BackendDevelopment #SystemDesign #SoftwareEngineer
Transactional in Spring: Understanding Proxy Behavior
More Relevant Posts
-
Your transaction works perfectly… ✅ Until you call 𝗮𝗻𝗼𝘁𝗵𝗲𝗿 𝗺𝗲𝘁𝗵𝗼𝗱 inside it. Suddenly things behave… differently. Why? 𝗣𝗿𝗼𝗽𝗮𝗴𝗮𝘁𝗶𝗼𝗻. If you’re using `@Transactional` and not thinking about propagation, you’re basically saying: “Spring… you decide.” So what is propagation? It defines how a transaction behaves when one method calls another. Simple idea. But huge impact. Let’s break it down with real-world scenarios 🔹 REQUIRED (default) “Join if exists, else create” 💡 Example: Placing an order → Payment service called Both run in the "same transaction". If payment fails → everything rolls back. 🔹 REQUIRES_NEW “Always start fresh” 💡 Example: Order fails ❌ But you still want to "log the failure" Logging runs in a separate transaction → it gets saved ✅ 🔹 SUPPORTS “Join if exists, else run without transaction” 💡 Example: Fetching optional audit data Transaction or not… it doesn’t care. 🔹 NOT_SUPPORTED “Run without transaction” 💡 Example: Heavy read operation where transaction overhead is unnecessary Suspends existing transaction ⏸️ 🔹 MANDATORY “Transaction must exist” 💡 Example: Critical financial operation If no transaction → throw error 🚨 🔹 NEVER “Transaction must NOT exist” 💡 Example: A method that should never be part of a transaction If one exists → exception 💥 🔹 NESTED “Transaction inside a transaction” 💡 Example: Partial rollback scenario Inner transaction fails → outer can still continue Here’s the real insight Most bugs in transaction handling don’t come from syntax… They come from 𝘄𝗿𝗼𝗻𝗴 𝗽𝗿𝗼𝗽𝗮𝗴𝗮𝘁𝗶𝗼𝗻 choices. Using `@Transactional` is easy. Using it correctly is what makes you a solid backend developer. Next time you add `@Transactional`, don’t stop there… Ask: “How should this transaction behave?” #CoreJava #SpringBoot #JavaDeveloper #Transactional #BackendDevelopment #SoftwareEngineering #Developers #Programming #Developers #RDBMS #SQL #JPA #Hibernate #Database #Microservices #aswintech #SystemDesign
To view or add a comment, sign in
-
Day 27. I put @Transactional in my controller. My senior caught it in code review. That conversation changed how I think about layers. I had this: @RestController public class UserController { @Transactional @PostMapping("/users") public ResponseEntity<?> createUser(@RequestBody User user) { userRepository.save(user); return ResponseEntity.ok("User created"); } } It worked. No errors. Everything looked fine. Looked clean. Until a senior looked at it. Here’s what actually happens: → Transaction starts at the controller → It stays open longer than needed → Higher chance of locks & performance issues → Blurred business logic boundaries That’s when it clicked. Transactions don’t belong in controllers. Controllers should: → Handle HTTP → Delegate work Not manage transactions. So I changed it. (see implementation below 👇) What I learned: → Transactions define business boundaries → They should live close to business logic (service layer) → Not at the API layer The hard truth: → Working code and well-designed code are two different things → Most tutorials only teach you the first one Writing working code is easy. Placing responsibility in the right layer is what makes you a backend developer. If your senior reviewed your controller today — would @Transactional be there? 👇 Drop your take #SpringBoot #Java #BackendDevelopment #CleanCode #Hibernate #JavaDeveloper
To view or add a comment, sign in
-
-
☕ Understanding @Transactional in Spring Boot One annotation that quietly protects your data integrity: @Transactional It ensures multiple DB operations either: ✅ All succeed ❌ Or all rollback No partial data corruption. 🔍 Real Example @Transactional public void transferMoney(Account a, Account b, int amount) { withdraw(a, amount); deposit(b, amount); } If deposit fails → Spring rolls back withdraw automatically. 🧩 What Happens Under the Hood Spring creates a proxy around the method: 1️⃣ Start transaction 2️⃣ Execute method 3️⃣ Commit if success 4️⃣ Rollback if exception 🚨 Critical Rules Many Developers Miss • Works only on public methods • Works only on Spring-managed beans • Self-invocation bypasses transaction • RuntimeException triggers rollback by default 🧠 Production Insight Transactions define system consistency boundaries. Too large → locks & slow DB Too small → inconsistent state 💡 Best Practice Keep transactions: • Short • Focused • Database-only @Transactional is not just annotation magic — it’s a core reliability guarantee in backend systems. #SpringBoot #Java #BackendEngineering #Transactions #LearnInPublic
To view or add a comment, sign in
-
Returning your database Entity directly in your API is a ticking time bomb. Early in my career, I built a simple user profile API. To save time, I just returned the JPA User entity directly from the controller. It worked perfectly, the code was clean, and the PR was approved. A few months later, another developer added a password_hash and two_factor_secret column to the database table to support a new security feature. Because my API was returning the raw entity, it automatically started serializing those new columns. We were suddenly leaking password hashes directly to the frontend without even touching the controller code. After 9 years of building backend systems, this is my biggest pet peeve. Your database schema and your API contract should never, ever be the same thing. The Senior Fix: Use Data Transfer Objects (DTOs). Yes, it feels like boilerplate. Yes, it feels tedious to map an OrderEntity to an OrderResponseDTO. But that separation is what saves you in production. 1. Security: You explicitly control exactly which fields leave your server. 2. Versioning: You can completely refactor your database schema without breaking the mobile app that relies on your API response. 3. Immutability: With modern Java, you can use Records for your DTOs. They are immutable, require zero Lombok boilerplate, and are incredibly fast. Your database represents how data is stored. Your API represents how data is consumed. Don't mix the two. What is your preferred way to map Entities to DTOs in Java? Do you write it manually, or do you use a library like MapStruct? Let's debate below. 👇 #CleanCode #Java #SpringBoot #SoftwareEngineering #BackendDevelopment #API #LLD #SystemDesign
To view or add a comment, sign in
-
Day 13. If you're returning Entities from your API, you're leaking your database. I made this mistake early: @GetMapping("/users/{id}") public User getUser(@PathVariable Long id) { return userRepository.findById(id).orElseThrow(); } Looks clean. But it's a security risk. Here’s what you might be exposing without realizing: → passwordHash → roles → internal flags like isAdmin That's not an API. That's your database leaking through JSON. And you probably didn't even notice it. The fix is simple: Return a DTO. @GetMapping("/users/{id}") public UserDTO getUser(@PathVariable Long id) { User user = userRepository.findById(id).orElseThrow(); return new UserDTO(user.getId(), user.getName(), user.getEmail()); } What you actually gain: → Security — control what leaves your server → Stability — DB changes don’t break your API → Clarity — frontend gets exactly what it needs The rule I follow now: → Entities belong to your database → DTOs belong to the outside world → Never mix the two Returning Entities is easy. Designing contracts is what makes you a backend developer. Are you still exposing entities directly? 👇 Drop it below #SpringBoot #Java #BackendDevelopment #DTO #JavaDeveloper
To view or add a comment, sign in
-
I just finished building a production-grade banking backend from scratch — and honestly, it taught me more than months of tutorials ever did. Here's what I built: A fully functional Banking API using Node.js, Express, MySQL, and Prisma ORM Not a tutorial project. A real system with real patterns: - JWT authentication with hashed passwords - Multi-account support per user - Atomic money transfers (no race conditions) - Double-entry bookkeeping — money never disappears, it moves - Idempotency — same request sent twice? Only executes once - Schema validation with Zod - Structured logging with Winston - Clean 3-layer architecture: Controller → Service → Repository The part that clicked for me? Banking systems can't afford bugs. So every transfer runs inside a database transaction with SELECT FOR UPDATE — which locks the row and forces concurrent requests to queue up. No race conditions. No phantom balances. And idempotency — if a client retries a failed transfer request, the money doesn't move twice. That's the kind of detail that separates a hobby project from a system you'd trust with real money. The architecture is layered intentionally: → Controllers handle HTTP only → Services own the business logic → Repositories talk to the database Swap MySQL for PostgreSQL tomorrow? Only the repository changes. This is the kind of backend work I genuinely enjoy — where the decisions you make early protect you (and your users) later. 🔗 https://lnkd.in/dm8_sHDe #NodeJS #Backend #MySQL #Prisma #SoftwareEngineering #WebDevelopment #BankingTech #CleanCode #JavaScript
To view or add a comment, sign in
-
-
Most of us build backend projects where the database is just… there. Call → query → response. But one question changed how I look at backend systems: •What actually happens when 100s of requests need the database at the same time? Opening a new connection per request feels simple — until you realize it’s one of the most expensive operations in a system. That’s where concepts like "connection pooling" come in. Not as a library feature, but as a design decision: • reuse instead of recreate • limit instead of overload • coordinate instead of collide I explored this by building a small "BANKING STYLE SYSTEM" where operations like deposit, withdrawal, and transfer run inside real transactions — while sharing a limited pool of connections under concurrent load. Thinking in terms of: • bounded resources • thread safety • wait vs fail strategies • transaction boundaries completely shifts how you design backend systems. It also explains why production systems rely on tools like HikariCP — not because they’re convenient, but because they solve hard problems around concurrency and resource management. Lately, I’ve been exploring these ideas by building small systems around them, and it made one thing clear: Good backend engineering is less about writing endpoints, and more about managing what happens under load. Curious — what backend concept changed the way you think about system design? GitHub - https://lnkd.in/gBU6dkwY #BackendDevelopment #SystemDesign #Concurrency #Java
To view or add a comment, sign in
-
-
One of the most abused annotations in Spring is @Transactional. People treat it like a “wrap everything and forget” switch. I’ve seen methods like this: 👉 save to DB 👉 call external payment API 👉 send email 👉 call another microservice 👉 update DB again All inside ONE transaction. Looks clean. Feels safe. It’s not. Because the moment you mix DB + external calls inside a transaction, things start getting messy: ⚠️ external APIs are slow ⚠️ transactions stay open longer (locks, performance issues) ⚠️ if API fails → DB rolls back, but external system doesn’t ⚠️ debugging becomes painful Now you have inconsistent systems and no clear recovery path. The better way? Keep @Transactional boring. ✔️ only DB operations inside ✔️ keep it short ✔️ do external calls before or after ✔️ think about failure scenarios explicitly @Transaction is not a safety net. It’s a boundary. Use it wrong → hidden bugs Use it right → stable systems Learned this the hard way 🙂 #Java #SpringBoot #BackendEngineering #SystemDesign #Microservices #CodeQuality
To view or add a comment, sign in
-
-
In a Spring Boot application, code is structured into layers to keep things clean, maintainable, and scalable. The most common layers are Controller, Service, and Repository each with a clear responsibility. i)Controller * Entry point of the application. * Handles incoming HTTP requests (GET, POST, etc.). * Accepts request data (usually via DTOs). * Returns response to the client. ii)Service * Contains business logic. * Processes and validates data. * Converts DTO ↔ Entity. iii)Repository * Connects with the database. * Performs CRUD operations. * Works directly with Entity objects. Request Flow (Step-by-Step): Let’s understand what happens when a user sends a request: 1. Client sends request Example: `POST /users` with JSON data. 2. Controller receives request * Maps request to a method. * Accepts data in a DTO. ``` @PostMapping("/users") public UserDTO createUser(@RequestBody UserDTO userDTO) { return userService.createUser(userDTO); } ``` 3. Controller → Service * Passes DTO to Service layer. 4. Service processes data * Applies business logic. * Converts DTO → Entity. ``` User user = new User(); user.setName(userDTO.getName()); ``` 5. Service → Repository * Calls repository to save data. ``` userRepository.save(user); ``` 6. Repository → Database * Data is stored in DB. 7. Response Flow Back * Repository → Service → Controller. * Entity converted back to DTO. * Response sent to client. Why DTO is Used: * Prevents exposing internal entity structure. * Controls input/output data. * Improves security. * Keeps layers independent. Why This Architecture Matters: * Clear separation of concerns * Easier debugging & testing * Scalable and maintainable codebase #Java #Spring #SpringBoot #BackendDevelopment #SoftwareEngineering #JavaDeveloper
To view or add a comment, sign in
-
-
𝗔𝗳𝘁𝗲𝗿 𝗿𝗲𝘃𝗶𝗲𝘄𝗶𝗻𝗴 𝗵𝘂𝗻𝗱𝗿𝗲𝗱𝘀 𝗼𝗳 𝗣𝗥𝘀, 𝗜 𝗸𝗲𝗲𝗽 𝘀𝗲𝗲𝗶𝗻𝗴 𝘁𝗵𝗲 𝘀𝗮𝗺𝗲 𝗽𝗲𝗿𝗳𝗼𝗿𝗺𝗮𝗻𝗰𝗲 𝗶𝘀𝘀𝘂𝗲. The developer did everything right: clean code, proper repository, correct relationships. And yet, the application was making 𝟱𝟬 𝗱𝗮𝘁𝗮𝗯𝗮𝘀𝗲 𝗾𝘂𝗲𝗿𝗶𝗲𝘀 to display a single page. Nobody noticed. Until production. This is the N+1 problem. In simple terms: instead of 1 query, your app makes 1 + N, one for each item. You don't see it in your code. You see it in your logs: 1 query for orders, then 1 query per customer. 500 orders = 501 queries. The reflex fix? Switch to EAGER loading. That's often the wrong answer. The real fix is understanding 𝘄𝗵𝗮𝘁 𝘆𝗼𝘂'𝗿𝗲 𝗹𝗼𝗮𝗱𝗶𝗻𝗴 𝗮𝗻𝗱 𝘄𝗵𝗲𝗻. Need the data always? use JOIN FETCH Need it sometimes? use @EntityGraph Need full control? use DTO query N+1 is not a JPA bug. It's a judgment problem: the difference between code that works locally and systems that perform in production. I've attached a visual breakdown of these 3 approaches with real examples. Have you ever shipped something that worked perfectly in dev… but broke in production? #Java #SpringBoot #JPA #Hibernate #Backend #Performance #SoftwareEngineering
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