Strict alternative to Spring #Transactional annotation - java

I have a Spring bean with #Transactional method.
public class ABean{
#Transactional
public void method aMethod(){
//do some job with Hibernate.
}
}
But now I need to call this method from another method which should be called not in Spring context (in Quartz context actually):
public class ABean implements org.quartz.Job{
#Transactional
public void method aMethod(){
//do some job with Hibernate.
}
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("start...");
//#Transactional annotation is ignored here
//so I have 'Could not obtain transaction-synchronized Session
//for current thread' exception.
aMethod();
System.out.println("done");
}
}
As I understand annotation #Transactional just somehow wraps method with some another code. So how I must wrap aMethod() call to call it exactly like Spring calls?

You can use Transaction from Hibernate instead as you said this method will not run in Spring managed environment and use it pro grammatically.
Create Session object from Hibernate SessionFactory.
From Session you can get Transaction using session.beginTransaction().
Check out :
Hibernate Docs

Spring only adds its transactional logic when you call it from another bean. When calling a method from the same class, the annotation is ignored.
For you that means you should move aMethod() to another Bean and invoke it from your existing ABean.

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.
}
}

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/

Invoke proxy AOP by calling method within the bean

lets say the I have got a bean called with two methods 'foo' and 'goo'
and 'goo' is marked with AOP interception call.
is it possible to write any piece of code inside 'foo' in order to invoke 'goo' method not directly but through the proxy wrapper of the bean in order to activate the AOP part of it?
public Class Pojo{
public void foo(){
//what should I write here in order to activate 'goo' in transactional mode??
}
#Transactional
public void goo(){
}
}
Yes, but you need to access it through the spring proxy:
public Class Pojo{
#Autowired
private Pojo springProxy;
public void foo(){
springProxy.goo();
}
#Transactional
public void goo(){
}
}
I couldn't use the autowired option. Perhaps it is because I am using reflection to invoke goo() (and any other method as well) from foo().
So eventually what solves my problem was to add in foo() code that will lookup for the Pojo's proxy bean class. and invoke the mothd using Sun invokation on the proxy bean
this invoked the AOP call as well.
Couldn't find any better workaround.

Categories

Resources