Using matrix variables

In the previous section, you saw the URI template facility to bind variables in the URL request path. But there is one more way to bind variables in the request URL in a name-value pair style, referred to as matrix variables within Spring MVC. Look at the following URL:

http://localhost:8080/webstore/market/products/filter/price;low=500;high=1000

In this URL, the actual request path is just up to http://localhost:8080/webstore/market/products/filter/price, and after that we have something like low=500;high=1000. Here, low and high are just matrix variables. But what makes Matrix variables so special is the ability to assign multiple values for a single variable; that is, we can assign a list of values to a URI variable. Look at the following URL:

http://localhost:8080/webstore/market/products/filter/params;brands=Google,Dell;categories=Tablet,Laptop

In this URL, we have two variables, namely brand and category. Both have multiple values: brands (Google, Dell) and categories (Tablet, Laptop). How can we read these variables from the URL during request mapping? Here comes the special binding annotation @MatrixVariable (org.springframework.web.bind.annotation.MatrixVariable). One cool thing about the @MatrixVariable annotation is that it allows us to collect the matrix variables in the map of a collection (Map<String, List<String>>), which will be more helpful when we are dealing with complex web requests.

Time for action - showing products based on filters

Consider a situation where we want to filter the product list based on brands and categories. For example, you want to list all the products that fall under the category Laptop and Tablets and from the manufacturer Google and Dell. With the help of Matrix variables, we can form a URL something like the following to bind the brands and categories variables' values into the URL:

http://localhost:8080/webstore/market/products/filter/params;brands=Google,Dell;categories=Tablet,Laptop

Let's see how to map this URL to a handler method with the help of the @MatrixVariable annotation:

  1. Open the ProductRepository interface and add one more method declaration to getProductsByFilter:
          List<Product> getProductsByFilter(Map<String,List<String>>        filterParams); 
    
  2. Open the InMemoryProductRepository implementation class and add the following method implementation for getProductsByFilter:
          @Override 
          public List<Product> getProductsByFilter(Map<String, 
          List<String>> filterParams) { 
             String SQL = "SELECT * FROM PRODUCTS WHERE CATEGORY IN ( 
             :categories ) AND MANUFACTURER IN ( :brands)"; 
     
             return jdbcTemplate.query(SQL, filterParams, new       
             ProductMapper()); 
          } 
    
  3. Open the ProductService interface and add one more method declaration to getProductsByFilter:
          List<Product> getProductsByFilter(Map<String, List<String>>        filterParams); 
    
  4. Open the ProductServiceImpl service implementation class and add the following method implementation for getProductsByFilter:
        public List<Product> getProductsByFilter(Map<String, 
        List<String>> filterParams) { 
            return productRepository.getProductsByFilter(filterParams); 
        } 
    
  5. Open ProductController and add one more request mapping method as follows:
          @RequestMapping("/products/filter/{params}") 
          public String 
          getProductsByFilter(@MatrixVariable(pathVar="params") 
          Map<String,List<String>> filterParams, Model model) { 
             model.addAttribute("products", 
             productService.getProductsByFilter(filterParams)); 
             return "products"; 
          } 
    
  6. Open our web application context configuration file (WebApplicationContextConfig.java) and enable matrix variable support by overriding the configurePathMatch method as follows:
          @Override 
          public void configurePathMatch(PathMatchConfigurer 
          configurer) { 
             UrlPathHelper urlPathHelper = new UrlPathHelper(); 
             urlPathHelper.setRemoveSemicolonContent(false); 
     
             configurer.setUrlPathHelper(urlPathHelper); 
          } 
    
  7. Now run our application and enter the following URL: http://localhost:8080/webstore/market/products/filter/params;brands=Google,Dell;categories=Tablet,Laptop. You will see the products list as shown in the following screen:
    Time for action - showing products based on filters

    Using matrix variables to show the product list filtered by criteria

What just happened?

Our aim was to retrieve the matrix variable values from the URL and do something useful; in our case the URL we were trying to map is http://localhost:8080/webstore/market/products/filter/params;brands=Google,Dell;categories=Tablet,Laptop. Here we want to extract the matrix variables brands and categories. The brands and categories variables have values: (Google, Dell) and (Tablet, Laptop) respectively. In the previously specified URL, the request path is just up to http://localhost:8080/webstore/market/products/filter/params only. That's why in step 5 we annotated our getProductsByFilter request mapping method as follows:

@RequestMapping("/products/filter/{params}") 

But you may be wondering why we have a URI template (/{params}) in the @RequestMapping annotation as a mapping to a path variable. This is because, if our request URL contains a matrix variable, then we have to form the @RequestMapping annotation with a URI template to identify the matrix variable segments. That's why we defined params as a URI template in the request mapping (@RequestMapping("/products/filter/{params}")) annotation.

Tip

A URL can have multiple matrix variables, and each matrix variable must be separated with a ";" (semicolon). To assign multiple values to a single variable, each value must be "," (comma) separated or we can repeat the variable name. See the following URL, which is a variable repeated version of the same URL that we used in our example: http://localhost:8080/webstore/market/products/filter/params;brands=Google;brands=Dell;categories=Tablet;categories=Laptop

Note that we repeated the variable brands and categories twice in the URL.

Okay, we mapped the web request to the getProductsByFilter method, but how do we retrieve the value from the matrix variables? The answer is the @MatrixVariable annotation.

@MatrixVariable is very similar to the @PathVariable annotation; if you look at the getProductsByFilter method signature in step 5, we annotated the method's parameter filterParams with the @MatrixVariable annotation as follows:

public String getProductsByFilter(@MatrixVariable(pathVar="params") Map<String,List<String>> filterParams, Model model) 

So Spring MVC will read all the matrix variables found in the URL after the {params} URI template and put them into the method parameter filterParams map. The filterParams map will have each matrix variable name as the key and the corresponding list will contain multiple values assigned for the matrix variable. The pathVar attribute from @MatrixVariable is used to identify the matrix variable segment in the URL; that's why it has the value params, which is nothing but the URI template value we used in our request mapping URL.

A URL can have multiple matrix variable segments. See the following URL:

http://localhost:8080/webstore/market/products/filter/params;brands=Google,Dell;categories=Tablet,Laptop/specification;dimention=10,20,15;color=red,green,blue

It contains two matrix variable segments each identified by the prefix params and specification respectively. So, in order to capture each matrix variable segment into maps, we have to form the controller method signature as follows:

@RequestMapping("/products/filter/{params}/{specification}") 
public String filter(@MatrixVariable(pathVar="params") Map<String,List<String>> criteriaFilter, @MatrixVariable(pathVar=" specification") Map<String,List<String>> specFilter, Model model) 

Okay, we got the value of the matrix variables' values into the method parameter filterParams, but what we have done with that filterParams map? We simply passed it as parameters to the service method to retrieve the products based on the criteria:

productService.getProductsByFilter(filterParams) 

Again, the service passes that map to the repository to get the list of products based on the criteria. Once we get the list, as usual, we simply add that list to the Model, and return the same logical View name that was used to list the products.

To enable the use of matrix variables in Spring MVC, we must set the RemoveSemicolonContent property of UrlPathHelper to false; we did that in step 6. Finally, we are able to see products based on the specified criteria in step 7 on our product listing page.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.122.46