Kotlin SpringJPA Many-To-Many relationship

In the tutorial, JavaSampleApproach will show you the way to create an Kotlin Spring Many-To-Many relationship application with SpringBoot.

Related posts:
Spring JPA – Many to Many relationship
Kotlin SpringJPA Hibernate One-To-Many relationship
Kotlin SpringJPA One-to-One relationship
Kotlin Spring MVC RequestMapping RESTful APIs with @GetMapping, @PostMapping, @PutMapping, @DeleteMapping | SpringBoot Example

I. Technologies

– Java 1.8
– Maven 3.3.9
– Spring Tool Suite – Version 3.8.1.RELEASE
– Spring Boot: 1.5.6.RELEASE
– MySQL Database
– Kotlin language

II. Practice

In the tutorial, We create a Kotlin SpringBoot project that have 2 entities Student and Subject with Many-To-Many relationship as below:

kotlin springjpa many-to-many - relationship

Project Structure:

kotlin springjpa many-to-many - project structure

Step to do:
– Create Kotlin SpringBoot project
– Create Kotlin data models
– Create Spring JPA repositories
– Implement RestController
– Run and check results

1. Create Kotlin SpringBoot project

Using SpringToolSuite to create a Kotlin SpringBoot project.

>>> More step details, you can follow below tutorial:

Then add needed dependencies:


[...]

<properties>
	<kotlin.compiler.incremental>true</kotlin.compiler.incremental>
	<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	<java.version>1.8</java.version>
	<kotlin.version>1.1.4</kotlin.version>
</properties>

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

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

	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
		<scope>runtime</scope>
	</dependency>
	<dependency>
		<groupId>org.jetbrains.kotlin</groupId>
		<artifactId>kotlin-stdlib-jre8</artifactId>
		<version>${kotlin.version}</version>
	</dependency>
	<dependency>
		<groupId>org.jetbrains.kotlin</groupId>
		<artifactId>kotlin-reflect</artifactId>
		<version>${kotlin.version}</version>
	</dependency>

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

[...]

2. Create Kotlin data models

Create 2 Kotlin data model:

Student.kt:


package com.javasampleapproach.kotlin.manytomany.model

import javax.persistence.CascadeType
import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.FetchType
import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.JoinTable
import javax.persistence.ManyToMany
import javax.persistence.Table

@Entity @Table(name = "student")
data class Student(
	var name: String = "",
	
	@ManyToMany(cascade = arrayOf(CascadeType.ALL))
    @JoinTable(name = "student_subject",
					joinColumns = arrayOf(JoinColumn(name = "student_id", referencedColumnName = "id")),
					inverseJoinColumns = arrayOf(JoinColumn(name = "subject_id", referencedColumnName = "id")))
	var subjects: List = mutableListOf(),
			
	@Id
    @GeneratedValue(strategy = GenerationType.AUTO)
	var id: Long = -1
){
	override fun toString(): String{
		return "{student: ${this.name}, subjects: ${subjects.map { it->it.name }}}";
	}
}

Subject.kt


package com.javasampleapproach.kotlin.manytomany.model

import javax.persistence.Entity
import javax.persistence.GeneratedValue
import javax.persistence.GenerationType
import javax.persistence.FetchType
import javax.persistence.Id
import javax.persistence.ManyToMany
import javax.persistence.Table

@Entity @Table(name = "subject")
data class Subject(
	@Id
	var id: Long = -1,	
		
	var name: String = "",
	
	@ManyToMany(mappedBy = "subjects")
	var students: List = mutableListOf()
){
	override fun toString(): String{
		return "{subject: ${this.name}, students: ${students.map { it->it.name }}}";
	}
}

3. Create Spring JPA repositories

Using interface JpaRepository to create 2 repositories:

interface StudentRepository


package com.javasampleapproach.kotlin.manytomany.repo

import com.javasampleapproach.kotlin.manytomany.model.Student
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface StudentRepository : JpaRepository{
}

interface SubjectRepository


package com.javasampleapproach.kotlin.manytomany.repo

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import com.javasampleapproach.kotlin.manytomany.model.Subject

@Repository
interface SubjectRepository : JpaRepository{
}

@Entity: Specifies that the class is an entity. This annotation is applied to the entity class.
@Id: Specifies the primary key of an entity.
@ManyToMany: Defines a many-valued association with many-to-many multiplicity
@JoinTable: Used in the mapping of associations. It is specified on the owning side of an association.
JoinColumn: Specifies a column for joining an entity association or element collection. If the JoinColumn annotation itself is defaulted, a single join column is assumed and the default values apply.

Now open application.properties file, add configuration:


spring.datasource.url=jdbc:mysql://localhost:3306/testdb
spring.datasource.username=root
spring.datasource.password=12345
 
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

4. Implement RestController

Implement a Kotlin RestController RestAPIsController with 4 APIs:

– For delete all data from tables, we create an API /api/clear
– For saving entities, We implement an API /api/save
– For retrieve entities, We implement 2 APIs
-> /api/students: fetch all Student entities from database.
-> /api/subjects: fetch all Subject entities from database.


package com.javasampleapproach.kotlin.manytomany.controller

import com.javasampleapproach.kotlin.manytomany.model.Student
import com.javasampleapproach.kotlin.manytomany.model.Subject
import com.javasampleapproach.kotlin.manytomany.repo.StudentRepository
import com.javasampleapproach.kotlin.manytomany.repo.SubjectRepository
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
import javax.transaction.Transactional

@RestController
@RequestMapping("/api")
class RestAPIsController {
	@Autowired
	lateinit var studentRepository: StudentRepository
	
	@Autowired
	lateinit var subjectRepository: SubjectRepository

