Spring #Transactional placement - spring

I'm recently playing a little with Spring/JPA2 to understand better how it works. During my experiments I found some strange behaviour. The question is:
Why the following code works well (confirmed record added in db):
#Repository
public class UserDAO {
#PersistenceContext
EntityManager em;
#Transactional
public void add(User user) {
doAdd(user);
}
public void doAdd(User user) {
em.persist(user);
}
}
But the following (#Transactional annotation moved to inner method):
#Repository
public class UserDAO {
#PersistenceContext
EntityManager em;
public void add(User user) {
doAdd(user);
}
#Transactional
public void doAdd(User user) {
em.persist(user);
}
}
Throws exception:
javax.persistence.TransactionRequiredException: No transactional EntityManager available
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:273)
at com.sun.proxy.$Proxy556.persist(Unknown Source)
at com.example.UserDAO.doAdd(UserDAO.java:24)
...

In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
transaction at runtime even if the invoked method is marked with
#Transactional.
Source
The #Transactional annotation support works by wrapping the actual DAO instance in a Proxy, which intercepts the method calls and starts/commits the transaction. In the second example the actual UserDAO instance is calling the doSave method and therefore there is no Proxy to intercept the method call.

Related

Spring JPA: javax.persistence.TransactionRequiredException: no transaction is in progress [duplicate]

I am new to Spring Transaction. Something that I found really odd, probably I did understand this properly.
I wanted to have a transactional around method level and I have a caller method within the same class and it seems like it does not like that, it has to be called from the separate class. I don't understand how is that possible.
If anyone has an idea how to resolve this issue, I would greatly appreciate. I would like to use the same class to call the annotated transactional method.
Here is the code:
public class UserService {
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}
It's a limitation of Spring AOP (dynamic objects and cglib).
If you configure Spring to use AspectJ to handle the transactions, your code will work.
The simple and probably best alternative is to refactor your code. For example one class that handles users and one that process each user. Then default transaction handling with Spring AOP will work.
Configuration tips for handling transactions with AspectJ
To enable Spring to use AspectJ for transactions, you must set the mode to AspectJ:
<tx:annotation-driven mode="aspectj"/>
If you're using Spring with an older version than 3.0, you must also add this to your Spring configuration:
<bean class="org.springframework.transaction.aspectj
.AnnotationTransactionAspect" factory-method="aspectOf">
<property name="transactionManager" ref="transactionManager" />
</bean>
In Java 8+ there's another possibility, which I prefer for the reasons given below:
#Service
public class UserService {
#Autowired
private TransactionHandler transactionHandler;
public boolean addUsers(List<User> users) {
for (User user : users) {
transactionHandler.runInTransaction(() -> addUser(user.getUsername, user.getPassword));
}
}
private boolean addUser(String username, String password) {
// TODO call userRepository
}
}
#Service
public class TransactionHandler {
#Transactional(propagation = Propagation.REQUIRED)
public <T> T runInTransaction(Supplier<T> supplier) {
return supplier.get();
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public <T> T runInNewTransaction(Supplier<T> supplier) {
return supplier.get();
}
}
This approach has the following advantages:
It may be applied to private methods. So you don't have to break encapsulation by making a method public just to satisfy Spring limitations.
Same method may be called within different transaction propagations and it is up to the caller to choose the suitable one. Compare these 2 lines:
transactionHandler.runInTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
transactionHandler.runInNewTransaction(() -> userService.addUser(user.getUserName, user.getPassword));
It is explicit, thus more readable.
The problem here is, that Spring's AOP proxies don't extend but rather wrap your service instance to intercept calls. This has the effect, that any call to "this" from within your service instance is directly invoked on that instance and cannot be intercepted by the wrapping proxy (the proxy is not even aware of any such call). One solutions is already mentioned. Another nifty one would be to simply have Spring inject an instance of the service into the service itself, and call your method on the injected instance, which will be the proxy that handles your transactions. But be aware, that this may have bad side effects too, if your service bean is not a singleton:
<bean id="userService" class="your.package.UserService">
<property name="self" ref="userService" />
...
</bean>
public class UserService {
private UserService self;
public void setSelf(UserService self) {
this.self = self;
}
#Transactional
public boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
public boolean addUsers(List<User> users) {
for (User user : users) {
self.addUser(user.getUserName, user.getPassword);
}
}
}
With Spring 4 it's possible to Self autowired
#Service
#Transactional
public class UserServiceImpl implements UserService{
#Autowired
private UserRepository repository;
#Autowired
private UserService userService;
#Override
public void update(int id){
repository.findOne(id).setName("ddd");
}
#Override
public void save(Users user) {
repository.save(user);
userService.update(1);
}
}
This is my solution for self invocation:
public class SBMWSBL {
private SBMWSBL self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(SBMWSBL.class);
}
// ...
}
You can autowired BeanFactory inside the same class and do a
getBean(YourClazz.class)
It will automatically proxify your class and take into account your #Transactional or other aop annotation.
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advised, as it may look strange to colleagues. But it works with singletons, is easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution as described in Espens answer.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class PersonDao {
private final PersonDao _personDao;
#Autowired
public PersonDao(PersonDao personDao) {
_personDao = personDao;
}
#Transactional
public void addUser(String username, String password) {
// call database layer
}
public void addUsers(List<User> users) {
for (User user : users) {
_personDao.addUser(user.getUserName, user.getPassword);
}
}
}
The issue is related to how spring load classes and proxies. It will not work , untill you write your inner method / transaction in another class or go to other class and then again come to your class and then write the inner nested transcation method.
To summarize, spring proxies does not allow the scenarios which you are facing. you have to write the 2nd transaction method in other class
There is no point to use AspectJ or Other ways. Just using AOP is sufficient. So, we can add #Transactional to addUsers(List<User> users) to solve current issue.
public class UserService {
private boolean addUser(String userName, String password) {
try {
// call DAO layer and adds to database.
} catch (Throwable e) {
TransactionAspectSupport.currentTransactionStatus()
.setRollbackOnly();
}
}
#Transactional
public boolean addUsers(List<User> users) {
for (User user : users) {
addUser(user.getUserName, user.getPassword);
}
}
}

