Thursday 29 April 2021

Spring jpa: many to many: Join table has a auto incremented id

In this post, I am going to explain how to model many to many mapping using a join table, where join table has a primary key column in it.

 

Requirement

a.   One student can enrol to zero or more course

b.   One course can be enrolled with zero or more students

 

Tables are modelled like below

 

STUDENT table

id

first_name

last_name

 

 

 

 

 

 

 

COURSE table

id

course_name

 

 

 

 

 

STUDENT_COURSE_MAPPING table

id

student_id

course_id

 

 

 

 

 

 

 

ddl.sql

CREATE TABLE `STUDENT` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `first_name` varchar(100) NOT NULL,
  `last_name` varchar(200) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_STUDENT_FIRST_NAME` (`first_name`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

CREATE TABLE `COURSE` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `course_name` varchar(100) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_COURSE_NAME` (`course_name`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;


CREATE TABLE `STUDENT_COURSE_MAPPING` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `student_id` int(10) unsigned NOT NULL,
  `course_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `UNIQUE_COURSE` (`student_id`, `course_id`),
  KEY `STUDENT_COURSE_MAPPING_STUDENT_ID` (`student_id`),
  KEY `STUDENT_COURSE_MAPPING_COURSE_ID` (`course_id`),
  CONSTRAINT `RECORD_SPEC_CREATED_BY` FOREIGN KEY (`student_id`) REFERENCES `STUDENT` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
  CONSTRAINT `RECORD_SPEC_UPDATED_BY` FOREIGN KEY (`course_id`) REFERENCES `COURSE` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=latin1;

Entity classes are modelled like below.

 

Student.java

@Entity
@Table(name = "STUDENT")
public class Student implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "first_name")
  private String firstName;

  @Column(name = "last_name")
  private String lastName;

  // bi-directional many-to-one association to StudentCourseMapping
  @OneToMany(mappedBy = "student")
  @JsonManagedReference
  private List<StudentCourseMapping> studentCourseMappings;

  .....
  .....
}


Course.java

@Entity
@Table(name = "COURSE")
public class Course implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "course_name")
  private String courseName;

  // bi-directional many-to-one association to StudentCourseMapping
  @OneToMany(mappedBy = "course")
  @JsonManagedReference
  private List<StudentCourseMapping> studentCourseMappings;
  
  .....
  .....
}


StudentCourseMapping.java

@Entity
@Table(name = "STUDENT_COURSE_MAPPING")
public class StudentCourseMapping implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "STUDENT_ID")
  private Integer studentId;

  @Column(name = "COURSE_ID")
  private Integer courseId;

  // bi-directional many-to-one association to Student
  @ManyToOne
  @JsonBackReference
  @JoinColumn(name = "STUDENT_ID", insertable = false, updatable = false)
  private Student student;

  .....
  .....

}


Follow below step-by-step procedure to build complete working application.

 

Step 1: Create new maven project ‘many-to-many-jointable-pk’.

 

Step 2: Update pom.xml with maven dependencies.

 

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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.sample.app</groupId>
  <artifactId>many-to-many-jointable-pk</artifactId>
  <version>1</version>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.6.RELEASE</version>
  </parent>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
    <dependency>
      <groupId>com.h2database</groupId>
      <artifactId>h2</artifactId>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-envers -->
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-envers</artifactId>
    </dependency>

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

    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger2</artifactId>
      <version>2.9.2</version>
    </dependency>


    <dependency>
      <groupId>io.springfox</groupId>
      <artifactId>springfox-swagger-ui</artifactId>
      <version>2.9.2</version>
    </dependency>

  </dependencies>
</project>


Step 3: Create application.properties file under src/main/resources folder.

 

application.properties

logging.level.root=WARN
logging.level.org.hibernate=ERROR

## H2 specific properties
spring.h2.console.enabled=true
spring.h2.console.path=/h2

spring.datasource.url=jdbc:h2:file:~/db/myOrg.db;DB_CLOSE_ON_EXIT=FALSE;DB_CLOSE_DELAY=-1;

spring.datasource.username=krishna
spring.datasource.password=password123

spring.datasource.driverClassName=org.h2.Driver

## JPA specific properties
# Creates the schema, destroying previous data.
spring.jpa.hibernate.ddl-auto=create

spring.jpa.database-platform=org.hibernate.dialect.H2Dialect

spring.jpa.show-sql=true
#spring.jpa.properties.hibernate.format_sql=true

## Database connection pooling properties
# Number of ms to wait before throwing an exception if no connection is available.
spring.datasource.max-wait=10000

# Maximum number of active connections that can be allocated from this pool at the same time.
spring.datasource.tomcat.max-active=10
spring.datasource.tomcat.max-idle=5
spring.datasource.tomcat.min-idle=3

spring.jackson.serialization.FAIL_ON_EMPTY_BEANS=false


Step 4: Define entity classes.

 

Course.java

package com.sample.app.entity;

import java.io.Serializable;
import java.util.List;

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

import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "COURSE")
public class Course implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "course_name")
  private String courseName;

  // bi-directional many-to-one association to StudentCourseMapping
  @OneToMany(mappedBy = "course")
  @JsonManagedReference
  private List<StudentCourseMapping> studentCourseMappings;

  public Course() {
  }

  public Integer getId() {
    return this.id;
  }

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

  public String getCourseName() {
    return this.courseName;
  }

  public void setCourseName(String courseName) {
    this.courseName = courseName;
  }

  public List<StudentCourseMapping> getStudentCourseMappings() {
    return this.studentCourseMappings;
  }

  public void setStudentCourseMappings(List<StudentCourseMapping> studentCourseMappings) {
    this.studentCourseMappings = studentCourseMappings;
  }

  /*
   * public StudentCourseMapping addStudentCourseMapping(StudentCourseMapping
   * studentCourseMapping) { getStudentCourseMappings().add(studentCourseMapping);
   * studentCourseMapping.setCourse(this);
   * 
   * return studentCourseMapping; }
   * 
   * public StudentCourseMapping removeStudentCourseMapping(StudentCourseMapping
   * studentCourseMapping) {
   * getStudentCourseMappings().remove(studentCourseMapping);
   * studentCourseMapping.setCourse(null);
   * 
   * return studentCourseMapping; }
   */

}


Student.java

package com.sample.app.entity;

import java.io.Serializable;
import java.util.List;

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

import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "STUDENT")
public class Student implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "first_name")
  private String firstName;

  @Column(name = "last_name")
  private String lastName;

  // bi-directional many-to-one association to StudentCourseMapping
  @OneToMany(mappedBy = "student")
  @JsonManagedReference
  private List<StudentCourseMapping> studentCourseMappings;

  public Student() {
  }

  public Integer getId() {
    return this.id;
  }

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

  public String getFirstName() {
    return this.firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return this.lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public List<StudentCourseMapping> getStudentCourseMappings() {
    return this.studentCourseMappings;
  }

  public void setStudentCourseMappings(List<StudentCourseMapping> studentCourseMappings) {
    this.studentCourseMappings = studentCourseMappings;
  }

  /*public StudentCourseMapping addStudentCourseMapping(StudentCourseMapping studentCourseMapping) {
    getStudentCourseMappings().add(studentCourseMapping);
    studentCourseMapping.setStudent(this);

    return studentCourseMapping;
  }

  public StudentCourseMapping removeStudentCourseMapping(StudentCourseMapping studentCourseMapping) {
    getStudentCourseMappings().remove(studentCourseMapping);
    studentCourseMapping.setStudent(null);

    return studentCourseMapping;
  }*/

}


StudentCourseMapping.java

package com.sample.app.entity;

import java.io.Serializable;

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

import com.fasterxml.jackson.annotation.JsonBackReference;

@Entity
@Table(name = "STUDENT_COURSE_MAPPING")
public class StudentCourseMapping implements Serializable {
  private static final long serialVersionUID = 1L;

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Integer id;

  @Column(name = "STUDENT_ID")
  private Integer studentId;

  @Column(name = "COURSE_ID")
  private Integer courseId;

  // bi-directional many-to-one association to Student
  @ManyToOne
  @JsonBackReference
  @JoinColumn(name = "STUDENT_ID", insertable = false, updatable = false)
  private Student student;

  // bi-directional many-to-one association to Course
  @ManyToOne
  @JsonBackReference
  @JoinColumn(name = "COURSE_ID", insertable = false, updatable = false)
  private Course course;

  public StudentCourseMapping() {
  }

  public Integer getId() {
    return this.id;
  }

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

  public Student getStudent() {
    return this.student;
  }

  public void setStudent(Student student) {
    this.student = student;
  }

  public Course getCourse() {
    return this.course;
  }

  public void setCourse(Course course) {
    this.course = course;
  }

  public Integer getStudentId() {
    return studentId;
  }

  public void setStudentId(Integer studentId) {
    this.studentId = studentId;
  }

  public Integer getCourseId() {
    return courseId;
  }

  public void setCourseId(Integer courseId) {
    this.courseId = courseId;
  }

}


Step 5: Define repository interfaces.

 

CourseRepository.java

package com.sample.app.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.sample.app.entity.Course;

public interface CourseRepository extends JpaRepository<Course, Integer> {

}


StudentRepository.java

package com.sample.app.repository;

import org.springframework.data.jpa.repository.JpaRepository;

import com.sample.app.entity.Student;

public interface StudentRepository extends JpaRepository<Student, Integer> {

}


StudentCourseMappingRepository.java


package com.sample.app.repository;

import java.util.List;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import com.sample.app.entity.StudentCourseMapping;
import org.springframework.data.jpa.repository.Query;

public interface StudentCourseMappingRepository extends CrudRepository<StudentCourseMapping, Integer> {

  @Query(value = "select s.course_id from STUDENT_COURSE_MAPPING s WHERE s.student_id= :studentId", nativeQuery = true)
  List<Integer> findAllCourseIds(@Param("studentId") Integer studentId);
}


Step 6: Define service layer interfaces.

 

CourseService.java

package com.sample.app.service;

import com.sample.app.dto.CourseRequestDto;
import com.sample.app.dto.CourseResponseDto;

public interface CourseService {
  public CourseResponseDto createCourse(CourseRequestDto courseDto);
}


StudentService.java

package com.sample.app.service;

import java.util.Set;

import com.sample.app.dto.StudentAndCoursesRespDto;
import com.sample.app.dto.StudentRequestDto;
import com.sample.app.dto.StudentResponseDto;

public interface StudentService {
  
  public StudentResponseDto createStudent(StudentRequestDto studentDto);
  
  public void addCoursesToStudent(Integer studentId, Set<Integer> courseIds);

  public StudentAndCoursesRespDto getStudentDetails(Integer studentId);
}


Step 7: Define service layer implementation classes.

 

CourseServiceImpl.java

package com.sample.app.service.impl;

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

import com.sample.app.dto.CourseRequestDto;
import com.sample.app.dto.CourseResponseDto;
import com.sample.app.entity.Course;
import com.sample.app.repository.CourseRepository;
import com.sample.app.service.CourseService;

@Service
public class CourseServiceImpl implements CourseService {

  @Autowired
  private CourseRepository courseRepository;

  @Override
  @Transactional
  public CourseResponseDto createCourse(CourseRequestDto courseDto) {
    Course course = new Course();
    course.setCourseName(courseDto.getCourseName());
    course = courseRepository.save(course);

    CourseResponseDto dto = new CourseResponseDto();
    dto.setCourseName(course.getCourseName());
    dto.setId(course.getId());

    return dto;
  }

}


StudentServiceImpl.java

package com.sample.app.service.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

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

import com.sample.app.dto.CourseResponseDto;
import com.sample.app.dto.StudentAndCoursesRespDto;
import com.sample.app.dto.StudentRequestDto;
import com.sample.app.dto.StudentResponseDto;
import com.sample.app.entity.Course;
import com.sample.app.entity.Student;
import com.sample.app.entity.StudentCourseMapping;
import com.sample.app.repository.CourseRepository;
import com.sample.app.repository.StudentCourseMappingRepository;
import com.sample.app.repository.StudentRepository;
import com.sample.app.service.StudentService;

@Service
public class StudentServiceImpl implements StudentService {

  @Autowired
  private StudentRepository studentRepo;

  @Autowired
  private StudentCourseMappingRepository studentCourseMappingRepository;

  @Autowired
  private CourseRepository courseRepository;

  @Override
  @Transactional
  public StudentResponseDto createStudent(StudentRequestDto studentDto) {
    Student student = new Student();
    student.setFirstName(studentDto.getFirstName());
    student.setLastName(studentDto.getLastName());
    student = studentRepo.save(student);

    StudentResponseDto dto = new StudentResponseDto();
    dto.setId(student.getId());
    dto.setFirstName(student.getFirstName());
    dto.setLastName(student.getLastName());

    return dto;
  }

  @Override
  @Transactional
  public void addCoursesToStudent(Integer studentId, Set<Integer> courseIds) {
    List<StudentCourseMapping> mappings = new ArrayList<>();

    for (Integer courseId : courseIds) {
      StudentCourseMapping mapping = new StudentCourseMapping();

      mapping.setStudentId(studentId);
      mapping.setCourseId(courseId);

      mappings.add(mapping);
    }

    studentCourseMappingRepository.saveAll(mappings);
  }

  @Override
  public StudentAndCoursesRespDto getStudentDetails(Integer studentId) {
    Student student = studentRepo.findById(studentId).get();
    StudentAndCoursesRespDto respDto = new StudentAndCoursesRespDto();
    respDto.setFirstName(student.getFirstName());
    respDto.setLastName(student.getLastName());
    respDto.setId(student.getId());

    List<Integer> courseIds = studentCourseMappingRepository.findAllCourseIds(studentId);

    if (courseIds == null || courseIds.isEmpty()) {
      return respDto;
    }

    List<Course> courses = courseRepository.findAllById(courseIds);

    List<CourseResponseDto> coursesDto = new ArrayList<>();

    for (Course course : courses) {
      CourseResponseDto courseDto = new CourseResponseDto();
      courseDto.setId(course.getId());
      courseDto.setCourseName(course.getCourseName());

      coursesDto.add(courseDto);
    }

    respDto.setCourses(coursesDto);

    return respDto;
  }

}


Step 8: Define dto layer classes.

 

CourseRequestDto.java

package com.sample.app.dto;

public class CourseRequestDto {

  private String courseName;

  public String getCourseName() {
    return courseName;
  }

  public void setCourseName(String courseName) {
    this.courseName = courseName;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((courseName == null) ? 0 : courseName.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    CourseRequestDto other = (CourseRequestDto) obj;
    if (courseName == null) {
      if (other.courseName != null)
        return false;
    } else if (!courseName.equals(other.courseName))
      return false;
    return true;
  }

}


CourseResponseDto.java

package com.sample.app.dto;

public class CourseResponseDto {
  private int id;
  private String courseName;

  public int getId() {
    return id;
  }

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

  public String getCourseName() {
    return courseName;
  }

  public void setCourseName(String courseName) {
    this.courseName = courseName;
  }

}


StudentAndCoursesRespDto.java

package com.sample.app.dto;

import java.util.List;

public class StudentAndCoursesRespDto extends StudentResponseDto {
  private List<CourseResponseDto> courses;

  public List<CourseResponseDto> getCourses() {
    return courses;
  }

  public void setCourses(List<CourseResponseDto> courses) {
    this.courses = courses;
  }

}


StudentCourseMappingDto.java

package com.sample.app.dto;

import java.util.Set;

public class StudentCourseMappingDto {
  private Integer studentId;
  private Set<Integer> courseIds;

  public Integer getStudentId() {
    return studentId;
  }

  public void setStudentId(Integer studentId) {
    this.studentId = studentId;
  }

  public Set<Integer> getCourseIds() {
    return courseIds;
  }

  public void setCourseIds(Set<Integer> courseIds) {
    this.courseIds = courseIds;
  }

}


StudentRequestDto.java

package com.sample.app.dto;

public class StudentRequestDto {
  private String firstName;

  private String lastName;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((firstName == null) ? 0 : firstName.hashCode());
    result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    StudentRequestDto other = (StudentRequestDto) obj;
    if (firstName == null) {
      if (other.firstName != null)
        return false;
    } else if (!firstName.equals(other.firstName))
      return false;
    if (lastName == null) {
      if (other.lastName != null)
        return false;
    } else if (!lastName.equals(other.lastName))
      return false;
    return true;
  }

}


StudentResponseDto.java

package com.sample.app.dto;

public class StudentResponseDto {
  private String firstName;

  private String lastName;
  
  private int id;

  public String getFirstName() {
    return firstName;
  }

  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public void setLastName(String lastName) {
    this.lastName = lastName;
  }

  public int getId() {
    return id;
  }

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


Step 8: Define swagger configuration.

 

SwaggerConfig.java

package com.sample.app.config;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

@Component
@EnableAutoConfiguration
@EnableSwagger2
public class SwaggerConfig {

  @Bean
  public Docket userApi() {

    return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).select().paths(PathSelectors.any())
        .apis(RequestHandlerSelectors.basePackage("com.sample.app.controller")).build();
  }

  private ApiInfo apiInfo() {

    return new ApiInfoBuilder().title("Query builder").description("Query builder using spring specification")
        .version("2.0").build();
  }
}


Step 9: Define controller classes.

 

CourseController.java

package com.sample.app.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.sample.app.dto.CourseRequestDto;
import com.sample.app.dto.CourseResponseDto;
import com.sample.app.service.CourseService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@Api(tags = "Course Controller", value = "This section contains Course specific operations")
@RequestMapping(value = "/v1/courses")
public class CourseController {

  @Autowired
  private CourseService courseService;

  @ApiOperation(value = "Create new course", notes = "Create new course")
  @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<CourseResponseDto> saveCourse(@RequestBody CourseRequestDto courseDto) {

    CourseResponseDto course = courseService.createCourse(courseDto);

    return new ResponseEntity<>(course, HttpStatus.CREATED);
  }

}


StudentController.java

package com.sample.app.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.sample.app.dto.StudentAndCoursesRespDto;
import com.sample.app.dto.StudentCourseMappingDto;
import com.sample.app.dto.StudentRequestDto;
import com.sample.app.dto.StudentResponseDto;
import com.sample.app.service.StudentService;

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

@RestController
@Api(tags = "Student Controller", value = "This section contains student specific operations")
@RequestMapping(value = "/v1/students")
public class StudentController {

  @Autowired
  private StudentService studentService;

  @ApiOperation(value = "Add new student", notes = "Add new student")
  @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<StudentResponseDto> saveStudent(@RequestBody StudentRequestDto studentDto) {

    StudentResponseDto student = studentService.createStudent(studentDto);

    return new ResponseEntity<>(student, HttpStatus.CREATED);
  }

  @ApiOperation(value = "Add courses to the student", notes = "Add courses to the student")
  @PostMapping(value = "/add-course", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity addCoursesToStudent(@RequestBody StudentCourseMappingDto studentCourseMappingDto) {
    studentService.addCoursesToStudent(studentCourseMappingDto.getStudentId(),
        studentCourseMappingDto.getCourseIds());
    return new ResponseEntity<>(HttpStatus.CREATED);
  }

  @ApiOperation(value = "Get Student by Id", notes = "Get student by id")
  @GetMapping(value = "/{id}", produces = MediaType.APPLICATION_JSON_VALUE)
  public ResponseEntity<StudentAndCoursesRespDto> getStudentById(@PathVariable final Integer id) {
    StudentAndCoursesRespDto studentAndCoursesRespDto = studentService.getStudentDetails(id);
    return new ResponseEntity<>(studentAndCoursesRespDto, HttpStatus.OK);
  }
}


Step 10: Define main application class.

 

App.java

package com.sample.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class App {

  public static void main(String args[]) {
    SpringApplication.run(App.class, args);
  }

}


Total project structure looks like below.




Run App.java.

 

Open the url ‘http://localhost:8080/swagger-ui.html’ in browser and play with swagger API.

 

You can download complete working application from below link.

https://github.com/harikrishna553/springboot/tree/master/jpa/many-to-many-jointable-pk















 

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment