05 September 2016

Using @Transactional to trigger rollback on runtime exceptions

In the last post, I've discussed some thoughts about trying to understand spring transactions.

Although not complete (and it appears it has issues as well). but today's topic is so compact and clear.

Spring follow's EJB transaction on that, it rollback on RuntimeException.

Suppose we have the following method:

@Transactionl
public void myServiceMethod(){
    updateDatabase();
    callSomeQueueOrWebService();
}

so, if the the second method call fails with RuntimeException, then the whole transaction will rollback causing the first method to rolled back.

But if you don't annotate the method with @Transactional, then both method calls (updateDatabase() and callSomeQueueOrWebService() ) will run independently and one might success however the other case fail.

Code:

Service Layer:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
private String fName = "Mohammad", lName = "Hewedy";

 @Transactional
 public void updateDbAndCallWSTrans() {
  userRepo.save(User.of(null, fName, lName));
  callWsThatThrowsException();
 }

 public void updateDbAndCallWSNoTrans() {
  userRepo.save(User.of(null, fName, lName));
  callWsThatThrowsException();
 }

 public Stream<User> getDbUpdate() {
  return userRepo.findByFNameAndLName(fName, lName);
 }

 private void callWsThatThrowsException() {
  // automatic rollback for runtime exceptions only!
  throw new RuntimeException("EXCEPTION from WS");
 }


The controller layer:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
 
 @RequestMapping("/tx/user")
 public ResponseEntity<?> getDbUpdate() {
  return ResponseEntity.ok(userService.getDbUpdate().toArray(User[]::new));
 }

 @RequestMapping("/tx/add-w")
 public ResponseEntity<?> updateDbAndCallWSTrans() {
  userService.updateDbAndCallWSTrans();
  return ResponseEntity.ok().build();
 }

 @RequestMapping("/tx/add-wo")
 public ResponseEntity<?> updateDbAndCallWSNoTrans() {
  userService.updateDbAndCallWSNoTrans();
  return ResponseEntity.ok().build();
 }


Note, the call to /tx/add-w will not insert any data into the database, and calls to /tx/user will return empty.

However, the call to /tx/add-wo, will insert the user object into the database, and call to /tx/user will return the inserted users object.

No comments: