Cascading Dropdown List with Ajax in Spring MVC Framework and Spring Data JPA

This project will demonstrate how to create Cascading Dropdown List with Ajax in Spring MVC Framework and Spring Data JPA

  • SpringMVCFramework
    • src/main/java
      • com.demo
        • SpringMVCFramework.java
      • com.demo.controllers
        • DemoController.java
      • com.demo.entities
        • Country.java
        • State.java
        • StateEntity.java
        • City.java
        • CityEntity.java
      • com.demo.repositories
        • CountryRepository.java
        • StateRepository.java
        • CityRepository.java
      • com.demo.services
        • CountryService.java
        • CountryServiceImpl.java
        • StateService.java
        • StateServiceImpl.java
        • CityService.java
        • CityServiceImpl.java
    • src/main/resources
      • static
      • templates
      • application.properties
    • src
      • main
        • webapp
          • WEB-INF
            • views
              • demo
                • index.jsp
    • pom.xml
package com.demo;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class SpringMVCFramework {
    
        public static void main(String[] args) {
            SpringApplication.run(SpringMVCFramework.class, args);
        }
    }
package com.demo.controllers;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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.bind.annotation.ResponseBody;

import com.demo.services.CityService;
import com.demo.services.CountryService;
import com.demo.services.StateService;
import com.google.gson.Gson;

@Controller
@RequestMapping(value = { "", "demo" })
public class DemoController {

    @Autowired
    private CountryService countryService;

    @Autowired
    private StateService stateService;

    @Autowired
    private CityService cityService;

    @RequestMapping(method = RequestMethod.GET)
    public String index(ModelMap modelMap) {
        modelMap.put("countries", countryService.findAll());
        return "demo/index";
    }

    @ResponseBody
    @RequestMapping(value = "loadStatesByCountry/{id}", method = RequestMethod.GET)
    public String loadStatesByCountry(@PathVariable("id") int id) {
        Gson gson = new Gson();
        return gson.toJson(stateService.findByCountry(id));
    }

    @ResponseBody
    @RequestMapping(value = "loadCitiesByState/{id}", method = RequestMethod.GET)
    public String loadCitiesByState(@PathVariable("id") int id) {
        Gson gson = new Gson();
        return gson.toJson(cityService.findByState(id));
    }

}
package com.demo.entities;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "country")
public class Country implements java.io.Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private String name;
    private Set<State> states = new HashSet<State>(0);

    public Country() {
    }

    public Country(String name) {
        this.name = name;
    }

    public Country(String name, Set<State> states) {
        this.name = name;
        this.states = states;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "name", nullable = false, length = 250)
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "country")
    public Set<State> getStates() {
        return this.states;
    }

    public void setStates(Set<State> states) {
        this.states = states;
    }

}
package com.demo.entities;

import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "state")
public class State implements java.io.Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private Country country;
    private String name;
    private Set<City> cities = new HashSet<City>(0);

    public State() {
    }

    public State(Country country, String name) {
        this.country = country;
        this.name = name;
    }

    public State(Country country, String name, Set<City> cities) {
        this.country = country;
        this.name = name;
        this.cities = cities;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "countryId", nullable = false)
    public Country getCountry() {
        return this.country;
    }

    public void setCountry(Country country) {
        this.country = country;
    }

    @Column(name = "name", nullable = false, length = 250)
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "state")
    public Set<City> getCities() {
        return this.cities;
    }

    public void setCities(Set<City> cities) {
        this.cities = cities;
    }
}
package com.demo.entities;

import java.io.Serializable;

public class StateEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private String name;

    public StateEntity() {
    }

    public StateEntity(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
package com.demo.entities;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import static javax.persistence.GenerationType.IDENTITY;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "city")
public class City implements java.io.Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private State state;
    private String name;

    public City() {
    }

    public City(State state, String name) {
        this.state = state;
        this.name = name;
    }

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "stateId", nullable = false)
    public State getState() {
        return this.state;
    }

    public void setState(State state) {
        this.state = state;
    }

    @Column(name = "name", nullable = false, length = 250)
    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
package com.demo.entities;

import java.io.Serializable;

public class CityEntity implements Serializable {

    private static final long serialVersionUID = 1L;
    private Integer id;
    private String name;

    public CityEntity() {
    }

