Question Details

No question body available.

Tags

java spring spring-boot hibernate

Answers (3)

February 11, 2026 Score: 3 Rep: 364 Quality: Medium Completeness: 60%

QUESTION 1. Is calling repository.save(entity) necessary in this case?

ANSWER: No, It's not required if the entity is managed.

DESCRIPTION:

If you Fetch the entity inside a @Transactional method, Modify its fields or Do not detach it, then Hibernate’s dirty checking will automatically detect changes and flush them at transaction commit.

You need repository.save() when Creating a new entity, Working with a detached entity, Outside a transactional context and Using merge() semantics intentionally.

QUESTION 2: Is avoiding save() best practice or just optimization?

ANSWER: This is not just optimization — it’s correct usage of JPA’s persistence context.

DESCRIPTION:

Calling save() on a managed entity is redundant, adds confusion, may trigger unnecessary merge(), shows misunderstanding of JPA lifecycle.

No save() is the cleanest and most idiomatic JPA usage.

QUESTION 3: Does @Transactional improve performance?

ANSWER: Not directly.It is primarily for consistency, atomicity, isolation, proper session management and lazy loading support.

DESCRIPTION:

What @Transactional Actually Does When applied opens a transaction, binds Hibernate session to the thread, enables dirty checking and commits or rolls back properly.

when without @Transactional each query may run in auto-commit mode, no managed persistence context, dirty checking won’t work and lazy loading fails.

So, @Transactional does NOT make code faster by itself, But it prevents bad performance patterns,

February 11, 2026 Score: 2 Rep: 6,290 Quality: Low Completeness: 80%

The main problem is the following:

  • @Transactional comes from spring-tx

  • Repository comes from spring-data

  • JPA comes from Hibernate

and you are trying to bring all those stuff together and make it work...

However, there is no chance to do that both robust and convenient. Let's consider the following method:

boolean withdraw(/ @Entity / Account acc, long amount) {
   ...
}

How can you be sure the changes performed in that method will be sooner or later persisted? You have no idea what is the actual state of Account acc entity, it could be managed, transient or detached. There are two options:

  • inject AccountRepository in your method and call save

  • pass accountId instead of Account acc and both place @Transactional over the method and inject AccountRepository as well

So, my opinion on this topic is the following: calling repository.save(entity) in general is better than not calling it, regardless of specifications and assumptions from third-parties.

Does @Transactional itself improve performance, or is it mainly for consistency?

In general, it does (subsequent getById calls won't touch DB), however the feature is dangerous.

February 11, 2026 Score: 0 Rep: 13,720 Quality: Medium Completeness: 60%

Use appropriate abstraction layers in your application.

There should be a facade layer that provides access to the use cases of your application. In the facade layer you can manage the transactions and orchestrate repositories and services to execute the logic needed for the use case. The facade api itself does not know about the persistence - so it would take entity ids and dtos as descriptions of what to do with the entity identified by the id - something like void withdraw(String accountId, WithdrawalRequest withdrawal)

There should be a service layer to operate on entities to implement logic how entities are changed as required to implement use cases. For example when the application should enable users to withdraw from their accounts there should be an AccountService that features a void withdraw(Account account, Withdrawal withdrawal) where WithdrawalRequest from the facade layer and Withrawal from the service layer are different classes probably sharing some properties but not necessarily all.

And there should be repository layer that implements repositories to fetch entities from the database like you usually have with jpa.

Then this can come together in the facade implementation like this:

@Transactional
void withdraw(String accountId, WithdrawalRequest withdrawalRequest) {
    Account account = accountRepository.findById(accountId);
    Withdrawal withdrawal = withdrawalFactory.create(withdrawalRequest);
    accountService.withdraw(account, withdrawal);
}

In this facade implemenation you are also aware of the the entity being managed because it is loaded from the repo in that same method and therefore an accountRepository.save(account) invocation being unnecessary. So there is objective evidence why such a call is being left out. In other facade implementation methods - for example when opening an account - you would have that account being there because you used for example an AccountFactory to create it and therefore explicitly need the save(account). But this is all possible just because of the fact that AccountFactory and AccountService impelemntations do know nothing about the repository layer - maintaining a high degree of separation of concerns. With this in mind using save(account) always is just a matter of taste and your project preferences. It has no real implications - also not performance wise. This also leads to answer your second question to be not just an optimization but a necessity to call save within a transaction - because it needs to be part of whatever additional consistency checks a more complex application is going to do. They can not be split apart and need the atomicity of the transaciton. @Transactional is necessary for consistency. It has some implications on performance - especially when considering jdbc connection pooling, jdbc connection utilization and latency. Having entity load and save as part of different transactions may cause multiple roundtrips to the connection pool per requests to obtain a connection. Depending on load this may block for a while and therefore increase latency per request. However having a connection being borrowed from the pool and idling while a longer lasting logic operation takes places makes that connection inaccessible to utilized by other processes.