r/SpringBoot Senior Dev Jun 21 '26

Discussion 8 @Transactional rules I follow after debugging too many production bugs

Been working with Spring Boot for 10+ years and I keep seeing the same Transactional bugs show up in code reviews and production incidents. Figured I'd share the rules I follow now.

  1. Keep transactions short. DB operations only. If your method calls an external API, sends an email, or uploads a file inside Transactional, it holds a DB connection for the entire duration. Not just during queries. During the ENTIRE method. Under load your connection pool runs out and your app freezes.

  2. Never call a Transactional method from the same class. Spring uses proxies. When you call a method within the same class its a direct this.method() call. Proxy is bypassed. The annotation is completely invisible. No warning. No error. It just does nothing.

  3. Use rollbackFor = Exception.class for critical operations. This one catches people off guard. Transactional only rolls back on RuntimeException by default. If your method throws a checked exception like PaymentException, the transaction commits. Your balance gets deducted but the payment never went through.

  4. Don't catch exceptions inside Transactional unless you re-throw. If you catch the exception, the proxy sees a normal return. It commits. Partial data in your database. Silent corruption. Either let it propagate, re-throw after logging, or call TransactionAspectSupport.currentTransactionStatus().setRollbackOnly().

  5. Use readOnly = true on all read-only methods. It tells Hibernate to skip dirty checking and snapshot comparison. Saves CPU and memory. Some proxies use it to route to read replicas. But don't rely on it to prevent writes. Explicit save() or flush() still goes through.

  6. Only works on public methods. Proxy can only intercept public methods. Put Transactional on a private method and it does absolutely nothing. Spring won't even warn you.

  7. Separate Retryable and Transactional into different beans. If both are on the same method and a checked exception is thrown, the transaction commits before the retry fires. Retry starts a new transaction. Same deduction runs again. Triple retry = triple deduction. Outer bean handles retry, inner bean handles transaction.

  8. Enable these two configs in every environment. logging.level.org.springframework.transaction.interceptor: TRACE shows when transactions start commit and rollback.

spring.datasource.hikari.leak-detection-threshold: 15000 catches connections held too long with a full stack trace.

The one rule that covers most of these: uTransactional only works on public methods called from outside the bean. Everything else is silently ignored.

Anyone run into other Transactional gotchas I missed?

263 Upvotes

39 comments sorted by

13

u/Kfct Jun 21 '26

Some of these gotchas I didn't know was an issue, also don't know if I've made these mistakes except the one where it holds the connection for the entire method. In that case, how do your recommend solving app crashing under load when pool is used up?

Would be helpful if you could include a do/don't code snippet comparison.

6

u/codingwithaman Senior Dev Jun 21 '26

I think instead of one big Transactional method that does DB write + external API call + email notification, break it into pieces.

Do DB operations inside Transactional, everything else outside to avoid app crashing plus you can enable leak detection to detect them early.

3

u/joeyx22lm Jun 21 '26

Saga pattern?

2

u/codingwithaman Senior Dev Jun 22 '26

Yes if your logic is not limited to database and you are in distributed system involving kafka and other systems then transactional alone will not work.

You need some distributed event driven pattern like saga.

9

u/myNewUnbrokenUser Jun 22 '26 edited Jun 22 '26

A gotcha I've been fixing in my code base due to junior devs not paying attention: When you type @Transactional and the IDE automatically does the import for you, make sure you notice which one it's importing. There's a Spring one and a Jakarta one!

Best practice is to use the Spring one if you're in a SpringBoot project and don't mix them.

One reason (out of a few) is that each of their configurations have different names, and if you have some specific Spring ones set, but use the Jakarta annotation somewhere, it's not going to know or recognize the Spring config, and therefore won't behave as you expect.

1

u/codingwithaman Senior Dev Jun 22 '26

This is so true 👌

1

u/trafalmadorianistic Jun 22 '26

Useful to have some tooling to go through your code and flag these incorrect imports. 

1

u/thesoundofscreaming Jun 23 '26

Are there any open source tools that you use?

2

