Let's create a service object that will perform the simple business operation of updating stock. Our aim is dead simple: whenever we enter the URL
http://localhost:8080/webstore/update/stock/, our web store should go through the inventory of products and add 1,000 units to the existing stock if the number in stock is less than 500:
Open the ProductRepository interface from the
1.
com.packt.webstore.domain.repository package in the src/main/java source folder and add one more method declaration in it as follows:
void updateStock(String productId, long noOfUnits);
Open the InMemoryProductRepository implementation class and add an
2. implementation for the previous declared method as follows:
@Override
public void updateStock(String productId, long noOfUnits) {
String SQL = "UPDATE PRODUCTS SET UNITS_IN_STOCK =
:unitsInStock WHERE ID = :id";
Map<String, Object> params = new HashMap<>();
params.put("unitsInStock", noOfUnits);
params.put("id", productId);
jdbcTemplate.update(SQL, params);
}
Create an interface called ProductService under the
3.
com.packt.webstore.service package in the src/main/java source folder. And add a method declaration in it as follows:
void updateAllStock();
Create a class called ProductServiceImpl under the
4. com.packt.webstore.service.impl package in the src/main/java source folder. And add the following code to it:
Spring MVC Architecture – Architecting Your Web Store
[ 72 ]
package com.packt.webstore.service.impl;
import java.util.List;
import org.springframework.beans.factory.annotation
.Autowired;
import org.springframework.stereotype.Service;
import com.packt.webstore.domain.Product;
import com.packt.webstore.domain.repository
.ProductRepository;
import com.packt.webstore.service.ProductService;
@Service
public class ProductServiceImpl implements ProductService{
@Autowired
private ProductRepository productRepository;
@Override
public void updateAllStock() {
List<Product> allProducts =
productRepository.getAllProducts();
for(Product product : allProducts) {
if(product.getUnitsInStock()<500)
productRepository.updateStock
(product.getProductId(),
product.getUnitsInStock()+1000);
}
}
}
Open ProductController from the com.packt.webstore.controller
5. package in the src/main/java source folder and add a private reference to ProductService with the @Autowired annotation as follows:
@Autowired
private ProductService productService;
Now add one more method definition as follows in ProductController: 6.
@RequestMapping("/update/stock")
public String updateStock(Model model) {
productService.updateAllStock();
return "redirect:/products";
}
Spring MVC Architecture – Architecting Your Web Store
Run your application and enter the URL
7. http://localhost:8080/webstore/products. You will be able to see a web page showing all the products. Notice the available units in stock for iPhone 6s will now show as Available 450 units in stock. All other products will show 1,000 as Available 450 units in stock.
Now enter the URL http://localhost:8080/webstore/update/stock, you
8. will be able to see the same web page showing all the products. But this time, you can see that the available units in stock for iPhone 6s have been updated, and will show as Available 1450 units in stock.
Products page showing the product after stock has been updated via a service call
What just happened?
Okay, before going through the steps I just want to remind you of two facts regarding repository objects—that all the data access (CRUD) operations in a domain object should be carried out through repository objects only. Fact number two is that service objects rely on repository objects to carry out all data access related operations. That's why before creating the actual service interface/implementation, we created a repository
interface/implementation method (updateStock) in steps 1 and 2.
Spring MVC Architecture – Architecting Your Web Store
[ 74 ]
The updateStock method from the InMemoryProductRepository class just updates a single product domain object's unitsInStock property for the given product. We need this method when we write logic for our service object method (updateAllStock) in the OrderServiceImpl class.
Now we come to steps 3 and 4 where we created the actual service definition and
implementation. In step 3, we created an interface called ProductService to define all the expected responsibilities of an order service. As of now, we defined only one responsibility within that interface, which updates all the stock via the updateAllStock method. In step
4, we implemented the updateAllStock method within the OrderServiceImpl class, where we retrieved all the products and went through them one by one in a for loop to check whether the unitsInStock is less than 500. If so, we add 1,000 more units only to that product.
In the previous exercise, within the ProductController class, we connected to the
repository through the ProductRepository interface reference to maximize loose
coupling. Similarly, now we have connected the Service layer and repository layer through the ProductRepository interface reference as follows in the ProductServiceImpl class:
@Autowired
private ProductRepository productRepository;
As you already learned, Spring assigns the InMemoryProductRepository object to
productRepository reference in the previously mentioned code because the
productRepository reference has an @Autowired annotation and we know that Spring creates and manages all the @Service and @Repository objects. Remember that
OrderServiceImpl has an @Service annotation on top of it.
To ensure transactional behavior, Spring provides an @Transactional (org.springframework.transaction.annotation.Transactional) annotation. We must annotate service methods with an @Transactional annotation to define transaction attributes, and we need to make some
more configurations in our application context to ensure the transactional behavior takes effect.
Since our book is about Spring MVC and the Presentation layer, I omitted the @Transactional annotation from our Service layer objects. To find out more about transactional management in Spring, check out h t t p : / / d
o c s . s p r i n g . i o / s p r i n g / d o c s / c u r r e n t / s p r i n g - f r a m e w o r k - r e f e r e
n c e / h t m l / t r a n s a c t i o n . h t m l.
Spring MVC Architecture – Architecting Your Web Store
Okay, we have created the Service layer, and now it is ready to be consumed from the Presentation layer. It is time for us to connect our Service layer with the controller. In step 5,
we created one more controller method called updateStock in ProductController to call the service object method:
@RequestMapping("/update/stock")
public String updateStock(Model model) {
productService.updateAllStock();
return "redirect:/products";
}
The updateStock method from ProductController class uses our productService reference to update all the stock.
You can also see that we mapped the /update/stock URL path to the updateStock method using the @RequestMapping annotation. So finally, when we are trying to hit the URL http://localhost:8080/webstore/update/stock, we are able to see the
available units in stock being updated by 1,000 more units for the product P1234.