You are currently viewing Global Exceptional Handling in Spring and Hibernate using @ControllerAdvice and @ExceptionHandler

Global Exceptional Handling in Spring and Hibernate using @ControllerAdvice and @ExceptionHandler

Hello guys, Bushan here, welcome back to B2 Tech. If you’are creating any kind of application handling exceptions is the most important part because it allows us to run the application smoothly without crashing anywhere. Today in this article, let’s discuss how to handle Exceptions in Spring and Hibernate using @ControllerAdvice and @ExceptionHandler.




@ControllerAdvice used for global error handling in the Spring MVC application. It also has full control over the body of the response and the status code.

Using @ControllerAdvice you can have a centralized way to handle exceptions, binding, etc. it applies to all the defined controller.

@ExceptionHandler works at the Controller level and it is only active for that particular Controller, not globally for the entire application.

@ExceptionHandler is local to a controller: only exceptions from this controller is routed to his @ExceptionHandler

You don’t handle exceptions at all levels of your application simultaneously; you have to consider their contextual meaning and what the appropriate strategy is for your application. Some errors should be ignored, some should be wrapped, some should be allowed to be raised directly.

One method for dealing with exceptions in a Spring-MVC application is to wrap fatal errors from the underlying libraries with your own where appropriate, named for the level at which they are thrown, e.g. ServiceException or RepositoryException. A @ControllerAdvice-annotated class can then handle these errors with @ErrorHandler-annotated methods and return 5XX HTTP errors.




Common application errors, like an entity not being found due to an incorrect id, can result in a custom exception, e.g. NotFoundException being raised and subsequently caught in your @ControllerAdvice-annotated class.

The advantage to this technique is that you have less error-handling code in your different application layers and can centralize the conversion of exceptions to responses.

Here in this article, we are going to handle 3 types of Exceptions, if the user types a wrong URL or if the request mapping is not found then we will handle the 404/PageNotFoundException. Next, if the entity is not found or the id is not found then we will handle IdNotFoundException and finally, we are going to handle a Generalized exception.

Let’s create a maven web app project. So here is my project structure

 

 

 

 

 

 

 

 

 

 

Next, create a Database

CREATE DATABASE crud;

Next, create a Table

CREATE TABLE country(
    id INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
    countryName VARCHAR(255) NOT NULL,
    population INT NOT NULL
);

pom.xml

<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.arpit.java2blog</groupId>
	<artifactId>SpringMVCHibernateCRUDExample</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>SpringMVCHibernateCRUDExample Maven Webapp</name>
	<url>http://maven.apache.org</url>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.4.1</version>
		</dependency>
		<!-- Hibernate -->
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>

		<!-- Apache Commons DBCP -->
		<dependency>
			<groupId>commons-dbcp</groupId>
			<artifactId>commons-dbcp</artifactId>
			<version>1.4</version>
		</dependency>
		<!-- Spring ORM -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- AspectJ -->
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>${org.aspectj-version}</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
		<!-- https://mvnrepository.com/artifact/jstl/jstl -->
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jsp-api</artifactId>
			<version>2.0</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<finalName>SpringMVCHibernateCRUDExample</finalName>

		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.1</version>
				<configuration>
					<source>${jdk.version}</source>
					<target>${jdk.version}</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>

	</build>
	<properties>
		<spring.version>4.2.1.RELEASE</spring.version>
		<security.version>4.0.3.RELEASE</security.version>
		<jdk.version>1.7</jdk.version>
		<hibernate.version>4.3.5.Final</hibernate.version>
		<org.aspectj-version>1.7.4</org.aspectj-version>
	</properties>
</project>

Add the below code in web.xml for handling exceptions




<!-- For throwing exception -->
<init-param>
    <param-name>throwExceptionIfNoHandlerFound</param-name>
    <param-value>true</param-value>
</init-param>

Create a class called GlobalDefaultExceptionHandler and annotate this class with @ControllerAdvice

Inside this class write a method and annotate this method with @ExceptionHandler and pass NoHandlerFoundException.class as a parameter.