    public CityEntity(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
package com.demo.repositories;

import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

import com.demo.entities.Country;

@Repository("countryRepository")
public interface CountryRepository extends CrudRepository<Country, Integer> {
}
package com.demo.repositories;

import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.demo.entities.State;
import com.demo.entities.StateEntity;

@Repository("stateRepository")
public interface StateRepository extends CrudRepository<State, Integer> {

    @Query("select new com.demo.entities.StateEntity(id, name) from State where country.id = :id")
    public List<StateEntity> findByCountry(@Param("id") int id);

}
package com.demo.repositories;

import java.util.List;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import com.demo.entities.City;
import com.demo.entities.CityEntity;

@Repository("cityRepository")
public interface CityRepository extends CrudRepository<City, Integer> {

    @Query("select new com.demo.entities.CityEntity(id, name) from City where state.id = :id")
    public List<CityEntity> findByState(@Param("id") int id);

}
package com.demo.services;

import com.demo.entities.Country;

public interface CountryService {

    public Iterable<Country> findAll();

    public Country find(int id);

}
package com.demo.services;

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

import com.demo.entities.Country;
import com.demo.repositories.CountryRepository;

@Service("countryService")
public class CountryServiceImpl implements CountryService {

    @Autowired
    private CountryRepository countryRepository;

    @Override
    public Iterable<Country> findAll() {
        return countryRepository.findAll();
    }

    @Override
    public Country find(int id) {
        return countryRepository.findById(id).get();
    }

}
package com.demo.services;

import java.util.List;
import com.demo.entities.StateEntity;

public interface StateService {

    public List<StateEntity> findByCountry(int id);

}
package com.demo.services;

import java.util.List;

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

import com.demo.entities.StateEntity;
import com.demo.repositories.StateRepository;

@Service("stateService")
public class StateServiceImpl implements StateService {

    @Autowired
    private StateRepository stateRepository;

    @Override
    public List<StateEntity> findByCountry(int id) {
        return stateRepository.findByCountry(id);
    }

}
package com.demo.services;

import java.util.List;

import com.demo.entities.CityEntity;

public interface CityService {

    public List<CityEntity> findByState(int id);

}
package com.demo.services;

import java.util.List;

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

import com.demo.entities.CityEntity;
import com.demo.repositories.CityRepository;

@Service("cityService")
public class CityServiceImpl implements CityService {

    @Autowired
    private CityRepository cityRepository;

    @Override
    public List<CityEntity> findByState(int id) {
        return cityRepository.findByState(id);
    }

}
spring.mvc.view.prefix = /WEB-INF/views/
spring.mvc.view.suffix = .jsp
spring.mvc.static-path-pattern=/resources/**

server.port=9596

spring.datasource.url= jdbc:mysql://localhost:3306/demo5
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1" isELIgnored="false"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Cascading Dropdown List with Ajax in Spring MVC Framework and Spring Data JPA</title>
<script src="${pageContext.request.contextPath }/resources/js/jquery-1.7.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){

    $('#comboboxCountry').on('change', function(){
        var countryId = $(this).val();
        $.ajax({
            type: 'GET',
            url: '${pageContext.request.contextPath }/demo/loadStatesByCountry/' + countryId,
            success: function(result) {
                var result = JSON.parse(result);
                var s = '';
                for(var i = 0; i < result.length; i++) {
                    s += '<option value="' + result[i].id + '">' + result[i].name + '</option>';
                }
                $('#comboboxState').html(s);
            }
        });
    });


    $('#comboboxState').on('change', function(){
        var stateId = $(this).val();
        $.ajax({
            type: 'GET',
            url: '${pageContext.request.contextPath }/demo/loadCitiesByState/' + stateId,
            success: function(result) {
                var result = JSON.parse(result);
                var s = '';
                for(var i = 0; i < result.length; i++) {
                    s += '<option value="' + result[i].id + '">' + result[i].name + '</option>';
                }
                $('#comboboxCity').html(s);
            }
        });
    });



});
</script>
</head>
<body>

    <form>
        <table>
            <tr>
                <td>Country</td>
                <td>
                    <select id="comboboxCountry" style="width:200px">
                        <c:forEach var="country" items="${countries }">
                            <option value="${country.id }">${country.name }</option>
                        </c:forEach>
                    </select>
                </td>
            </tr>
            <tr>
                <td>State</td>
                <td>
                    <select id="comboboxState" style="width:200px"></select>
                </td>
            </tr>
            <tr>
                <td>City</td>
                <td>
                    <select id="comboboxCity" style="width:200px"></select>
                </td>
            </tr>
        </table>
    </form>

</body>
</html>
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.10.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.demo</groupId>
    <artifactId>CascadingDropDownListWithSpringMVC</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>CascadingDropDownListWithSpringMVC</name>
    <description>Cascading Drop Down List with Spring MVC and Spring Data JPA</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
            <scope>provided</scope>
        </dependency>

        <!-- JSTL tag lib -->
        <dependency>
            <groupId>javax.servlet.jsp.jstl</groupId>
            <artifactId>javax.servlet.jsp.jstl-api</artifactId>
            <version>1.2.1</version>
        </dependency>

        <dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
        </dependency>

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Screenshots