I have a main DB handler method, which calls other methods, which are also working with BD things.
I put #Transactional annotation for the main method, because I want to roll back everything, if something goes wrong.
My question is: should I put this annotation also for the submethods, or it will know that the submethods were called from a method which is transactional.
For example, in the deleting method an exception occurs, how can I make sure that the writing part will be also rollbacked:
#Transactional
public void maintDbTings() {
writing();
deleting();
}
#Transactional //do I need this?
public void writing() {
//no exceptions
}
#Transactional //do I need this?
public void deleting() {
//exception occurs
}
Spring begins a transaction when it encounters a method annotated with #Transactional. The transaction’s scope
covers the execution of that method, the execution of any methods that method invokes, and
so on, until the method returns. Any managed resources that are covered by the configured
PlatformTransactionManager and that you use during the transaction scope participate in the
transaction. For example, if you use the org.springframework.jdbc.datasource.DataSourceTransactionManager, a Connection retrieved from the linked DataSource
participates in the transaction automatically.
The transaction terminates one of two ways: Either the method completes execution directly and the transaction manager commits the transaction, or the method throws an exception and the transaction manager rolls the transaction back.
I hope it is clear now.
In plain english, when you have this:
#Transactional
public void maintDbTings() {
writing();
}
#Transactional //do I need this?
public void writing() {
//no exceptions
}
And call mainDbTings, the #Transactional on the writing has no effect. Meaning that the transaction that was started for mainDbThings will still be present/open in writing. So in this case you can easily drop it.
On the other hand since writing is public someone might call it expecting it to be transactional, since it is a service class most probably. In this case making writing to be #Transactional is mandatory and you can't drop it.
So it's up your needs really.
You can use propagation properties like REQUIRED, REQUIRES_NEW, NESTED according to your requirement as described in the below link:
http://docs.spring.io/spring-framework/docs/4.2.x/spring-framework-reference/html/transaction.html
Related
It's not exactly as the title says, but close to. Consider these Spring beans:
#Bean
class BeanA {
#Transactional(propagation = Propagation.REQUIRED, rollbackFor = EvilException.class)
public void methodA() {
/* ... some actions */
if (condition) {
throw new EvilException();
}
}
}
#Bean
class BeanB {
#Autowired private BeanA beanA;
final int MAX_TRIES = 3;
#Transactional(propagation = Propagation.NESTED)
public void methodB() {
// prepare to call Bean A
try {
beanA.methodA();
/* maybe do some more things */
}
catch (EvilException e) {
/* recover from evil */
}
}
}
#Bean
class MainWorkerBean {
#Autowired private BeanB beanB;
#Autowired private OtherBean otherBean;
#Transactional(propagation = Propagation.REQUIRED)
public void doSomeWork() {
beanB.methodB();
otherBean.doSomeWork();
}
}
Important note: I'm using JDBC transaction manager that supports savepoints.
What I'm expecting this to do is, when EvilException is thrown, the transaction of the BeanA is rolled back, which with this setup happens to be the savepoint created by starting methodB. However, this appears to not be the case.
When going over with debugging tools, what I'm seeing is this:
When doSomeWork of MainWorkerBean starts, new transaction is created
When methodB starts, transaction manager properly initializes a savepoint and hands it to TransactionInterceptor
When methodA starts, transaction manager sees Propagation.REQUIRED again, and hands out a clean reference to the actual JDBC transaction again, that has no knowledge of the savepoint
This means that when exception is thrown, TransactionStatus::hasSavepoint return false, which leads to roll back of the whole global transaction, so recovery and further steps are as good as lost, but my actual code has no knowledge of the rollback (since I've written recovery for it).
For now, I can't consider changing BeanA's transaction to Propagation.NESTED. Admittedly, looks like it's going to allow me to have the more local rollback, but it's going to be too local, because as I understand it, Spring then will have two savepoints, and only roll back the BeanA savepoint, not BeanB one, as I'd like.
Is there anything else I'm missing, such as a configuration option, that would make internal transaction with Propagation.REQUIRED consider that it is running inside a savepoint, and roll back to savepoint, not the whole thing?
Right now we're using Spring 4.3.24, but I already crawled through their code and can't spot any relevant changes, so I don't think upgrading will help me.
As described in this bug ticket: https://github.com/spring-projects/spring-framework/issues/11234
For spring versions < 5.0, in the situation described, the global transaction is set to 'rollback-only'.
In this transaction I am processing several tasks. If an error should occur during a single task, I do not want the whole transaction to be rolled back, therefore I wrap the task processing in another transaction boundary with a propagation of PROPAGATION_NESTED.
The problem comes when, during task processing, calls are made to existing service methods defined with a transaction boundary of PROPAGATION_REQUIRED. Any runtime exceptions thrown from these methods cause the underlying connection to be marked as rollback-only, rather than respecting the current parent transaction nested propagation.
[...]
As of Spring Framework 5.0, nested transactions resolve their rollback-only status on a rollback to a savepoint, not applying it to the global transaction anymore.
On older versions, the recommended workaround is to switch globalRollbackOnParticipationFailure to false in such scenarios.
However, even for Spring5, I noticed when reproducing the problem, that the nested transaction may be rolled back, including all things done in the catch block of methodB(). So your recover code might not work inside methodB(), depending on what your recovery looks like. If methodA() was not transactional, that would not happen. Just something to watch out for.
Some more details to be found here: https://github.com/spring-projects/spring-framework/issues/8135
I'm using spring boot. I was new to spring and started a spring project. So I didn't know about pre defined repositories (JPA, CRUD) which can be easily implemented. In case, I wanted to save a bulk data, so I use for loop and save one by one, Its taking more time. So I tried to use #Async. But it doesn't also work, is my concept wrong?
#Async has two limitation
it must be applied to public methods only
self-invocation – calling the async method from within the same class won’t work
1) Controller
for(i=0;i < array.length();i++){
// Other codes
gaugeCategoryService.saveOrUpdate(getEditCategory);
}
2) Dao implementation
#Repository
public class GaugeCategoryDaoImpl implements GaugeCategoryDao {
// Other codings
#Async
#Override
public void saveOrUpdate(GaugeCategory GaugeCategory) {
sessionFactory.getCurrentSession().saveOrUpdate(GaugeCategory);
}
}
After removing #Async , it working normally. But with that annotation it doesn't work. Is there any alternative method for time consuming? Thanks in advance.
the #Async annotation creates a thread for every time you call that method. but you need to enable it in your class using this annotation #EnableAsync
You also need to configure the asyncExecutor Bean.
You can find more details here : https://spring.io/guides/gs/async-method/
In my opinion, there are several issues with your code:
You overwrite the saveOrUpdate() method without any need to do so. A simple call to "super()" should have been enough to make #Async work.
I guess that you somewhere (within your controller class?) declare a transactional context. That one usually applies to the current thread. By using #Async, you might leave this transaction context as (because of the async DAO execution), the main thread may already be finished when saveOrUpdate() is called. And even though I currently don't know it exactly, there is a good change that the declared transaction is only valid for the current thread.
One possble fix: create an additional component like AsyncGaugeCategoryService or so like this:
#Component
public class AsyncGaugeCategoryService {
private final GaugeCategoryDao gaugeCategoryDao;
#Autowired
public AsyncGaugeCategoryService(GaugeCategoryDao gaugeCategoryDao) {
this.gaugeCategoryDao = gaugeCategoryDao;
}
#Async
#Transactional
public void saveOrUpdate(GaugeCategory gaugeCategory) {
gaugeCategoryDao.saveOrUpdate(gaugeCategory);
}
}
Then inject the service instead of the DAO into your controller class. This way, you don't need to overwrite any methods, and you should have a valid transactional context within your async thread.
But be warned that your execution flow won't give you any hint if something goes wrong while storing into the database. You'll have to check the log files to detect any problems.
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.
In Spring how can we make sure that certain operations are always executed together. If any one of them fails, the entire transaction needs to be rolled back. I searched this a lot and found #Transactional(propagation = Propagation.REQUIRED) annotations and TransactionTemplate.execute() methods close to my problem. Kindly clarify and help.
Both #Transactional and TransactionTemplate ensure atomicity. #Transactional is for declarative transaction management, TransactionTemplate is for programmatic transaction management. You should choose one.
The idea of transaction propagation applies only to declarative transaction management and defines a transaction behaviour when it is executed in more than one method. Note that Propagation.REQUIRED is default for Transactional.propagation. It means Support a current transaction (that is if a transaction was already started in the calling method) or create a new one if none exists.
#Transactional(propagation = Propagation.REQUIRED
May solve your problem.
Suppose in your Impl there is a method Excecute.Inside Excecute method there are other M1(),M2(),M3(),M4(),M5() methods.
May be you trying to say if for M1(),M2().M3().M4() methods Db operation succedded and at last for M5() it throws some exception then M1() to M5() all db operation should be rollback
Execute(){
M1();
M2();
M3();
M4();
M5();
if(Any error in any methods transaction will be roll back).As single trasaction object is used for all methods i.e(M1 to M5) when #Transactional(propagation = Propagation.REQUIRED is used.
}
You can create a single method that delegates to the two database calls and annotate that with #Transactional, e.g.
#Transactional
public void atomicOperation(...) {
dbCall();
logicOperation();
}
If either one fails, e.g. an exception is thrown, the entire operation will rollback. The Spring Reference Documentation has dedicated a chapter for transactions, for example there is information about #Transactional settings and Transaction propagation.
I'm trying to use Container-Managed Transactions inside webservice, but transaction isn't created. I have something like that:
#WebService(...)
#Stateless
#TransactionManagment(TransactionManagmentType.CONTAINER)
public class ExampleService {
// EntityManager and other fields
#TransactionAttribure(TransactionAttributeType.REQUIRED)
public void test(String s){
// persist something with EntityManager
}
}
When I call this method, I get javax.persistence.TransactionRequiredException: The method public abstract void javax.persistence.EntityManager.persist(java.lang.Object) must be called in the context of a transaction.
What am I doing wrong?
Thanks!
From what I recall 'TransactionAttributeType.REQUIRED' means that method should be only called when transaction is already in progress for current thread (in other words 'called in context of transaction'). It's not clear who if anybody starts transaction in your case. If nobody then the exception you're getting makes perfect sense.
Now I'm not sure how or is it even currently possible to propagate transaction across Web services call. I don't think this is particularly good idea to do so even if possible.
Perhaps you what you need TransactionAttributeType.REQURES_NEW in your case so Container would start the transaction before passing control to your annotated method?