@ExceptionHandler(NoHandlerFoundException.class)
	public ModelAndView handlerNoHandlerFoundException() {
		
		ModelAndView mv = new ModelAndView("error");
		
		mv.addObject("errorTitle", "The page is not constructed!");
		
		mv.addObject("errorDescription", "The page you are looking for is not available now!");
		
		mv.addObject("title", "404 Error Page");
		
		return mv;
	}

Inside the same class, write one more method to handle customized exception which is EntityNotFoundException/IdNotFoundException/CountryNotFoundException and we will annotate this method with @ExceptionHandler but this time we will pass our customized exception class CountryNotFoundException.class as a parameter.

@ExceptionHandler(CountryNotFoundException.class)
	public ModelAndView handleCountryNotFoundException() {
		
		ModelAndView mv = new ModelAndView("error");
		
		mv.addObject("errorTitle", "Country not available!");
		
		mv.addObject("errorDescription", "The country you are looking for is not available right now!");
		
		mv.addObject("title", "Country Unavailable");
		
		return mv;
	}

Inside the controller, while retrieving the Country, check for the NULL, if it is NULL then throw CountryNotFoundException

Country country = this.countryService.getCountry(id);
if(country == null) throw new CountryNotFoundException();

To handle the generalized exception, inside the same class we will write one more method and will annotate this method with @ExceptionHandler and this time we will pass Exception.class as a parameter.

@ExceptionHandler(Exception.class)
	public ModelAndView handlerException(Exception ex) {
		
		ModelAndView mv = new ModelAndView("error");
		
		mv.addObject("errorTitle", "Contact Your Administrator!!");
						
		mv.addObject("errorDescription", ex.toString());
		
		mv.addObject("title", "Error");
		
		return mv;
	}

To display the error messages to the user we will create a view page called error.jsp and will display the error messages.




<h1>${errorTitle}</h1>
<hr/>
<blockquote style="word-wrap:break-word">
    ${errorDescription}
</blockquote>

Here is the complete source code for the example.

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<display-name>Archetype Created Web Application</display-name>

<servlet>
	<servlet-name>spring</servlet-name>
 	<servlet-class>
  		org.springframework.web.servlet.DispatcherServlet
 	</servlet-class>
 	<!-- For throwing exception -->
 	<init-param>
		<param-name>throwExceptionIfNoHandlerFound</param-name>
		<param-value>true</param-value>
	</init-param>
 	<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
	<servlet-name>spring</servlet-name>
	<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

spring-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<annotation-driven />

	<resources mapping="/resources/**" location="/resources/" />

	<beans:bean
		class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<beans:property name="prefix" value="/WEB-INF/views/" />
		<beans:property name="suffix" value=".jsp" />
	</beans:bean>

	<beans:bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close">
		<beans:property name="driverClassName" value="com.mysql.jdbc.Driver" />
		<beans:property name="url"
			value="jdbc:mysql://localhost:3306/crud" />
		<beans:property name="username" value="root" />
		<beans:property name="password" value="" />
	</beans:bean>

	<!-- Hibernate 4 SessionFactory Bean definition -->
	<beans:bean id="hibernate4AnnotatedSessionFactory"
		class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<beans:property name="dataSource" ref="dataSource" />
		<beans:property name="annotatedClasses">
			<beans:list>
				<beans:value>in.bushansirgur.model.Country</beans:value>
			</beans:list>
		</beans:property>
		<beans:property name="hibernateProperties">
			<beans:props>
				<beans:prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect
				</beans:prop>
				<beans:prop key="hibernate.show_sql">true</beans:prop>
			</beans:props>
		</beans:property>
	</beans:bean>

	<context:component-scan base-package="in.bushansirgur" />

	<tx:annotation-driven transaction-manager="transactionManager" />

	<beans:bean id="transactionManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<beans:property name="sessionFactory"
			ref="hibernate4AnnotatedSessionFactory" />
	</beans:bean>


</beans:beans>

Country.java




package in.bushansirgur.model;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/*
 * This is our model class and it corresponds to Country table in database
 */
@Entity
@Table(name="country")
public class Country{
	
