Spring @Transactional & Exception

Spring Transactional Annotation & Exception


What is Transaction?

The transaction is unit work which will success or fails as the unit. So suppose we are dealing with two accounts for transfer money from one to another.  Now if Money from one account is already debited but while it is credited to an another account there was Exception so the ideal scenario would be debited action should rollback. Otherwise, it would be an Inconsistent state.

So if we use Transaction in our business logic we can ensure, logic under the Transaction work as a unit it will be rollback if anything found wrong.

In one word.

Either Unit of work Succeeds completely or failure completely no intermediate state.


Now the question comes How we can handle Transaction.

There are two ways to Handle it

BMT: Bean managed Transaction.
CMT: Container Managed Transaction.



BMT: If we need a finer control over business logic or want to introduce savepoints this type of technique should be adopted where bean provider has a responsibility to start, commit, rollback the transaction.

CMT: If we want to delegate the responsibility to container we use it. Sometimes we called it Declarative Transaction.


We all know in Spring using @Transactional annotation we can adopt Declarative transaction technique.

In this article, I will not show how to configure it but tell about the precaution you want to take while using @Transactional annotation.

Study the pseudo code.
@Transactional
pbublic void transferMoney(Account to,Account from,int amount) throws Exception{
        debiFromAccount(to,amount)
        creditFromAccount(from,amount);
       
    }
   
    public debiFromAccount(Account to,int amount){
        //do staff and debited money from data base
    }
   
    public creditFromAccount(Account from,int amount)throws Exception{
        //do straff
        throw new EXception("Error during credit");
    }



Now do a dry run
If Account to initial balance is 1000
Account from is 500
Transfer amount : 100


After error occurred in creditFromAccount(Account from,int amount) what will be the output you expected?

To : 1000
From : 500

Afterall I use @Transactional so in case of Exception it should be rolled back automatically right.

But unfortunately it left a inconsistent state

Output is like

To : 900
From : 500


So obviously you will ask me Why, what is going on here?

Let me clear you, @Transactional only rollback transaction only for the unchecked exception but for checked Exception and it’s subclass it commits data. So although an Exception raised here but as it is a Checked Exception, Spring simply ignores it and commit the data to the database so the system goes inconsistent.

Spring documentation says that,



While the EJB default behavior is for the EJB container to automatically roll back the transaction on a system exception (usually a runtime exception), EJB CMT does not roll back the transaction automatically on an application exception (that is, a checked exception other than java.rmi.RemoteException). While the Spring default behavior for declarative transaction management follows EJB convention (roll back is automatic only on unchecked exceptions), it is often useful to customize this.

So pay attention to the last line “it is often useful to customize this”

So how can we customize it?

It is very simple just use following with @Transactional

@Transactional(rollbackFor = Exception.class)
So if you throw an Exception or Subclass of Exception always use the above with @Transactional annotation to tell Spring to rollback transaction in case for Checked Exception occur.

Spring Mock MVC and FileUpload Mocking

FileUpload Mocking using Spring Mock MVC
The aim of an Agile project is to deliver a minimum viable product to the client and based on client response we add the new functionality on that project.

Say, We have to implement a File Upload functionality, which uploads any types of files and extract metadata and content from file moreover it stores metadata into Solr/Elasticsearch for further intelligent searching.

According to agile mode, we first need to create a basic viable product.

So what is a minimum viable product in terms of  File upload functionality?

1.Create a UI which support File Upload
2. Implement a Middleware which extracts file content. (Not extract metadata)


So One UI developer starts developing the UI and You start developing the Middleware. But until UI and Middleware not finished we can’t do the Integration so should we wait for UI developer when he finishes then we start our Integration testing. No that is bad we waste our time for waiting for some other developer to finish his job.

Rather one thing we can do we Unit test our module by Mocking the UI as if files comes from UI.

In this Article, we will see How to Mock File Upload.

Here we use Spring Mock MVC for mocking MVC part
Use Junit and common File Upload.