	@GetMapping("/clear")
	@Transactional
	fun clear(): String{
		// delete all data 
		subjectRepository.deleteAll()
		studentRepository.deleteAll()
		
		return "Done!"
	}

	@GetMapping("/save")
	fun save(): String {
		//prepare subjects 
		val math = Subject(1, "Mathematics")
		val computer = Subject(2, "Compter")
		val economics = Subject(3, "Economics")
		
		// attached subjects for each student
		val jack = Student("Jack", listOf(math, computer, economics))
		val peter = Student("Peter", listOf(computer, economics))
		
		// persist students to database
		studentRepository.save(listOf(jack, peter))
		
		return "Done!"
	}
	
	@GetMapping("/students")
	fun retrieveAllStudents(): String{
		// fetch all students from database
		val students = studentRepository.findAll()
		
		// some processing for better String format on browser showing 
		var info : String = ""
		students.forEach{
			info += it.toString() + "
" } return info } @GetMapping("/subjects") fun retrieveAllSubjects(): String{ // fetch all students from database val subjects = subjectRepository.findAll() // some processing for better String format on browser showing var info : String = "" subjects.forEach{ info += it.toString() + "
" } return info } }

5. Run and check results

Build and Run the Kotlin SpringBoot project by commanlines {mvn clean install, mvn spring-boot:run}.

– To make sure a clean database tables before working, we make a clear request: http://localhost:8080/api/clear

kotlin springjpa hibernate many-to-many - request - clear data

– Make a saving request: http://localhost:8080/api/save

kotlin springjpa hibernate many-to-many - request - save data

-> Check database’s results:

kotlin springjpa hibernate many-to-many - all database after saving

– Retrieve Student entities: http://localhost:8080/api/students
-> Result:

kotlin springjpa hibernate many-to-many - request - students

– Retrieve Subject entities: http://localhost:8080/api/subjects
-> Result:

kotlin springjpa hibernate many-to-many - request - subjects

– Hibernate’s Logs:


[...]

Hibernate: insert into student (name) values (?)
Hibernate: select subject0_.id as id1_2_0_, subject0_.name as name2_2_0_ from subject subject0_ where subject0_.id=?
Hibernate: select subject0_.id as id1_2_0_, subject0_.name as name2_2_0_ from subject subject0_ where subject0_.id=?
Hibernate: select subject0_.id as id1_2_0_, subject0_.name as name2_2_0_ from subject subject0_ where subject0_.id=?
Hibernate: insert into subject (name, id) values (?, ?)
Hibernate: insert into subject (name, id) values (?, ?)
Hibernate: insert into subject (name, id) values (?, ?)
Hibernate: insert into student (name) values (?)
Hibernate: insert into student_subject (student_id, subject_id) values (?, ?)
Hibernate: insert into student_subject (student_id, subject_id) values (?, ?)
Hibernate: insert into student_subject (student_id, subject_id) values (?, ?)
Hibernate: insert into student_subject (student_id, subject_id) values (?, ?)
Hibernate: insert into student_subject (student_id, subject_id) values (?, ?)
Hibernate: select subject0_.id as id1_2_, subject0_.name as name2_2_ from subject subject0_
Hibernate: select students0_.subject_id as subject_2_1_0_, students0_.student_id as student_1_1_0_, student1_.id as id1_0_1_, student1_.name as name2_0_1_ from student_subject students0_ inner join student student1_ on students0_.student_id=student1_.id where students0_.subject_id=?
Hibernate: select students0_.subject_id as subject_2_1_0_, students0_.student_id as student_1_1_0_, student1_.id as id1_0_1_, student1_.name as name2_0_1_ from student_subject students0_ inner join student student1_ on students0_.student_id=student1_.id where students0_.subject_id=?
Hibernate: select students0_.subject_id as subject_2_1_0_, students0_.student_id as student_1_1_0_, student1_.id as id1_0_1_, student1_.name as name2_0_1_ from student_subject students0_ inner join student student1_ on students0_.student_id=student1_.id where students0_.subject_id=?
Hibernate: select student0_.id as id1_0_, student0_.name as name2_0_ from student student0_
Hibernate: select subjects0_.student_id as student_1_1_0_, subjects0_.subject_id as subject_2_1_0_, subject1_.id as id1_2_1_, subject1_.name as name2_2_1_ from student_subject subjects0_ inner join subject subject1_ on subjects0_.subject_id=subject1_.id where subjects0_.student_id=?
Hibernate: select subjects0_.student_id as student_1_1_0_, subjects0_.subject_id as subject_2_1_0_, subject1_.id as id1_2_1_, subject1_.name as name2_2_1_ from student_subject subjects0_ inner join subject subject1_ on subjects0_.subject_id=subject1_.id where subjects0_.student_id=?

[...]

III. Sourcecode

KotlinSpringJPAManyToMany

6 thoughts on “Kotlin SpringJPA Many-To-Many relationship”

  1. I have similar task, and many to many relation between fields and subjects. How can i add first only subjects without attached subjects for each student?

  2. Thanks for the useful example. 😀

    I did have to change
    interface SubjectRepository : JpaRepository{
    to
    interface SubjectRepository : JpaRepository{
    Similarly for StudentRepository.

    Also had to change
    studentRepository.save(listOf(jack, peter))
    to
    studentRepository.save(jack)
    studentRepository.save(peter)
    in RestAPIsController.kt

    1. I should have said:

      I did have to change
      interface SubjectRepository : JpaRepository{
      to
      interface SubjectRepository : JpaRepository{
      Similarly for StudentRepository.

  3. I would like to thank you for the efforts you’ve put in penning this site.
    I really hope to check out the same high-grade blog posts from you in the future as well.
    In truth, your creative writing abilities has inspired me to get my very own website now 😉

Leave a Reply

Your email address will not be published. Required fields are marked *