Spring Transactional does not roll back - java

I have a question about xml-free configuration of Spring. Unfortunately it does not roll back my DB changes even if I mark a corresponding method with #Transactional annotation.
First of all I have a controller, which calls a class marked with #Transactional.
#RequestMapping(value="/Device", method=RequestMethod.POST, produces={"application/json"})
#ResponseStatus(HttpStatus.CREATED)
public #ResponseBody List<Device> addDevice(
#RequestBody Device deviceToAdd) {
List<Device> devices= deviceManager.addDevice(deviceToAdd);
return devices;
}
All controllers have an ExceptionResolver that catches DuplicateKeyException:
#ExceptionHandler(DuplicateKeyException.class)
public ResponseEntity<Result> handleBindException(DuplicateKeyException e) {
log.error(e.getMessage(), e);
Result result = new Result("this object already exists");
return new Response(result);
}
Here is my main transactional class which calls two DAO classes. The second one causes DuplicateKeyException, but the results of the first one are not deleted from DB.
#Transactional(propagation = Propagation.REQUIRED, rollbackFor=Exception.class)
public class DeviceManager {
#Autowired
DaoClass1 daoClass1;
#Autowired
DaoClass2 daoClass2;
public List<Device> addDevice(Device device){
Pocket pocket = daoClass1.addPocket(new Pocket());<--is not rolled back after Exception
device.setPocket(pocket);
List<Devices> addedDevices = daoClass2.addDevice(device); <--- causes exception
return devices;
}
I tried also to use #Transactional annotation both for the classes daoClass1 and daoClass2, but it did not change anything.
What could I have done wrong?

Related

Transactional not rolling back JPA save in spring boot

I am working on a spring boot app where I am using transactional and its not rolling back its changes when I throw a exception:
My method:
private BtoBWalletTransactionResponseModel doWalletOperation(BtoBWalletTransactionTypes transactionType, BtoBWalletTransactionRequestModel transactionRequest) {
// DB Operation
BtoBWalletTransaction savedTransaction = commonTransactionalService.finishWalletTransaction(userWallet, btoBWalletTransaction);
log.info("wallet {} txn of amount {} for user {}",transactionType.name(),txnAmount,userId);
// throwing a exception to rollback
throw new RuntimeException("Time to Rollback");
} catch(Exception e){
log.error(e.getMessage());
log.error("error while doing wallet operations for user {}",userId);
throw new WalletException(e.getMessage());
}
}
My common TransactionalService Interface:
public interface CommonTransactionalService {
BtoBWalletTransaction finishWalletTransaction(BtoBUserWallet userWallet,BtoBWalletTransaction btoBWalletTransaction);
}
My Interface Impl:
import javax.transaction.Transactional;
#Service
public class CommonTransactionalServiceImpl implements CommonTransactionalService {
#Autowired
private BtoBWalletTransactionRepo btoBWalletTransactionRepo;
#Transactional
#Override
public BtoBWalletTransaction finishWalletTransaction(BtoBUserWallet userWallet, BtoBWalletTransaction walletTransaction) {
BtoBWalletTransaction savedTransaction = btoBWalletTransactionRepo.save(walletTransaction);
btoBUserWalletRepo.save(userWallet);
return savedTransaction;
}
}
Now even when I am sending a RuntimeException the DB record is not getting rolled back.
Can someone help? stuck since hours here.
Transactional is scoped, if you anotate a method (or class) as #Transactional all the methods this class calls wil also be transactional. and if within this transaction an exception occurs things wil be rolled back.
If however like in you example a non-transactional method calls a transactional one and after that call throws an exception the previous transactioned function wil not be rolled back as it's outside of the transactions scope.

Arango spring data rollback

I am doing a work on arango db. Dose arangodb-spring-boot-starter has the transition and rollback support
I have tried #Transition annotation in the custom repo layer. added a error by custom error, the service has a functionality to create multiple document. I was expecting the rollback which is not happened.
This is the arango repository code.
public interface RelationRepository extends ArangoRepository<Relation, String> {
#Transactional
#Query("insert { _from: #from, _to: #to } into #collection return NEW")
Set<Relation> createEdge(#Param("from") String from,#Param("to") String to;}
This is the code snippet for the service
#Service
public class RelationService {
#Autowired
private RelationRepository relationRepository;
private final Logger log = LoggerFactory.getLogger(RelationService.class);
#Transactional(rollbackFor = SQLException.class)
public HashMap<String,String> demoRelation() {
relationRepository.createEdge("vertex1/121286","vertex2/167744","relation",
Instant.now().toEpochMilli(),Long.MAX_VALUE);
if(true)
throw new SQLException("custom exception to check rollback");
return null;
}
}
I was expecting the rollback, instead it is creating records

How to rollback transaction on calling #Transactional and non-Transactional method in same and different Service?

I am using spring data rest and Spring JPA. I am having one method which update one database table.
#Autowired InvoiceClient;
#Override
#Transactional
public String doBilling(String x){
//get date from TableOne
Bill bill = billsRepository.getBill(x);
if(bill.isPaid()){
generateInvoice();
}
bill.setPaymentDate(new Date());
return "SUCCESS";
}
generateInvoice is non Transactional method which calls #Transactional method from other service.
public void generateInvoice(){
invoiceClient.generateInvoice();//this is #Transactional, make changes in TableTwo
}
In case of any exception in generateInvoice method whole transaction is rolled back.
Now I want to add one more method which will have list of bill numbers. I call doBilling method in loop to do billing for all the bills.
#Override
#Transactional(readOnly = false, rollbackFor = {Throwable.class}, propagation = Propagation.REQUIRED)
public String doBillingForAll(List<String> tx){
for(String x: tx){
doBilling(x);
}
}
But now in case of any exceptions in doBilling method, all the setPayment methods are getting rolled back but generateInvoice is persisted.
I want to rollback generateInvoice also. How can I do it?
You don't need to define a rollbackFor = {Throwable.class}.
By default all RuntimeException do a rollback when using #Transactional.
It can be that because you are using and intermediate non #Transactional annotated method, the main Transaction is suspended and a nested one is created.
Try to put #Transactional in your public void generateInvoice() then Propagation.REQUIRED should be applied with rollback of your invoices

#Transaction annotated method does not save data into a database

In my unit tests I want to persist some entities and test their retrieval from the database. They were not being saved and I figured out that when the test method was also annotated with #Transaction, anything that happened inside it did not get persisted, even though the method finished without an error.
I had previously encountered a LazyInitializationException when messing with a many-to-many lazy-loaded association and annotating the method with #Transaction seemed to fix the issue, that's why I have been using it.
What could be the cause why the entities don't get saved? There is no reason for the transaction to be rolled back, since it does not fail.
Code of related classes:
#Test
#Transactional
public void plainPersistence() throws NullParameterException {
User user = userHelper.createUser("User1", "password", null, null);
Assert.assertNotNull(userDAO.findByUsername("User1"));
}
userHelper:
#Service
public class UserHelper {
#Autowired
private UserDAO userDAO;
public User createUser(...) throws NullParameterException {
User newUser = new User(username, ...);
userDAO.save(newUser);
return newUser;
}
UserDAO's save() method subsequently calls save() on UserRepository:
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
public User findByUsername(String username);
}
Since you're likely using Spring test, you should note that they are configured so that the default behaviour is to rollback the changes. To change this you should annotate your test classes with, if you are using Spring < 4.2
#TransactionConfiguration(defaultRollback = false)
otherwise, annotate the class with #Rollback(value = false)

Java Spring #Transactional method not rolling back as expected

Below is a quick outline of what I'm trying to do. I want to push a record to two different tables in the database from one method call. If anything fails, I want everything to roll back. So if insertIntoB fails, I want anything that would be committed in insertIntoA to be rolled back.
public class Service {
MyDAO dao;
public void insertRecords(List<Record> records){
for (Record record : records){
insertIntoAAndB(record);
}
}
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
Where MyDAO dao is an interface that is mapped to the database using mybatis and is set using Spring injections.
Right now if insertIntoB fails, everything from insertIntoA still gets pushed to the database. How can I correct this behavior?
EDIT:
I modified the class to give a more accurate description of what I'm trying to achieve. If I run insertIntoAAndB directly, the roll back works if there are any issues, but if I call insertIntoAAndB from insertRecords, the roll back doesn't work if any issues arise.
I found the solution!
Apparently Spring can't intercept internal method calls to transactional methods. So I took out the method calling the transactional method, and put it into a separate class, and the rollback works just fine. Below is a rough example of the fix.
public class Foo {
public void insertRecords(List<Record> records){
Service myService = new Service();
for (Record record : records){
myService.insertIntoAAndB(record);
}
}
}
public class Service {
MyDAO dao;
#Transactional (rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
public void insertIntoAAndB(Record record){
insertIntoA(record);
insertIntoB(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoA(Record record){
dao.insertIntoA(record);
}
#Transactional(propagation = Propagation.REQUIRED)
public void insertIntoB(Record record){
dao.insertIntoB(record);
}
public void setMyDAO(final MyDAO dao) {
this.dao = dao;
}
}
I think the behavior you encounter is dependent on what ORM / persistence provider and database you're using. I tested your case using hibernate & mysql and all my transactions rolled back alright.
If you do use hibernate enable SQL and transaction logging to see what it's doing:
log4j.logger.org.hibernate.SQL=DEBUG
log4j.logger.org.hibernate.transaction=DEBUG
// for hibernate 4.2.2
// log4j.logger.org.hibernate.engine.transaction=DEBUG
If you're on plain jdbc (using spring JdbcTemplate), you can also debug SQL & transaction on Spring level
log4j.logger.org.springframework.jdbc.core=DEBUG
log4j.logger.org.springframework.transaction=DEBUG
Double check your autocommit settings and database specific peciular (eg: most DDL will be comitted right away, you won't be able to roll it back although spring/hibernate did so)
Just because jdk parses aop annotation not only with the method, also parse annotation with the target class.
For example, you have method A with #transactional, and method B which calls method A but without #transactional, When you invoke the method B with reflection, Spring AOP will check the B method with the target class has any annotations.
So if your calling method in this class is not with the #transactional, it will not parse any other method in this method.
At last, show you the source code:
org.springframework.aop.framework.jdkDynamicAopProxy.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
......
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
// Check whether we have any advice. If we don't, we can fallback on direct
// reflective invocation of the target, and avoid creating a MethodInvocation.
if (chain.isEmpty()) {
// We can skip creating a MethodInvocation: just invoke the target directly
// Note that the final invoker must be an InvokerInterceptor so we know it does
// nothing but a reflective operation on the target, and no hot swapping orfancy proxying.
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
}
else {
// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
}
}

Categories