Indirect Hibernate/JPA method call loses transaction - java

I'm using Spring/JPA2/hibernate with this code:
class A {
#Autowired
B b;
#RequestMapping("/test")
public void test(final HttpServletRequest r1, HttpServletResponse r2) throws ... {
b.inner(); // Works
b.outer(); // javax.persistence.TransactionRequiredException:
// no transaction is in progress ... :|
}
#Component
class B {
#PersistenceContext
EntityManager em;
public void outer() { inner(); }
#Transactional
public void inner() { em.flush(); }
}
Why does inner() only when called indirectly loose the transaction?

http://static.springsource.org/spring/docs/current/reference/transaction.html#transaction-declarative-annotations
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.
Consider the use of AspectJ mode (see mode attribute in table below) if you expect self-invocations to be wrapped with transactions as well. In this case, there will not be a proxy in the first place; instead, the target class will be weaved (that is, its byte code will be modified) in order to turn #Transactional into runtime behavior on any kind of method.
The #Autowired reference B b (inside class A), is wrapped with a Spring AOP transaction-aware proxy.
When b.inner() is called, you are invoking it on the transaction-aware instance, and it is marked as #Transactional. Thus a Spring managed transaction is started.
When b.outer() is called, it is also on a transaction-aware instance, but it is not #Transactional. Thus a Spring managed transaction is not started.
Once you are inside the invocation of outer() the call to inner() is not going through the transaction-aware proxy, it is being invoked directly. It is the same as this.inner(). Since you are invoking it directly, and not through the proxy, it does not have the Spring transaction-aware semantics.
Since no transaction has been started, this results in the TransactionRequiredException.
Possible solutions include making the method outer() #Transactional.
#Transactional
public void outer() { inner(); }
Or making the entire class #Transactional.
#Transactional
#Component
class B {
#PersistenceContext
EntityManager em;

The transactional context lasts over the scope of your spring bean's entire lifespan. #Transactional notation has a scope of the entire component and you should annotate your #Component as #Transactional eg
#Transactional
#Component
class B {
#PersistenceContext
EntityManager em;
public inner() { }
public outer() { }
}
The methods inner and outer should accomplish individual units of work. If you need some helper function or what have you that is fine, but the unit of work that requires the transactional boundary should be scoped to each method. See the spring docs on #Transactional http://static.springsource.org/spring/docs/3.0.x/reference/transaction.html

Related

Spring Data: JPA and Nested Transaction

I am having a problem with Spring JPA Data and nested transactions. Following are two methods with a nested transaction of my service.
#Service
public UserService {
#Transactional
public User createUser(UserDto userDto) {
....
user = saveUser(user);
sendEmail(user);
....
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public User saveUser(User user) {
return userRepository.save(user);
}
It happens there is one scenario that the method userRepository.save() should throw an exception but somehow is not being thrown, it looks like it is waiting the parent transaction to be finished. I was expecting the exception being thrown on the saveUser method and the sendEmail method not even to be executed.
Because the method UserService.saveUser have the propagation set to Propagation.REQUIRES_NEW I was expecting that transaction to be commited (the SQL statement to be executed) and any exception being propagated.
I did not setup anything related with Transaction, so i believe that the flush mode is set to AUTO.
Can anyone spot what i am doing wrong or what is my misconception?
It's because you're invoking #Transactional method from within same bean.
#Transactional only works on methods invoked on proxies created by spring. It means, that when you create a #Service or other bean, method called from the outside will be transactional. If invoked from within bean, nothing will happen, as it doesn't pass through proxy object.
The easiest solution would be to move the method to another #Service or bean. If you really want to keep it within same component, then you need to invoke it, so that it gets wrapped in proxy by spring AOP. You can do this like that:
private YourClass self;
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void postContruct(){
self = applicationContext.getBean(YourClass.class);
}
Then invoking method on self would result in opening a transaction.
In other words: you are not experiencing any of those anomalies, because #Transactional over saveUser does not work.

Why Spring Bean Self Invocation is working here for Transaction

Environment :
Tomcat 6
Spring 4
Hibernate 4
Spring MVC
Code :
I have below service layer class :
public interface AbhisheskService {
public List<AbhishekDTO> findByMatchingCriteria(AbhishekDetailsSearchDTO searchDto);
}
#Service("abhishekService")
public class AbhishekServiceImpl implements AbhisheskService {
#Autowired
private AbhishekDao abhishekDao;
#Transactional
public List<AbhishekDTO> findByMatchingCriteria(AbhishekDetailsSearchDTO searchDto) {
return getAbs();
}
public List<AbhishekDTO> getAbs(){
Abhishekdetails absDt = this.abhishekDao.get(4L);
return null;
}
}
The AbhishekDao is a standard DAO layer interface which extends GenericDAO super interface.
public interface AbhishekDao extends GenericDAO<Abhishekdetails, Long>{
public List<Abhishekdetails> findByMatchingCriteria(AbhishekDetailsSearchDTO searchDto);
}
My question is :
findByMatchingCriteria method is marked with #Transactional.
This method calls another method getAbs which is NOT MARKED AS #Transactional and it is invoked within findByMatchingCriteria (self-invocation).
As per my understanding since :
1)findByMatchingCriteria is calling getAbs within itself (self-invocation) , getAbs() method SHOULD NOT run inside transaction. Since it is bypassing dynamically created proxy here
2)Moreever getAbs doesn't have #Transactional annotation on it.
3)But when getAbs calls this.abhishekDao.get(4L) everything works fine and a record with ID 4L is retrieved. The DAO bean is calling sessionFactory.getCurrentSession() inside it to get object from Db. But why is this working ?
Since there SHOULD NOT BE any active transaction.
4)Why is above code working ? A lot of posts on Spring Transaction management state that self invocation will not work. (Even spring docs).
Then why is above set up working ?
Am I amissing anything here ?
Or My understanding of spring transaction is incorrect?
Please repply as I am getting confused here
The way it works is:
- AbhishekServiceImpl bean is wrapped in a proxy.
findByMatchingCriteria is #Transactional, so before the method is invoked Spring gets new database connection from the connection pool and sets auto commit to false.
The transactions are bound to a thread, so the other methods on this thread will use this connection.
The methods findByMatchingCriteria and getAbs are executed
after findByMatchingCriteria Spring calls commit on the connection(or rollback if RuntimeException occurs).
So your code is in transaction that's around findByMatchingCriteria
A case where a transaction will not be created is if you have #Transactional on getAbs , but not on findByMatchingCriteria(reverse the calling) and you call findByMatchingCriteria outside of the service. But if you call only getAbs outside of the service it will be in transaction.
More clear example:
#Service
public class MyServiceImpl implements MyService{
#Autowired
private MyDao myDao;
#Transactional
public List<T> transactionalMethod() {
return this.myDao.get(4L);
}
public List<T> notTransactionalMethod(){
return transactionalMethod();
}
}
In some other class:
#Component
public class SomeClass {
#Autowired
private MyService myService;
public void someMethod(){
myService.transactionalMethod();//will be in transaction. Here actualy you go to the proxy first and then it calls the real method.
myService.notTransactionalMethod();//will not be in transaction and hibernate will throw an error.
//You go to the proxy, but doesent do anything special because the method is not transactional and it calls the real method,
//but here you dont't got trough the proxy so the #Transactional is ignored.
}
}