Let create a pom.xml for resolving the dependencies

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.example</groupId>
 <artifactId>SpringWebExample</artifactId>
 <packaging>war</packaging>
 <version>0.0.1-SNAPSHOT</version>
 <name>SpringWebExample Maven Webapp</name>
 <url>http://maven.apache.org</url>
 <properties>
      <java-version>1.7</java-version>
      <org.springframework-version>4.3.0.RELEASE</org.springframework-version>
   </properties>
   <dependencies>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>${org.springframework-version}</version>
      </dependency>
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.0.1</version>
          <scope>provided</scope>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-test</artifactId>
          <version>${org.springframework-version}</version>
      </dependency>
      <dependency>      
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.2</version>
      </dependency>
      <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>jstl</artifactId>
          <version>1.2</version>
      </dependency>
       <dependency>
         <groupId>junit</groupId>
         <artifactId>junit</artifactId>
         <version>4.12</version>
         <scope>test</scope>
          </dependency>
   </dependencies>
   <build>
      <finalName>HelloWorld</finalName>
      <pluginManagement>
          <plugins>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-compiler-plugin</artifactId>
                  <version>2.3.2</version>
                  <configuration>
                      <source>${java-version}</source>
                      <target>${java-version}</target>
                  </configuration>
              </plugin>
              <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-war-plugin</artifactId>
                  <version>2.4</version>
                  <configuration>
                      <warSourceDirectory>src/main/webapp</warSourceDirectory>
                      <warName>SpringWebExample</warName>
                      <failOnMissingWebXml>false</failOnMissingWebXml>
                  </configuration>
              </plugin>
          </plugins>
      </pluginManagement>
   </build>
</project>




Here we use Spring-webmvc,Spring-test,Common-fileupload,junit jars.


Now Create Spring configuration class

package com.example.anotatedconfiguration;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.example")
public class SpringConfig extends WebMvcConfigurerAdapter{
   
   @Bean
   public ViewResolver viewResolver() {
      InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
      viewResolver.setViewClass(JstlView.class);
      viewResolver.setPrefix("/WEB-INF/pages/");
      viewResolver.setSuffix(".jsp");

      return viewResolver;
   }
   
   @Bean
    public CommonsMultipartResolver multipartResolver() {

       CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
       return commonsMultipartResolver;

    }

   @Override
   public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
      configurer.enable();
   }

}






Please note that here we register two Spring Beans

  1. ViewResolver
  2. Multipart Resolver


View Resolver helps to resolve the view.
Multipart Resolver helps to handle a Multipart request for File Upload.


In Next Step, we will create a Web configuration file which is use instead of web.xml because we follow annotation based spring MVC configuration.




package com.example.anotatedconfiguration;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;

import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;

public class WebServletConfiguration implements WebApplicationInitializer{

   public void onStartup(ServletContext ctx) throws ServletException {
      AnnotationConfigWebApplicationContext webCtx = new AnnotationConfigWebApplicationContext();
      webCtx.register(SpringConfig.class);
      webCtx.setServletContext(ctx);

      ServletRegistration.Dynamic servlet = ctx.addServlet("dispatcher", new DispatcherServlet(webCtx));

      servlet.setLoadOnStartup(1);
      servlet.addMapping("/");
     
   }

}




Next, we will create a FileUpload Controller class

package com.example.controller;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.multipart.MultipartFile;

import com.example.model.FileInfo;

@Controller
public class FileUploadController {
   
   @RequestMapping(path= "/fileupload",method=RequestMethod.POST)
   public void uploadFiles(@ModelAttribute("files")FileInfo fileInfo,HttpServletRequest request) throws IOException{
     
      fileInfo.setName(request.getParameter("name"));
      fileInfo.setUplodedBy(request.getParameter("uploadedBy"));
      System.out.println(fileInfo);
      MultipartFile[] files = fileInfo.getFiles();
      for(MultipartFile file : files)
      {
          byte[] bytes = file.getBytes();
           Path path = Paths.get("/var/tmp/"+ file.getOriginalFilename());
           Files.write(path, bytes);
           System.out.println(file.getOriginalFilename() +  " :: File uploaded successFully");
      }
     
     
   }

}



Here I map the uploadFiles method with a file upload URL and tell Spring that it support post request.
Next I Bound the Files into  FileInfo files attribute using @ModelAttribute("files")FileInfo fileInfo annotation.

This tells Spring to bound Model attribute files in Fileinfo Object. SO if any value is bound under files key(Model Object) which is MutipartFile here then it will bound with FileInfo files attribute.


Next, we  fetch some information from Request and then Iterate over the files attribute to retrieve MultipartFile(We can add multiple files also ) and store them into /var/temp location on a Linux box.

FileInfo Model Class

package com.example.model;

import java.util.Arrays;

import org.springframework.web.multipart.MultipartFile;

