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.
Related
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.
We are using Spring and Hibernate in our project. We are using Spring transactional annotations and we understand what it does, but we still have some doubts regarding rollbacks.
Let's say we have the following code.
#Transactional
#Service
public class PersonServiceImpl implements PersonService {
#Autowired
private PersonDao personDao;
#Autowired
private AnotherService anotherService;
#Override
public void doSomething() throws Exception {
Person person = new Person();
person.setName("John");
personDao.insert(person);
person.setName("Megan");
//What happens if the following call fails and throws and exception.
personDao.update(person);
// What happens if this other service's call fails?
anotherService.doSomethingElse();
}
}
If either the update or the other service's call fails, what would happen to the insert? Would there be a rollback and the insert would never be executed? Or would the insert still persist in the DB? Do I need to declare the following command in each method for the rollback to be executed.
#Transactional(rollbackFor= Exception.class)
If that is the case, what happens if my service call throws the exception? Would it still works? Thanks for the help. I would really liked to understand the way rollbacks are executed inside a service call.
By default if the exception is checked (extends Exception), the rollback won't happen - the transaction will be committed. Otherwise (if defaults were changed or we're talking about unchecked exceptions), the transaction is rolled back and INSERT/UPDATE statements don't persist anything. BTW, this is described in the JavaDocs to the rollbackFor attribute ;)
If the exception is eventually thrown from your Transactional method it doesn't matter where that exception originated.
If you're not sure about the behaviour, you can always debug Spring's HibernateTransactionManager and its superclasses.
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.
}
}
In Spring TransactionSynchronization interface it has methods (in order of execution):
- beforeCommit
- beforeCompletion
- afterCommit: Can perform further operations right after the main transaction has successfully committed.
- afterCompletion
Why Spring doesn't have rollback methods, such as beforeRollback or afterRollback but it has for commit only (beforeCommit and afterCommit)? Will this is necessary? Can anyone give me some advices or explains about this?
If I want to continue further operations that are supposed to follow on a successful rollback of the main transaction, like notification messages or emails what should I do in this case?
#Override
public void afterCompletion(int status) {
if (status == TransactionSynchronization.STATUS_ROLLED_BACK) {
logger.trace("Rolled back...");
}
}
Can I ask why you are using this interface?
This is used for callbacks by the PlatformTransactionManager which is in charge of managing the transaction lifecycle.
The "normal" use of Spring Transactions is to utilise #Transactional annotations, aop declarations in xml or TransactionTemplate / PlatformTransactionManager programatically to set the transaction scope, behaviour and visibility, as described in the docs - here.
To manage a transaction behaviour for rollbacks etc you just tell spring what to do in the event of an Exception
public class Foo {
#Autowired public Service someService;
#Transactional(propagation = Propagation.REQUIRES_NEW,
noRollbackFor = {IOException.class})
public boolean bar(SomeObject someObject) throws IOException {
someService.doComplicatedThing(someObject.getValue());
}
}
This tells the PlatformTransactionManager to start a new TX when hitting the foo() method, commit on successful return or an IOException, and rollback if there is some other type of exception. This is the genius of it - you dont need to worry about littering your code with getTransaction().isActive() and complicated checks for managing the isolation.
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