Proxied REQUIRES_NEW method doesn't rollback (spring)

I have spring injecting a service into itself to allow the service to make transactional calls to itself. Unfortunately, I'm finding that a requires_new method that is throwing a NullPointerException and being caught is not rolling back the new transaction. The outer transaction is not interrupted which is what I want, but I'm having trouble explaining why the requires new transaction isn't rolled back. Any ideas?
#Service(value="orderService")
#Transactional
public class OrderServiceImpl implements OrderService {
#Resource
private OrderService orderService; // transactional reference to this service
public void requiredTransMethod(){
try {
orderService.requiresNewTransMethod();
}catch(Throwable t){
LOG.error("changes from requiresNewTransMethod call should be rolled back right?", t);
}
}
#Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewTransMethod() {
// database changes that are NOT rolled back
throw new NullPointerException("bla bla bla");
}
}
This could be an instance of transactional annotations not working because you are calling them from within the same class.
The way Spring's AOP implementation works (by default) is by using proxy classes, which will not work as expected for method invocations from within the same class.

Spring #Transactional doesn't work for an annotated method when called from within service class

In below code , when methodInner() is called from within methodOuter, should be under transaction bounds. But it is not.
But when methodInner() is called directly from MyController class , it is bound by transaction. Any explanations?
This is controller class.
#Controller
public class MyController {
#Autowired
#Qualifier("abcService")
private MyService serviceObj;
public void anymethod() {
// below call cause exception from methodInner as no transaction exists
serviceObj.methodOuter();
}
}
This is service class.
#Service("abcService")
public class MyService {
public void methodOuter() {
methodInner();
}
#Transactional
public void methodInner() {
.....
//does db operation.
.....
}
}
Spring uses Java proxies by default to wrap beans and implement annotated behavior. When doing calls within a service you bypass proxy and run method directly, so annotated behavior is not triggered.
Possible solutions:
Move all #Transactional code to separate service and always do calls to transactional methods from outside
Use AspectJ and weaving to trigger annotated behavior even within a service
Add #Transactional to methodOuter() and it works.

#Transactional method called from another method doesn't obtain a transaction

In Spring, a method that is annotated with #Transactional will obtain a new transaction if there isn't one already, but I noticed that a transactional method does not obtain any transaction if it is called from a non-transactional one. Here's the code.
#Component
public class FooDao {
private EntityManager entityManager;
#PersistenceContext
protected void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
}
#Transactional
public Object save(Object bean) {
return this.entityManager.merge(bean);
}
public Object saveWrap(Object bean) {
return save(bean);
}
}
#Component
public class FooService {
private FooDao fooDao;
public void save(Object bean) {
this.fooDao.saveWrap(bean); // doesn't work.
this.fooDao.save(bean); // works
}
}
saveWrap() is a regular method that calls save() which is transactional, but saveWrap() won't persist any changes.
I'm using Spring 3 and Hibernate 3. What am I doing wrong here? Thanks.
It is one of the limitations of Springs AOP. Because the dao bean is in fact a proxy when it is created by spring, it means that calling a method from within the same class will not call the advice (which is the transaction). The same goes for any other pointcut
Yes, this is expected behaviour. #Transactional tells spring to create a proxy around the object. The proxy intercepts calls to the object from other objects. The proxy does not intercept calls within the object.
If you want to make this work, add #Transactional on the method that is invoked from "outside".
This is a bit late I know, but would just like to add a way to overcome this limitation is that within the method obtain the spring bean from the application context and invoke the method. When the spring bean is obtained from the application context it will be the proxy bean not the original bean . Since the proxy bean is now invoking the method instead of the original bean the transaction advice will be implemented on it.
A possible workaround is to call the method like if it was invoked from "outside"
You can do it by getting the current proxy of the component and then call the method :
((MyService) AopContext.currentProxy()).innerMethod();
Source: https://www.programmersought.com/article/58773839126/

Categories

Resources