The Spring Boot JPA Many to Many Relationship Mapping Example shows you the process of mapping an many-to-many relationship using Spring Data JPA and Spring Boot. A ManyToMany relationship in Java is where the source object has an attribute that stores a collection of target objects and (if) those target objects had the inverse relationship back to the source object it would also be a ManyToMany relationship.
Other interesting posts you may like
Let’s begin:
Project structure
Our classic mvn project in this Spring Boot JPA Many to Many Relationship Mapping Example.
Maven dependencies
Our Spring Boot JPA Many to Many Relationship Mapping Example will use JPA, MySQL, so that we must add these dependencies in the pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javabycode</groupId> <artifactId>spring-boot-jpa-many-to-many-mysql-example</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <name>Spring Boot JPA Many-To-Many Example</name> <description>Spring Boot JPA Many-To-Many Example</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <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.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> |
Many-To-Many Relationship
We are using a database named example_manytomany and two student and subject tables . The subject and student tables have a many-to-many relationship via a join table named student_subject.
Here is the sql script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
CREATE DATABASE IF NOT EXISTS `example_manytomany`; USE `example_manytomany`; -- -- Table structure for table `student` -- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8; -- -- Table structure for table `subject` -- DROP TABLE IF EXISTS `subject`; CREATE TABLE `subject` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8; -- -- Table structure for table `student_subject` -- DROP TABLE IF EXISTS `student_subject`; CREATE TABLE `student_subject` ( `student_id` int(10) unsigned NOT NULL, `subject_id` int(10) unsigned NOT NULL, PRIMARY KEY (`student_id`,`subject_id`), KEY `fk_studentsubject_subject_idx` (`subject_id`), CONSTRAINT `fk_studentsubject_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT `fk_studentsubject_subject` FOREIGN KEY (`subject_id`) REFERENCES `subject` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; |
Create JPA Entities
Entity class Student and Subject are simple POJO class. Here we are using class Student and Subject with JPA @Entity annotation to map them to a database tables (these tables were created in above step).
Student entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
package com.javabycode.jpa.model; import javax.persistence.*; import java.util.Set; @Entity public class Student{ private int id; private String name; private Set<Subject> subjects; public Student() { } public Student(String name) { this.name = name; } public Student(String name, Set<Subject> subjects){ this.name = name; this.subjects = subjects; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "student_subject", joinColumns = @JoinColumn(name = "student_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "subject_id", referencedColumnName = "id")) public Set<Subject> getSubjects() { return subjects; } public void setSubjects(Set<Subject> subjects) { this.subjects = subjects; } @Override public String toString() { String result = String.format( "Student [id=%d, name='%s']%n", id, name); if (subjects != null) { for(Subject subject : subjects) { result += String.format( "Subject[id=%d, name='%s']%n", subject.getId(), subject.getName()); } } return result; } } |
Subject entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package com.javabycode.jpa.model; import javax.persistence.*; import java.util.Set; @Entity public class Subject { private int id; private String name; private Set<Student> students; public Subject(){ } public Subject(String name){ this.name = name; } public Subject(String name, Set<Student> students){ this.name = name; this.students = students; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @ManyToMany(mappedBy = "subjects") public Set<Student> getStudents() { return students; } public void setStudents(Set<Student> students) { this.students = students; } } |
Dig deeper:
@Table annotation maps the entity with the table. If no table annotation is present, the JPA implementation will auto assign a table for the class. It means that the class name of the entity maps with the table.
@Id annotation marks the identifier property of the entity.
@Column application maps the entity’s field with the table’s column. If no column annotation is present, the JPA implementation will auto assign a column for the class. It means that the field name of the entity maps with the table’s column.
@ManyToMany annotation define a many-to-many relationship between 2 entities. All ManyToMany relationships require a JoinTable. The JoinTable is defined using the @JoinTable annotation. The JoinTable defines a foreign key to the source object’s primary key (joinColumns), and a foreign key to the target object’s primary key (inverseJoinColumns). Normally the primary key of the JoinTable is the combination of both foreign keys.
Spring Data JPA Repository
In this example, we all need to extend the JpaRepository. This is a built-in Repository implemented some common functions to work with database: findOne, findAll, save,etc.
StudentRepository
1 2 3 4 5 6 7 |
package com.javabycode.jpa.repository; import com.javabycode.jpa.model.Student; import org.springframework.data.jpa.repository.JpaRepository; public interface StudentRepository extends JpaRepository<Student, Integer>{ } |
SubjectRepository
1 2 3 4 5 6 7 |
package com.javabycode.jpa.repository; import com.javabycode.jpa.model.Subject; import org.springframework.data.jpa.repository.JpaRepository; public interface SubjectRepository extends JpaRepository<Subject, Long>{ } |
Properties Configuration
1 2 3 4 5 6 |
spring.datasource.url=jdbc:mysql://localhost/example_manytomany spring.datasource.username=javabycode spring.datasource.password=mypassword spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true |
Create SpringBootApplication class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
package com.javabycode.jpa; import com.javabycode.jpa.model.Student; import com.javabycode.jpa.model.Subject; import com.javabycode.jpa.repository.StudentRepository; import com.javabycode.jpa.repository.SubjectRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import javax.transaction.Transactional; import java.util.HashSet; @SpringBootApplication public class MyJpaApplication implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(MyJpaApplication.class); @Autowired private StudentRepository studentRepository; @Autowired private SubjectRepository subjectRepository; public static void main(String[] args) { SpringApplication.run(MyJpaApplication.class, args); } @Override @Transactional public void run(String... strings) throws Exception { // save a couple of students Subject subjectM = new Subject("Math"); Subject subjectJ = new Subject("Java programming"); Subject subjectC = new Subject("C programming"); studentRepository.save(new HashSet<Student>(){{ add(new Student("David Pham", new HashSet<Subject>(){{ add(subjectM); add(subjectJ); }})); add(new Student("Larry Page", new HashSet<Subject>(){{ add(subjectM); add(subjectC); }})); }}); // load all students for(Student student : studentRepository.findAll()) { logger.info(student.toString()); } // save a couple of subjects Student studentD = new Student("David Pham"); Student studentL = new Student("Larry Page"); subjectRepository.save(new HashSet<Subject>() {{ add(new Subject("Math", new HashSet<Student>() {{ add(studentD); add(studentL); }})); add(new Subject("Java programming", new HashSet<Student>() {{ add(studentD); add(studentL); }})); }}); // load all subjects for(Subject subject : subjectRepository.findAll()) { logger.info(subject.toString()); } } } |
DEMO
You can go to the project directory on the console screen and run the command line
1 |
mvn spring-boot:run |
Or create the unit test class, for example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.javabycode.jpa; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = MyJpaApplication.class) public class JpaApplicationTests { @Test public void contextLoads() { } } |
Run the above test class and see the output like below
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
Hibernate: insert into student (name) values (?) Hibernate: insert into subject (name) values (?) Hibernate: insert into subject (name) values (?) Hibernate: insert into student (name) values (?) Hibernate: insert into subject (name) values (?) 2017-04-22 15:18:03.717 INFO 6856 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select student0_.id as id1_0_, student0_.name as name2_0_ from student student0_ 2017-04-22 15:18:03.996 INFO 6856 --- [ main] com.javabycode.jpa.MyJpaApplication : Student [id=15, name='David Pham'] Subject[id=19, name='Java programming'] Subject[id=20, name='Math'] 2017-04-22 15:18:03.996 INFO 6856 --- [ main] com.javabycode.jpa.MyJpaApplication : Student [id=16, name='Larry Page'] Subject[id=21, name='C programming'] Subject[id=20, name='Math'] |
That’s all on the Spring Boot JPA Many to Many Relationship Mapping Example.
References
Many-to-Many Relationships
Spring Boot – Database initialization
Download complete source code, click link below
spring-boot-jpa-many-to-many-mysql-example-1.zip (465 downloads)