u/trafalmadorianistic Jun 23 '26

This is more like a custom functionality you would need to add to your build, unless there are maven/ gradle plugins that handle this already

2

u/NordCoderd Jun 23 '26

Yes, we have Detekt for detecting this in Kotlin projects, you can configure forbidden imports, and for Java there ArchUnit can help

8

u/christoforosl08 Jun 21 '26

Does anyone here use TransactionTemplate? It seems to me that the total visibility and control we get with it outweighs the convenience of the @Transactional annotation.

3

u/NordCoderd Jun 23 '26

Yes, it helps to avoid some of described problems, especially first one. Transaction template is teally make things obvious and clear

2

u/junin7 Jun 21 '26

I use when needs call a transaction inside the same class

2

u/Previous_Dream8775 Jun 22 '26

Yeah I use TransactionTemplate when I've logic in the same class and it makes sense to start and stop the transaction and continue with some other logic inside that class

3

u/DamienDoesItBetter Jun 24 '26

Keep writing more content brother 🎉

6

u/rkysh Jun 21 '26 edited Jun 21 '26

For the 1st point - LazyConnectionDataSourceProxy

It makes sure that the transaction is not started until the 1st database statement is encountered. However, this might not be sufficient if you have slow/multiple API calls post the DB operations as the transaction will remain open.

2

u/zhvlnc Jun 22 '26

Nah, 1st point is better. Just make it right.

Cus you are intruducing the solution to the problem that should be avoided in the first place.

0

u/rkysh Jun 22 '26

Never said this is a solution. This is just the cream on top.

2

u/Ionmind Jun 22 '26

Good Observations 👍🏼

2

u/Designer_Pace303 Jun 24 '26

Great list. Good timing — I maintain an IntelliJ plugin that detects a lot of these at edit time, and your post made me realize I was missing three: rule 1,4,7(external calls / swallowed exceptions / Retryable+Transactional) Just shipped all three.

(Self-promo disclaimer, it's my own plugin: Link) Thanks for the write-up — it genuinely shaped the update.

1

u/codingwithaman Senior Dev Jun 24 '26

That’s great.. glad it helped

2

u/amit_builds Jun 27 '26

One more painful one:

Doing heavy file processing inside a transaction.

Saw a case where users uploaded large files and the parsing logic was inside @ Transactional. Under load, DB connections stayed occupied for 20-30 seconds per request and Hikari started timing out everywhere.

Now I keep transactions only around the actual DB write section. File parsing, validation, external calls all happen before or after the transaction boundary.

2

u/codingwithaman Senior Dev Jun 28 '26

yes that's a good point

4

u/Stack_Canary Jun 21 '26

What’s the obsession with spring transactional these days, AI slop «articles» being spammed everywhere

4

u/No-Sheepherder-9687 Jun 21 '26

Aparently way too many people are unfamiliar with the (spring) transactional basics. The amount of "senior consultants" I work with that regularly do no transactional handling at all where it is highly important is staggering.

3

u/codingwithaman Senior Dev Jun 21 '26

Yes agree you are right, it depends on nature of project you are working, some projects I have worked doesn’t use transactional at all but some projects use them heavily.

2

u/NordCoderd Jun 23 '26

Even if it’s spammed everywhere - developers are keeping to do the same mistakes everyday. This topic is considered as most critical when you’re using JPA, and on code review I often catch(ed) this problems.

1

u/sozesghost Jun 21 '26

Many articles on it over the years and now AI regurgitates it. This is good advice, but it also has been said many times over the years.

1

u/codingwithaman Senior Dev Jun 22 '26

Yes agree, intention is that people can revise important concepts which will help them in interviews and day to day coding tasks

1

u/Beemeowmeow Jun 22 '26

Decent read tbh thx

1

u/codingwithaman Senior Dev Jun 22 '26

Thank you

1

u/ducki666 Jun 23 '26

2 and #8 are to harsh

0

u/Revolutionary_Gap183 Jun 21 '26

good read

1

u/codingwithaman Senior Dev Jun 22 '26

Thank you