Could someone explain whether it is correct to use the @Transactional
annotation in repositories at the interface level or method level?
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Long> {
@Transactional
User findByUsernameAndBirthday(String username);
}
Could someone explain whether it is correct to use the @Transactional
annotation in repositories at the interface level or method level?
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
@Repository
@Transactional
public interface UserRepository extends CrudRepository<User, Long> {
@Transactional
User findByUsernameAndBirthday(String username);
}
Both will work, the thing I suppose you wanted to ask is when to put it on interface or method level/procs and cons.
The trick is when you put @Transactional on the interface level that will imply that ALL methods in that repository will be transactional by default, that is not recommended because some methods, like read methods, do not require a transaction.
Putting @Transactional on method level ensures that only that specific method runs inside of the transaction ( DELETE, SAVE, UPDATE ).
Also keep in mind, since you tagged Spring Data, it already provides transactional behavior by default, so adding @Transcational on that interface seems kinda redundant.
Another important aspect of @Transactional is rollback behavior.
Imagine if you had 3 methods, depositMoney, showMoney, and withdrawMoney. And imagine if you were to place @Transactional on top of the class. Each call of that each method would trigger a new transaction, and that is perfectly fine until you need something called rollback that Hibernate/Spring offers. The difference between having all 3 methods in a single transaction like
@Transactional
public void transferMoney(String accNumber) {
depositMoney(accNumber, 100);
// no showMoney because it does not need transaction
// since it is read only same as your findBy method
withdrawMoney(accNumber, amount);
}
would be very handy if your depositMoney method fails, or showMoney, all would be rollbacked and deposited money would not be lost in the transaction because it is separated from other methods.
But if you were to call methods individually, then transactions would be separated and in case of failure of any method, there would be nothing to rollback compared to other methods.
You still can have the same behavior even if you have @Transactional on top of the interface, but placing @Transactional on top of the method provides more control, at least in my opinion.
In short, you should use method level @Transactional for better control over transactions, avoid placing it on top of the interface since we do not need transactions for read methods/Spring Data already handles transactions by default. There is a good practice about placing @Transactional(readOnly=true) on read methods and you can read more about it HERE
So your code should look like
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
public interface UserRepository extends CrudRepository<User, Long> {
@Transactional(readOnly = true)
User findByUsernameAndBirthday(String username);
}