The transactional roll back is not working when the exception is caught on the catch block, and another method is called for throw the exception. The pseudo code for the above is:
#Transactional(rollBackFor = Exception.class)
public void method1() {
// Calling another method
method2();
}
private void method2() {
try {
dbOperation1();
} catch (Exception e) {
handleFault()
}
}
handleFault() {
// Calling another method and throwing an exception
throwException()
}
throwException() {
//....
throw new Exception();
}
Related
I've found that there is a difference between being explicit and not having the same exception thrown.
1.
public class FooService {
#Transactional
public void foo() {
try {
fooRepository.save(foo); // when DataIntegrityViolationException!!
} catch(DataIntegrityViolationException e) {
log.error("foo error");
}
}
}
public class Caller {
public void caller() {
try {
fooService.foo();
} catch(Exception e) {
log.error("Caller error"); // happen
}
}
}
In the case of example 1, DataIntegrityViolationException occurs because it violates the constraint when saving. In the log, a caller error is output and Unexpect~Exception is caught.
On the other hand, if an explicit DataIntegrityViolationException is thrown, no caller error is stamped.
2.
public class FooService {
#Transactional
public void foo() {
try {
fooRepository.save(foo); // when success
if (true) {
throw new DataIntegrityViolationException("ex");
}
} catch(DataIntegrityViolationException e) {
log.error("foo error");
}
}
}
public class Caller {
public void caller() {
try {
fooService.foo();
} catch(Exception e) {
log.error("Caller error"); // not logging
}
}
}
There is no difference when explicitly calling an exception in the Spring documentation. I wonder why this happened.
I'm working on a microservice app, in service layout I want to invoke with CompletableFuture.runAsync(). The problem is when I want to throw exception, I have my own Handler Exception, but I can't capture error when it is produced in my catch block inside CompletedFuture shown below:
Controller:
#PostMapping(path="/offers/offer")
public CompletableFuture<Oferta> infoPropiedad(#Valid #RequestBody OfertaRequest inDTO) throws
WebServiceBadResponseException, SOAPException, IOException, InterruptedException, ExecutionException {
System.out.println("THREAD: "+Thread.currentThread().getName());
CompletableFuture<Oferta> outTO = new CompletableFuture<Oferta>();
return CompletableFuture.supplyAsync(()->{
try {
return ofertasService.ofertasService(inDTO);
} catch (Exception e) {
System.out.println("Error inesperado en la capa del controlador");
}
return null;
});
}
Service:
CompletableFuture<OfertaCrm> completableFutureCRM =
CompletableFuture.supplyAsync(()-> {
try {
return clientOferta.llamadaWebServiceOfertas(inDTOCrm);
} catch (Exception e1) {
//throw Exception and capture it with my handler class
}
});
ClientWs:
public OfertaCrm llamadaWebServiceOfertas(OfertaRequestCRM inDtoCrm)
throws SOAPException, IOException {
CompletableFuture<OfertaCrm> completableFuture = new CompletableFuture<OfertaCrm>();
logger.info("Iniciamos la llamada al WS");
//Error produces here and I want to controle it and capture with my handler class
Error handler:
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler({
WebServiceBadResponseException.class,
SOAPException.class,
IOException.class
})
#ResponseBody
public ErrorMessage internalError(Exception exception) {
return new ErrorMessage(exception,exception.getMessage());
}
I could not be applying the correct form. Any idea how to throw the exception inside the supplyAsync block?
CompletableFuture will wrap the exception thrown within the execution inside a CompletionException. You can handle it by intercepting the root cause exception directly. Below is a simplified example.
Controller:
#RestController
public class SimpleController {
#Autowired
SimpleService simpleService;
#GetMapping("/testing")
public CompletableFuture<Integer> testing(){
return simpleService.doStuff();
}
}
Service:
#Service
public class SimpleService {
public CompletableFuture<Integer> doStuff(){
// 1 / 0 will throw ArithmeticException
return CompletableFuture.supplyAsync(() -> 1 / 0);
}
}
Controller Advice:
#RestControllerAdvice
public class SimpleControllerAdvice {
#ExceptionHandler(ArithmeticException.class)
public String handleCompletionException(ArithmeticException ex){
return "hello world";
}
}
GET /testing
hello world
This is the class to save:
#Service
public class DataService {
#Transactional(readOnly = true)
public String fetchData() { //no exception signature
try {
//some operations
checkData();
}
catch(Exception e) {
throw new CanerRuntimeException("an error occurred in fetchdata: " + e.getMessage(), e);//it cant come here with exception from child
}
}
private void checkData() throws SystemException { //intellj made me put that exception
try {
//some operations
if (!isCanerNotMade) {
String errorMessage = "It is not caner made by";
throw new CanerBusinessException(errorMessage);
}
}
} catch(CanerBusinessException e) {
logger.error("CheckForFksLimitations CanerBusinessExceptionerror {}", e.getMessage());
throw e;
}
} catch(Exception e) {
logger.error("CheckForFksLimitations Exception error {}", e.getMessage());
throw e;
} finally {
if (fksLog != null) {
saveLog(fksLog);
}
logger.info("CheckForFksLimitations ended for identityNumber: {}", identityNumber);//3
}
}
#Transactional
private void saveLog(FksLog fksLog) {
try {
logger.info("CheckForFksLimitations saving fksControlLog: {}", mobilityUtil.getObjectAsJson(fksControlLog));//1
FksControlLog savedfksControlLog = fksControlLogRepository.saveAndFlush(fksControlLog);
logger.info("CheckForFksLimitations saved fksControlLog: {}", mobilityUtil.getObjectAsJson(savedfksControlLog));//2
} catch(CanerBusinessException e) {
logger.info("CheckForFksLimitations error: {}", e.getMessage(), e);
}
}
and that exceptions are:
public class CanerBusinessException extends RuntimeException {}
public class CanerRuntimeException extends RuntimeException {}
I send data for both cases. One for not to throw exception and it can save without any rollback. I made saveAndFlush because it cant save inside a readonly=False parent method. That is how it can save as child.
But when i send the case to throw exception, it throws exception. It goes to finally block then save method. But after that, it rolls back
I see those logs:
CheckForFksLimitations saving fksControlLog: {"id":null,
CheckForFksLimitations saved fksControlLog: {"id":91,
CheckForFksLimitations ended for identityNumber: ARJUNA016129: Could not end XA resource com.ibm.db2.jcc.t4.a4#2a5410b8 com.ibm.db2.jcc.am.XaException: [jcc][t4][10401][12066][4.24.92] Xa exception: XA_RBROLLBACK ERRORCODE=-4228, SQLSTATE=null
It is oracle db.
I did not put any rollback class for exception. It is because of this?
I also put exception to parent signatures but did not work. This service called by a controller.
In Spring batch Writer I'm updating the db row status from 0 to 1. If any exception occurs update to 2.
However due to #transaction rollback I'm unable to update the status to 2.
(I'm throwing exception to trigger the rollback)
#Override
#Transactional
public void write(List<? extends TestEntity> enityList) throws Exception {
for(TestEntity testEntity : enityList) {
try {
testEntity.setStatus(2);
testRepository.save(testEntity);
testRepository.flush();
testMethod(testEntity); (which throws exception)
}catch (Exception exception) {
testEntity.setStatus(2);
testRepository.save(testEntity);
}
}
}
#Transactional
public void testMethod(TestEntity testEntity) throws Exception {
try{
//Some service call
//...
} catch(Exception e) {
log.error("error", e);
throw new Exception("exp");
}
}
Methods that have the #Transactional will rollback the transaction when they throw an exception. So if an exception is an expected and okay-ish flow of your code, you shouldn't throw an exception and return some kind of result object or status code instead.
#Transactional
public void testMethodThatIsAllowedToFail(TestEntity testEntity) {
try{
//Some service call
} catch(Exception e) {
return Status.FAILURE; // enum you have to create
}
return Status.SUCCESS;
}
// spring batch writer
public void write(List<? extends TestEntity> enityList) throws Exception {
[...]
Status result = testMethod(testEntity); (which throws exception);
if (result != Status.SUCCESS) {
// do something with it
}
[...]
}
you could also play around with #Transactional(propagation = Propagation.REQUIRES_NEW) but you would have to think hard whether having an extra transaction is desireable.
// i am trying to execute test cases by grouping them using testng
package com.xyz.ngp.selenium;
import org.testng.annotations.*;
import javax.swing.*;
import com.thoughtworks.selenium.SeleneseTestNgHelper;
public class Grouping extends SeleneseTestNgHelper {
#BeforeGroups (groups = {"smoke"})
public void oneTimeSetUp() {
try {
String st="we are in BeforeGroups";
JOptionPane.showMessageDialog(null,st);
// scroll down to the bottom to see justprintsomething.
justprintsomething();
} catch (Exception e) {
e.printStackTrace();
}
}
#Test(groups = {"smoke"})
public void test1() throws Exception {
String st="you wanted to execute smoke group";
JOptionPane.showMessageDialog(null,st);
}
#Test(groups = {"functional"})
public void test2() throws Exception {
String st="you wanted to execute : either (functional) or this test: (test2)";
JOptionPane.showMessageDialog(null,st);
}
#Test(groups = {"test3"})
public void test3() throws Exception {
String st="you wanted to execute : this test: (test3)";
JOptionPane.showMessageDialog(null,st);
}
//#BeforeMethod (groups = "smoke") //do i need this beforegroups here?
public void justprintsomething() throws Exception {
try {
// it gets printed
String st="inside justprintsomething going to selenium.open";
JOptionPane.showMessageDialog(null,st);
// if i comment out the below line code works fine
selenium.open("http://www.google.com/");
} catch (Exception e) {
e.printStackTrace();
}
}
}
// i am getting null pointer exception error just before selenium.open.
You never initialized the selenium object.