public class FileInfo {
   
   public MultipartFile[] files;
   public String name;
   public String uplodedBy;
   public MultipartFile[] getFiles() {
      return files;
   }
   public void setFiles(MultipartFile[] files) {
      this.files = files;
   }
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getUplodedBy() {
      return uplodedBy;
   }
   public void setUplodedBy(String uplodedBy) {
      this.uplodedBy = uplodedBy;
   }
   @Override
   public String toString() {
      return "FileInfo [files=" + Arrays.toString(files) + ", name=" + name
              + ", uplodedBy=" + uplodedBy + "]";
   }
   
   
   

}




Now , our setup is complete so we test the solution using Spring Mock so without Integration with UI we can verify our FileUploadController works successfully or not.

package com.example.fileupload.test;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import com.example.anotatedconfiguration.SpringConfig;

@WebAppConfiguration
@ContextConfiguration(classes = SpringConfig.class)
@RunWith(SpringJUnit4ClassRunner.class)
public class FileUploadControllerTest {
   
   @Autowired
    private WebApplicationContext webApplicationContext;
   
   @Test
   public void testFileUpload() throws Exception
   {
      MockMultipartFile mockMultipartFile =
                 new MockMultipartFile("files", "FileUploadTest.txt", "text/plain", "This is a Test".getBytes());
     
      MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
         mockMvc.perform(MockMvcRequestBuilders.fileUpload("/fileupload")
                 .file(mockMultipartFile)
                 .param("name", "FileUploadTest.txt")
                 .param("uploadedBy","Shamik Mitra"));
       
         //Assert.assertTrue(true);
             
             
   }

}


Follow this class very carefully

Here I use two top-level annotations

@WebAppConfiguration
@ContextConfiguration(classes = SpringConfig.class)

It tells it a Web application and looks SpringConfig class for Spring beans.

Next, I create  MockMultipart file where I pass  name of file, Content-type and file content in the Constructor
 MockMultipartFile mockMultipartFile = new MockMultipartFile("files", "FileUploadTest.txt", "text/plain", "This is a Test".getBytes());


Next line I create a MockMVC object which acts as a Mock Spring MVC then I create a request by passing file, and some parameters into Mock Request.

mockMvc.perform(MockMvcRequestBuilders.fileUpload("/fileupload")
                 .file(mockMultipartFile)
                 .param("name", "FileUploadTest.txt")
                 .param("uploadedBy","Shamik Mitra"));






If we run the Junit

We see that a file named “FileUploadTest”  will create in /var/tmp folder and in console it prints

INFO: Looking for @ControllerAdvice: org.springframework.web.context.support.GenericWebApplicationContext@49ba4a47: startup date [Thu Jan 26 11:09:47 IST 2017]; root of context hierarchy
Jan 26, 2017 11:09:48 AM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/fileupload],methods=[POST],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public void com.example.controller.FileUploadController.uploadFiles(com.example.model.FileInfo,javax.servlet.http.HttpServletRequest) throws java.io.IOException
Jan 26, 2017 11:09:48 AM org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping registerHandlerMethod
INFO: Mapped "{[/greet/{name}],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String com.example.controller.GreetController.greet(java.lang.String,org.springframework.ui.ModelMap)
Jan 26, 2017 11:09:48 AM org.springframework.web.servlet.handler.SimpleUrlHandlerMapping registerHandler
INFO: Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler]
Jan 26, 2017 11:09:48 AM org.springframework.mock.web.MockServletContext log
INFO: Initializing Spring FrameworkServlet ''
Jan 26, 2017 11:09:48 AM org.springframework.test.web.servlet.TestDispatcherServlet initServletBean
INFO: FrameworkServlet '': initialization started
Jan 26, 2017 11:09:48 AM org.springframework.test.web.servlet.TestDispatcherServlet initServletBean
INFO: FrameworkServlet '': initialization completed in 28 ms
FileInfo [files=[org.springframework.mock.web.MockMultipartFile@547e6f03], name=FileUploadTest.txt, uplodedBy=Shamik Mitra]
FileUploadTest.txt :: File uploaded successFully
Jan 26, 2017 11:09:48 AM org.springframework.web.context.support.GenericWebApplicationContext doClose
INFO: Closing org.springframework.web.context.support.GenericWebApplicationContext@49ba4a47: startup date [Thu Jan 26 11:09:47 IST 2017]; root of context hierarchy