#Transactional has no effect on JpaRepository

I have a parent transaction at controller layer, but I want to start a new transaction when I call a repository, to achieve this I tried annotating Repository interface as below
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{ }
However this seems to not start a new transaction upon calls to EventRepo#save. Why?
Here is my service layer.
public interface IApplicationService {
void save(Event event);
}
#Service
public class ApplicationService implements IApplicationService {
#Autowired
private EventRepo eventRepo;
#Override
public void save(Event event) {
eventRepo.save(event);
}
}
It is in turn called from controller layer
#RequestMapping(value="/{indicator}", method=RequestMethod.POST)
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
#ResponseBody
public String processRequest(#PathVariable Integer indicator) {
Event event = new Event("Student1");
service.save(event);
if(indicator != 0) {
throw new RuntimeException();
}
return "Success";
}
However everything works perfectly if I annotate Service interface with #Transactional
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface IApplicationService {
void save(Event event);
}
When I say working what is mean is, if I run the below curl commands I will see 2 rows in h2 db for Event entity
curl -X POST http://localhost:8080/1
curl -X POST http://localhost:8080/0
I understand it is good to control transactions at Service layer then repository or controller layer, constructing situation this way makes it easy to demonstrate the problem.
Spring boot starter version is 2.5.6
below dependencie have versions managed by springboot starter
spring-boot-starter-data-jpa
spring-boot-starter-web
lombok
h2
Here is a thread that suggests it should be ok to annotate Repository layer although discourages it.
#Transactional on a JpaRepository
In this Spring article we can read the following:
Additionally, we can get rid of the #Transactional annotation for the method as the CRUD methods of the Spring Data JPA repository implementation are already annotated with #Transactional.
To me, this means that whatever #Transactional annotation you add to your EventRepo will be overridden by the #Transactional annotation mentioned above in the CRUD methods. Having said that, I really doubt #Transactional annotation has any effect in JpaRepository methods. It would have in your own custom methods, but it seems to me that it has none in the inherited methods.
In order to apply your own transactional settings in EventRepo#save override the save method:
#Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRES_NEW)
public interface EventRepo extends JpaRepository<Event, Integer>{
#Override
Event save(Event event);
}
Explanation
Spring ignores your #Transactional annotation because it cannot find the save method in the EventRepo proxy and applies the default transaction settings from the parent CrudRepository interface.
Further reading: How Does Spring #Transactional Really Work?

Transactional and Stream in Spring

I try to understand why this code doesn't work
In component:
#PostConstruct
public void runAtStart(){
testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
And repository :
public interface PersonRepository extends JpaRepository<Person, Long> {
Stream<Person> findTop10ByFirstName(String firstName);
}
I get:
org.springframework.dao.InvalidDataAccessApiUsageException: You're trying to execute a streaming query method without a surrounding transaction that keeps the connection open so that the Stream can actually be consumed. Make sure the code consuming the stream uses #Transactional or any other way of declaring a (read-only) transaction.
One key thing about Spring is that many annotated features use proxies to provide the annotation functionality. That is #Transactional, #Cacheable and #Async all rely on Spring detecting those annotations and wrapping those beans in a proxy bean.
That being the case, a proxied method can only be used when invoked on the class and not from within the class. See this about the topic.
Try:
Refactoring and call this #Transactional method from another class in your context, or
By self-autowiring the class into itself and calling the #Transactional method that way.
To demonstrate (1):
public class MyOtherClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional`
myTestStreamClass.testStream();
}
}
To demonstrate (2):
#Component
public class MyTestStreamClass {
#Autowired
private MyTestStreamClass myTestStreamClass;
#PostConstruct
public void runAtStart(){
// This will invoke the proxied interceptors for `#Transactional` since it's self-autowired
myTestStreamClass.testStream();
}
#Transactional(readOnly = true)
public void testStream(){
try(Stream<Person> top10ByFirstName = personRepository.findTop10ByFirstName("Tom")){
top10ByFirstName.forEach(System.out::println);
}
}
}

Where should we use #Transactional and where is Service layer?

