Development
Hibernate Object
Product.java - We reverse engineer this class from the database. It is the model for the application, which is mapped to the database table. It is self-explanatory.
DAO layer
AbstractDAO<T> - This is a generic class which contains common calls to hibernate API. All DAO can extend this class to reuse these pre-implemented class. Note, how the sessionFactory is injected by Spring framework into the DAO layer using @Autowired annotation. The sessionFactory is declared in the Catalog-Servlet.xml file which we defined earlier.
The JavaDoc comments on each of the method, explain the use of the function.
package com.tech_freaks.catalog.dao;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
/**
* Generic Base class which encapsulates hibernate specific calls which will be available from the
* extending DAO which is mapped to the database table.
* @author Tech Freaks
*
* @param <T> - The Hibernate object which it works on
*/
public class AbstractDAO <T> {
@Autowired
private SessionFactory sessionFactory;
/**
* Gets a current session from the hibernate factory
* @return
*/
protected Session getSession(){
return sessionFactory.getCurrentSession();
}
/**
* Generic method for finding by primary key.
* @param id - Primary key value in integer
* @param myClass - Type of Class which will be loaded
* @return T representing instance of the loaded hibernate class instance
*/
public <T> T findById(Integer id, Class myClass) {
return (T) getSession().load(myClass, id);
}
/**
* Inserts record into database
* @param t - Instance of the Hibernate object which needs to be persisted in the db
* @return - Key value of the newly inserted record
*/
public Integer persist(T t) {
return (Integer) getSession().save(t);
}
/**
* Updates the database for provided generic type
* @param t Generic type for the hibernate object
*/
public void update(T t) {
getSession().update(t);
getSession().flush();
}
/**
* Deletes the entity from the database for the provided generic type
* @param t Generic type for the hibernate object
*/
public void delete(T t) {
getSession().delete(t);
getSession().flush();
}
}
ProductDAOImpl & ProductDAO
This is the actual DAO interface and its implementation. It extends the AbstractDAO<Product> and utilizes the methods defined there.
package com.tech_freaks.catalog.dao;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import com.tech_freaks.catalog.model.Product;
public class ProductDAOImpl extends AbstractDAO<Product> implements ProductDAO {
/**
* Fetches all the product from the PRODUCT table
*/
@Override
public List<Product> getAllProducts() {
Session session = getSession();
List<Product> productList = session.createQuery("from Product").list();
return productList;
}
/**
* Fetches product based on the primary key productId
*/
@Override
public Product findById(Integer id) {
return (Product) getSession().get(Product.class, id);
}
/**
* Inserts a new record in the product table.
*/
@Override
public Integer insert(Product product) {
return persist(product);
}
/**
* Loads the product using the primary key and then removes it from the database.
*/
@Override
public boolean remove(Integer id) {
boolean deleted = false;
Product delProduct = findById(id);
if(delProduct!=null) {
delete(delProduct);
deleted = true;
}
return deleted;
}
}
ProductServiceImpl & ProductService
The is the Service layer interface and its implementation for the Product manipulation. It internally calls the DAO layer. The Service layer is called from the Controller class. The methods are self-explanatory.
The below excerpt from ProductServiceImpl shows @Autowiring annotation on productDAO, which is injected in by the Spring framework. Also, each method is marked with annotation @Transactional. This is the reason, you do not see any code related to transaction management in the DAO layer. Spring manages the transaction code for you.
...
@Autowired
private ProductDAO productDAO;
@Override
@Transactional
public List<Product> listProducts() {
return productDAO.getAllProducts();
}
...
CatalogRestController is the entry point into the code for all REST service verbs. The @RestController annotation eliminates the need to mark each method with @ResponseBody when it returns an Object or a Collection of objects. Now even without @ResponseBody, when an object or collection of object is returned, it is automatically converted into JSON. The class is also marked with @RequestMapping("/products"). This means the REST URL for every verb call will have '/products' in it, after the application context name. Now, each method is marked with @RequestMethod. We follow the REST specification by mapping GET to retrieve data, POST for insert data, PUT for update data and DELETE for deleting data.
If we look at the method "add", there are certain points worth noticing.
First, after every REST call, we return a HttpStatusCode. If the insert is successful, we return CREATED status or 201. If it fails, because the Product object was not populated, it means, the JSON was not well formed. In this case, we return BAD_REQUEST or 400. This helps the client to read the HttpHeader in the response and take appropriate action.
Second, after insert, the method create a REST URL to fetching the newly inserted record. This is set in the Location HttpHeader.
The private method isValid() is called internally to perform some basic validation before the service layer is invoked.
Similarly, all the other method are developed. The complete CatalogRestController code is below.
package com.tech_freaks.catalog.controller;
import java.net.URI;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.tech_freaks.catalog.model.Product;
import com.tech_freaks.catalog.service.ProductService;
@RestController
@RequestMapping("/products")
/**
* Entry point for REST service calls. Different method for handling insert, update, delete and get
*/
public class CatalogRestController {
protected final Log logger = LogFactory.getLog(getClass());
@Autowired
private ProductService productService;
@RequestMapping(method = RequestMethod.GET)
public List<Product> getProducts() {
return productService.listProducts();
}
@RequestMapping(value = "/{productId}", method = RequestMethod.DELETE)
public ResponseEntity<?> remove(@PathVariable Integer productId) {
boolean deleted = false;
ResponseEntity<?> response = null;
try {
deleted = productService.removeProduct(productId);
} catch (Exception e) {
logger.error("Error deleting product from database: "
+ e.getMessage());
}
if (deleted)
response = new ResponseEntity<>(HttpStatus.OK);
else
response = new ResponseEntity<>(HttpStatus.NO_CONTENT);
return response;
}
@RequestMapping(value = "/{productId}", method = RequestMethod.GET)
public ResponseEntity<?> getProduct(@PathVariable Integer productId) {
ResponseEntity<Product> response = null;
Product product = productService.getProduct(productId);
if (product != null) {
response = new ResponseEntity<>(product, HttpStatus.OK);
} else {
response = new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return response;
}
@RequestMapping(value = "/{productId}", method = RequestMethod.PUT)
public ResponseEntity<?> update(@PathVariable Integer productId,
@RequestBody Product input) {
ResponseEntity<?> response = new ResponseEntity<>(HttpStatus.OK);
logger.info("Product Id to be updated is " + productId);
logger.info("New Product Name will be " + input.getName());
try {
if (productId > 0 && isValid(input)) {
input.setProductId(productId);
productService.updateProduct(input);
response = new ResponseEntity<>(HttpStatus.OK);
} else {
response = new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
} catch (HibernateException he) {
logger.error("Unable to update product in database: "+ he.getMessage());
response = new ResponseEntity<>(HttpStatus.NO_CONTENT);
} catch (Exception e) {
logger.error("Unable to update product in database: "+ e.getMessage());
response = new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
return response;
}
@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> add(@RequestBody Product input) {
HttpHeaders httpHeaders = new HttpHeaders();
ResponseEntity<?> response = null;
if (isValid(input)) {
logger.info("Product description is " + input.getDescription());
try {
Integer productId = productService.createProduct(input);
URI location = ServletUriComponentsBuilder
.fromCurrentServletMapping()
.path("/products/{productId}.json").build()
.expand(productId).toUri();
logger.info("Location uri is " + location.toString());
httpHeaders.setLocation(location);
response = new ResponseEntity<>(null, httpHeaders,
HttpStatus.CREATED);
} catch (Exception e) {
logger.error("Unable to insert new product in database: "
+ e.getMessage());
response = new ResponseEntity<>(null, httpHeaders,
HttpStatus.UNPROCESSABLE_ENTITY);
}
} else {
response = new ResponseEntity<>(null, httpHeaders,
HttpStatus.BAD_REQUEST);
}
return response;
}
/*
* Internally used to validate the fields to make sure, all required fields
* are present before calling the service layer for insert
*/
private boolean isValid(Product product) {
boolean valid = false;
if (product != null) {
if (!StringUtils.isEmpty(product.getName())
&& !StringUtils.isEmpty(product.getDescription())
&& !StringUtils.isEmpty(product.getPartnumber())
&& !StringUtils.isEmpty(product.getName()))
valid = true;
}
return valid;
}
}
At this point, you can download the complete project from GitHub at https://github.com/tech-freaks/catalog-service