	@Id
	@Column(name="id")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	int id;
	
	@Column(name="countryName")
	String countryName;	
	
	@Column(name="population")
	long population;
	
	public Country() {
		super();
	}
	public Country(int i, String countryName,long population) {
		super();
		this.id = i;
		this.countryName = countryName;
		this.population=population;
	}
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getCountryName() {
		return countryName;
	}
	public void setCountryName(String countryName) {
		this.countryName = countryName;
	}
	public long getPopulation() {
		return population;
	}
	public void setPopulation(long population) {
		this.population = population;
	}	
	
}

CountryDAO.java

package in.bushansirgur.dao;

import java.util.List;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import in.bushansirgur.model.Country;

@Repository
public class CountryDAO {

	@Autowired
	private SessionFactory sessionFactory;

	public void setSessionFactory(SessionFactory sf) {
		this.sessionFactory = sf;
	}

	public List<Country> getAllCountries() {
		Session session = this.sessionFactory.getCurrentSession();
		List<Country> countryList = session.createQuery("from Country").list();
		return countryList;
	}

	public Country getCountry(int id) {
		Session session = this.sessionFactory.getCurrentSession();
		Country country = (Country) session.get(Country.class, new Integer(id));
		return country;
	}

	public Country addCountry(Country country) {
		Session session = this.sessionFactory.getCurrentSession();
		session.persist(country);
		return country;
	}

	public void updateCountry(Country country) {
		Session session = this.sessionFactory.getCurrentSession();
		session.update(country);
	}

	public void deleteCountry(int id) {
		Session session = this.sessionFactory.getCurrentSession();
		Country p = (Country) session.load(Country.class, new Integer(id));
		if (null != p) {
			session.delete(p);
		}
	}	
}

CountryService.java

package in.bushansirgur.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import in.bushansirgur.dao.CountryDAO;
import in.bushansirgur.model.Country;

@Service("countryService")
public class CountryService {

	@Autowired
	CountryDAO countryDao;
	
	@Transactional
	public List<Country> getAllCountries() {
		return countryDao.getAllCountries();
	}

	@Transactional
	public Country getCountry(int id) {
		return countryDao.getCountry(id);
	}

	@Transactional
	public void addCountry(Country country) {
		countryDao.addCountry(country);
	}

	@Transactional
	public void updateCountry(Country country) {
		countryDao.updateCountry(country);

	}

	@Transactional
	public void deleteCountry(int id) {
		countryDao.deleteCountry(id);
	}
}

CountryController.java




package in.bushansirgur.controller;


import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
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.servlet.ModelAndView;

import in.bushansirgur.exception.CountryNotFoundException;
import in.bushansirgur.model.Country;
import in.bushansirgur.service.CountryService;

@Controller
public class CountryController {
	
	@Autowired
	CountryService countryService;
	
	@RequestMapping(value = {"/", "/index", "/home", "/getAllCountries"}, method = RequestMethod.GET, headers = "Accept=application/json")
	public String getCountries(Model model) {
		
		List<Country> listOfCountries = countryService.getAllCountries();
		model.addAttribute("country", new Country());
		model.addAttribute("listOfCountries", listOfCountries);
		return "countryDetails";
	}