I have rest style controller in Spring. In controller I have injected dao interfaces. From controller I persist data. In the other words, I have like REST web service. people sends me data, and I persits it.
/**
* Payment rest controller which receives
* JSON of data
*/
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestDao dao;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
At the moment I have #transaction annotation in Dao classes. For instance:
import org.springframework.transaction.annotation.Transactional;
#Component
#Transactional
public interface TestDao {
#Transactional(propagation = Propagation.REQUIRED)
public void first();
}
I have read that this is very bad style. Using this answer at stackoverflow , here is explain and examples why is this bad - we must not add this annotation in DAO and in controller too. We must add it in service layer.
But I don't understand what is the service layer? Or where is it? I do not have anything like this.
where should I write #Transactional annotation?
Best regards,
According to the cited post, you should design your classes somehow like this (rather pseudocode):
controller (responsible for handling clients' requests/responses)
#Controller
#RequestMapping("/data")
public class TestREST {
#Autowired
private TestService service;
public void storePayment(PaymentDTO dto) {
service.storePayment(dto); //request from a client
}
public PaymentDTO getPayment(int paymentId) {
return service.getPayment(paymentId); //response to a client
}
}
service layer (also called business layer, responsible for business logic - knows what to do with incoming messages, but does not know where they come from).
public class TestServiceImpl {
#Autowired
private TestDao dao;
#Transactional(propagation=Propagation.REQUIRED) //force transaction
public void storePayment(PaymentDTO paymentDto) {
// transform dto -> entity
dao.storePayment(paymentEntity); //read-write hence transaction is on
}
#Transactional(propagation=Propagation.NOT_SUPPORTED) //avoid transaction
public Payment getPayment(int paymentId) {
return dao.findPayment(paymentId); //read-only hence no transaction
}
}
data access layer (also called persistence layer, responsible for accessing database - knows how to use entity model / ORM, does not know anything about the upper service layer)
public class TestDAOImpl {
#PersistenceContext
private EntityManager em;
public void storePayment(PaymentEntity paymentEntity) {
em.persist(paymentEntity);
}
public PaymentEntity getPayment(int paymentId) {
return em.find(PaymentEntity.class, paymentId);
}
}
By this approach you get separation of concerns mentioned in the post. From the other hand such an approach (business layer vs data access layer) got a little dose of criticism from Adam Bien's on his blog ("JPA/EJB3 killed the DAO"). As you can see there is no a single solution for the problem, but I encourage to read some other opinions and apply the solution you find the most suitable for your needs.
When you call two Dao methods first & second from controller, 2 transactions will be done, one with starts before first method and ends after it's execution and the second one starts before second method starts and ends after it's execution. Whereas you create an additional class in between controller and dao (usually this is called service layer) and annotate it with #Transactional and call multiple Dao methods in it, a transaction is started at the start of service method and all the dao calls will be executed and transaction will be closed, which is what you require. And inject the Service into Controller.
Controller -> Service -> Dao
#Controller
#RequestMapping("/data")
public class PaymentTransaction {
#Autowired
private TestService service;
#RequestMapping(value = "/test", method = RequestMethod.POST)
#ResponseBody()
public String test(HttpServletRequest request) {
...
}
}
#Service
#Transactional
public class TestService {
#Autowired
private TestDao dao;
#Transactional
public void serviceCall(){
dao.first();
dao.second();
}
}

How to call #Transactional method from one service from other #Transactional method from other service

I have:
1) Service:
#Service("scanner")
#Transactional
public class Scanner
{
#Inject
AnalyzerService analyzerService;
#Transactional
private void scan() {
analyzerService.analyze();
}
}
2) Service:
#Service
public class AnalyzerService
{
#Inject
AnalyzerDao analyzerDao;
#Transactional
public void analyze() {
List<AnalyzerResult> items;
// code filling items list removed;
save(items);
}
#Transactional
private void save(List<SomeType> items) {
analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
}
}
3) Dao:
#Repository
public class AnalyzerDao extends GenericDaoImpl<AnalyzerResult>
{
//all needed methods for find, edit, delete and save which works fine in other cases.
}
Question:
Why after call analzyerDao.save(items) DB is still empty? Is it problem with transaction some how?
When I invoke flush() method and getSession().getTransaction().commit() just after line analyzerDao.save(items) then records appearing in DB but exception is thrown:
Caused by: org.springframework.transaction.TransactionSystemException: Could not commit Hibernate transaction; nested exception is org.hibernate.TransactionException: Transaction not successfully started
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:660)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:754)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:723)
at org.springframework.transaction.interceptor.TransactionAspectSupport.commitTransactionAfterReturning(TransactionAspectSupport.java:393)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:120)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at com.sun.proxy.$Proxy44.execute(Unknown Source)
at org.quartz.core.JobRunShell.run(JobRunShell.java:223)
... 1 more
Caused by: org.hibernate.TransactionException: Transaction not successfully started
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:127)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:656)
... 9 more
How should implementation be done to work 'save' method fine?
What should I do to save items just in line analyzerDao.save(items) and not only after first transaction will be finished?
What you need is a new transaction just to save what you need to save. You can achieve this by configuring the propagation of #Transactional annotation to REQUIRES_NEW.
Unfortunately your case is a bit tricky, because you are invoking a method within this context when you do save(items);, this means the transaction interceptor will not intercept such invocation, therefore you have the possibility to inject the service to a field hold by itself and invoke it on the injected service instead of this forcing the invocation to that method be intercepted by the transaction interceptor, please try the following implementation:
#Service
public class DefaultAnalyzerService implements AnalyzerService {
#Inject
AnalyzerDao analyzerDao;
#Inject
AnalyzerService analyserService;
#Transactional
#Override
public void analyze() {
List<AnalyzerResult> items;
// code filling items list removed;
analyserService.save(items);
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
#Override
public void save(List<SomeType> items) {
analyzerDao.save(items); // <--- Why after call save items are not saved in DB?
}
}
Another thing that changed was the visibility of save(List<SomeType> items), that is public now on in order to be intercepted by transaction interceptor and an interface was extracted. This is needed due to limitations with spring, but you can use AspectJ to handle such interceptor, therefore please take a look here.
Data won't appear in the database until the transaction is committed. For #Transactional methods, the transaction is committed by Spring after returning from the method.
By the way, #Transactional on private methods has no effect, so Scanner.scan() is not transactional at all.

Resources