Creating “Add Product” View and Controller

Creating Add Product Views and Controllers

We would like to make a views to insert a new product like this.

Snap 2014-01-02 at 10.23.36

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ page session="false"%>

<html>
<head>
<title>Add new product</title>
<link type="text/css"
	href="<c:url value="/resources/css/bootstrap.css"/>" rel="stylesheet" />
</head>
<body>
	<div class="container">
		<div class="row">
			<nav class="navbar navbar-default" role="navigation">
				<ul class="nav navbar-nav">
					<li><a href="<c:url value="/"/>">Home</a></li>
					<li><a href="<c:url value="/category/"/>">Category</a></li>
					<li><a href="<c:url value="/product/"/>">Product</a></li>
				</ul>
			</nav>
		</div>
	</div>


	<div class="container">
		<div class="row">
			<h1>Add new product</h1>

			<form:form action="addProductConfirm" method="post"
				commandName="productBean">
				<div class="form-group">
					<label for="productName">Product Name</label>
					<form:input path="productName" id="productName" />
				</div>
				<div class="form-group">
					<label for="productStock">Stock</label>
					<form:input path="productStock" id="productStock" />
				</div>
				<div class="form-group">
					<label for="productPrice">Price</label>
					<form:input path="productPrice" id="productPrice" />
				</div>

				<div class="form-group">
					<label for="productDescription">Description</label>
					<form:textarea path="productDescription" id="productDescription" />
				</div>

				<div class="form-group">
					<label for="category">Category</label>
					<form:select path="category" items="${categories}"
						itemLabel="categoryName" itemValue="categoryId">
					</form:select>
				</div>

				<div class="form-group">
					<br />
					<button type="submit" class="btn btn-default btn-success">Submit</button>
				</div>
			</form:form>
		</div>
	</div>
</body>
</html>


Based on our O/R mapping, we got this condition:

  • The product table has categoryId as foreign key.
  • The Product class entity has property with Category type as the mapping object.

In the making of view page:

  • Every attribute in entity class is mapped in view page with path=”attributeName”.
  • We have combo box to select the desired Category when adding new Product. This value is mapped in path=”category”.

g5440
So, here is the problem. When we choose the category combo box, we only get text value (from itemValue=”categoryId” in the <form:select>). But the entity class need a Category object to be mapped. If we directly sending this value to the controller by using @ModelAttribute, it will produce an error.

The solution is to convert this itemValue into a Category object. It can be done by using a property editor class and adding it into @InitBinder in controller class. For information, property editor class is a class that converts a value into desired object. It is inherited from PropertyEditorSupport class.

In our case, it will convert an itemValue (which is text of categoryId) to Category object (which we can get from categoryService.findById(categoryId) method).

package org.munif.bookstore;

import java.beans.PropertyEditorSupport;

import org.munif.domain.Category;
import org.munif.service.CategoryService;
import org.springframework.stereotype.Component;

@Component
public class CategoryEditor extends PropertyEditorSupport {
	
	// Do not use @Autowired.
	// Use dependency injection in class constructor.
	private final CategoryService categoryService;
	
	public CategoryEditor() {
		this.categoryService = null;
	}
	
	//This constructor will be used to inject the categoryService.
	public CategoryEditor(CategoryService categoryService) {
		this.categoryService = categoryService;
	}
	
	@Override
	public void setAsText(String text) throws IllegalArgumentException {
		// Find a category by its categoryId from text
		Category category = categoryService.findById(Integer.parseInt(text));
		setValue(category);
	}	
}

And put it at initBinder method in our controller class.

package org.munif.bookstore;

import org.hibernate.Hibernate;
import org.munif.domain.Category;
import org.munif.domain.Product;
import org.munif.service.CategoryService;
import org.munif.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ProductController {
	
	@Autowired
	private ProductService productService;
	
	@Autowired
	private CategoryService categoryService;
	

	// Registering CategoryEditor class.
	// If you need any other editor class declaration, you can register inside this method.
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(Category.class, new CategoryEditor(categoryService));
	}
	

	@RequestMapping(value="/product/", method=RequestMethod.GET)
	public ModelAndView productHome() {
		ModelAndView mav = new ModelAndView("/product/list");
		Hibernate.initialize(productService.findAll());
		mav.addObject("products", productService.findAll());
		
		return mav;
	}
	
	
	
	@RequestMapping(value="/product/add", method=RequestMethod.GET)
	public ModelAndView productForm() {
		ModelAndView mav = new ModelAndView("/product/add");
		mav.addObject("productBean", new Product());
		mav.addObject("categories", categoryService.findAll());
		
		return mav;
	}
	
	@RequestMapping(value="/product/addProductConfirm", method=RequestMethod.POST)
	public ModelAndView productAddConfirm(@ModelAttribute Product product, BindingResult result) {
		ModelAndView mav = new ModelAndView("redirect:/product/");
		productService.addProduct(product);
		
		return mav;
	}
	
	
	@RequestMapping(value="/product/cat/{categoryId}", method=RequestMethod.GET)
	public ModelAndView productListByCategory(@PathVariable Integer categoryId) {
		ModelAndView mav = new ModelAndView("/product/listByCategory");
				
		mav.addObject("products", categoryService.findById(categoryId).getProducts());
		mav.addObject("categoryName", categoryService.findById(categoryId).getCategoryName());
		
		return mav;
	}
}

Additional Setting

To activate the Hibernate LAZY loading for EVERY entity class, you need to add a filter (OpenSessionInViewFilter) in your web.xml file. (Info: web.xml is in your WEB-INF directory).

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

	<!-- The definition of the Root Spring Container shared by all Servlets 
		and Filters -->
	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>/WEB-INF/spring/root-context.xml</param-value>
	</context-param>

	<!-- Creates the Spring Container shared by all Servlets and Filters -->

	<!-- Filter for Hibernate LAZY loading -->
	<filter>
		<filter-name>hibernateFilter</filter-name>
		<filter-class>org.springframework.orm.hibernate4.support.OpenSessionInViewFilter</filter-class>
		<init-param>
			<param-name>sessionFactoryBeanName</param-name>
			<param-value>sessionFactory</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>hibernateFilter</filter-name>
		<url-pattern>/*</url-pattern>
		<dispatcher>REQUEST</dispatcher>
		<dispatcher>FORWARD</dispatcher>
	</filter-mapping>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<!-- Processes application requests -->
	<servlet>
		<servlet-name>appServlet</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>appServlet</servlet-name>
		<url-pattern>/</url-pattern>
	</servlet-mapping>
</web-app>
Advertisements

3 thoughts on “Creating “Add Product” View and Controller

  1. Hi Munif, very informative post on Spring MVC with CRUD. Couldn’t find the source code, please make it available.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s