	@RequestMapping(value = "/getCountry/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
	public Country getCountryById(@PathVariable int id){
		return countryService.getCountry(id);
	}

	@RequestMapping(value = "/addCountry", method = RequestMethod.POST, headers = "Accept=application/json")
	public String addCountry(@ModelAttribute("country") Country country) {	
		if(country.getId()==0)
		{
			countryService.addCountry(country);
		}
		else
		{	
			countryService.updateCountry(country);
		}
		
		return "redirect:/getAllCountries";
	}

	@RequestMapping(value = "/updateCountry/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
	public String updateCountry(@PathVariable("id") int id,Model model)  throws CountryNotFoundException{
		Country country = this.countryService.getCountry(id);
		if(country == null) throw new CountryNotFoundException();
		model.addAttribute("country", country);
	        model.addAttribute("listOfCountries", this.countryService.getAllCountries());
	        return "countryDetails";
	}

	@RequestMapping(value = "/deleteCountry/{id}", method = RequestMethod.GET, headers = "Accept=application/json")
	public String deleteCountry(@PathVariable("id") int id) {
		countryService.deleteCountry(id);
		 return "redirect:/getAllCountries";

	}	
}

countryDetails.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form" %>
<html>
<head>
<style>           
.blue-button{
	background: #25A6E1;
	filter: progid: DXImageTransform.Microsoft.gradient( startColorstr='#25A6E1',endColorstr='#188BC0',GradientType=0);
	padding:3px 5px;
	color:#fff;
	font-family:'Helvetica Neue',sans-serif;
	font-size:12px;
	border-radius:2px;
	-moz-border-radius:2px;
	-webkit-border-radius:4px;
	border:1px solid #1A87B9
}     
table {
  font-family: "Helvetica Neue", Helvetica, sans-serif;
   width: 50%;
}
th {
  background: SteelBlue;
  color: white;
}
 td,th{
                border: 1px solid gray;
                width: 25%;
                text-align: left;
                padding: 5px 10px;
            }
</style>
</head>
<body>
<form:form method="post" modelAttribute="country" action="/SpringMVCHibernateCRUDExample/addCountry">
<table>
		<tr>
			<th colspan="2">Add Country</th>
		</tr>
		<tr>
		<form:hidden path="id" />
          <td><form:label path="countryName">Country Name:</form:label></td>
          <td><form:input path="countryName" size="30" maxlength="30"></form:input></td>
        </tr>
		<tr>
			    <td><form:label path="population">Population:</form:label></td>
          <td><form:input path="population" size="30" maxlength="30"></form:input></td>
		</tr>
		<tr>
			<td colspan="2"><input type="submit"
				class="blue-button" /></td>
		</tr>
	</table> 
</form:form>

<h3>Country List</h3>
<c:if test="${!empty listOfCountries}">
	<table class="tg">
	<tr>
		<th width="80">Id</th>
		<th width="120">Country Name</th>
		<th width="120">Population</th>
		<th width="60">Edit</th>
		<th width="60">Delete</th>
	</tr>
	<c:forEach items="${listOfCountries}" var="country">
		<tr>
			<td>${country.id}</td>
			<td>${country.countryName}</td>
			<td>${country.population}</td>
			<td><a href="<c:url value='/updateCountry/${country.id}' />" >Edit</a></td>
			<td><a href="<c:url value='/deleteCountry/${country.id}' />" >Delete</a></td>
		</tr>
	</c:forEach>
	</table>
</c:if>
</body>
</html>

error.jsp

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>

<c:set var="contextRoot" value="${pageContext.request.contextPath}" />
<!DOCTYPE html>
<html>
	<head>
		<title>${title}</title>
		
		<!-- Latest compiled and minified CSS -->
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
	</head>
	<body>
		<div class = "container">
			<div class = "jumbotron">
				<h1>${errorTitle}</h1>
				<hr/>
				<blockquote style="word-wrap:break-word">
					${errorDescription}
				</blockquote>
				<button type = "button" class = "btn btn-primary" onclick="window.location.href='${contextRoot}/home'">Home</button>
			</div>
		</div>
		
		<!-- jQuery library -->
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
		
		<!-- Latest compiled JavaScript -->
		<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
	</body>
</html>

Here is the quick demo of the application,




That’s it for this article, I hope this article is helped you in one or the other way, if so please share this article with your social media profiles. If you have any queries regarding this article then leave your questions in the comment section I will try to answer it. If you are looking for source code of this example, then I give a link to the source code at the end of this article. Thank you so much of reading, I will see you in the next article.




Bushan Sirgur

Hey guys, I am Bushan Sirgur from Banglore, India. Currently, I am working as an Associate project in an IT company.

This Post Has One Comment

  1. luqmaan s

    How can the @ControllerAdvice annotation be used to handle multiple types of exceptions, including custom exceptions like CountryNotFoundException and generalized exceptions, in a Spring MVC application?

Leave a Reply