Skip to main content

Many to Many mapping in Spring boot

In this blog, I will explain how to use many-to-many mapping in Spring boot Application

What you need?
  • JAVA
  • MySql
  • Eclipse IDE ( whatever you like IDE, I'm using Eclipse for this example)
  • Maven ( you can use the Gradle as well)

Initial Plan


spring boot application will be created using spring initializer web tool and imported as a maven project to the eclipse ide. Scenarios will be post and tags. one post have more than one tags and one tags have more than one posts. Two model classes will be created for Post and Tags. Finally, the command line runner interface will be used to run the application.


Below diagram is the database model diagram which we going to install using the spring boot application.




Let's Start to Code.

You need to configure the application.properties file for database connections. add the following content to the src/main/resources/application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/learning
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto = update
Change the database username and password according to your MySql configurations.


1. AuditModel Class

This class will automatically create create_at and update_at fields while persisting the entities.


package lk.hjsoft.springbootmanytomany.model;

import java.io.Serializable;
import java.util.Date;

import javax.persistence.Column;
import javax.persistence.EntityListeners;
import javax.persistence.MappedSuperclass;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties(value = { "createdAt", "updatedAt" }, allowGetters = true)
public abstract class AuditModel implements Serializable {

 @Temporal(TemporalType.TIMESTAMP)
 @Column(name = "createAt", nullable = false)
 @CreatedDate
 public Date createAt;

 @Temporal(TemporalType.TIMESTAMP)
 @Column(name = "updateAt", nullable = false)
 @LastModifiedDate
 public Date updateAt;

 public Date getCreateAt() {
  return createAt;
 }

 public void setCreateAt(Date createAt) {
  this.createAt = createAt;
 }

 public Date getUpdateAt() {
  return updateAt;
 }

 public void setUpdateAt(Date updateAt) {
  this.updateAt = updateAt;
 }
}
Auditing, JsonIgonreProperties. JsonIgnore annotations are discussed in this blog post

2. Post class


package lk.hjsoft.springbootmanytomany.model;

import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.Lob;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "POSTS")
public class Post extends AuditModel {

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

 @NotNull
 @Size(max = 200)
 @Column(name = "title")
 private String title;

 @NotNull
 @Size(max = 200)
 @Column(name = "description")
 private String description;

 @NotNull
 @Lob
 private String content;
 
 @ManyToMany(fetch = FetchType.LAZY, cascade = {
   CascadeType.PERSIST,
   CascadeType.MERGE
 })
 @JoinTable(name="POSTS_TAGS", joinColumns = {
   @JoinColumn(name="post_id")
 }, inverseJoinColumns = {
   @JoinColumn(name="tag_id")
 })
 private Set tags = new HashSet<>();

 public Set getTags() {
  return tags;
 }

 public void setTags(Set tags) {
  this.tags = tags;
 }

 public Long getId() {
  return id;
 }

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

 public String getTitle() {
  return title;
 }

 public void setTitle(String title) {
  this.title = title;
 }

 public String getDescription() {
  return description;
 }

 public void setDescription(String description) {
  this.description = description;
 }

 public String getContent() {
  return content;
 }

 public void setContent(String content) {
  this.content = content;
 }

}
3. Tag Class


package lk.hjsoft.springbootmanytomany.model;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

@Entity
@Table(name = "TAGS")
public class Tag implements Serializable {

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

 @NotNull
 @Size(max = 100)
 private String name;
 
 @ManyToMany( fetch = FetchType.LAZY, cascade = {
  CascadeType.PERSIST,
  CascadeType.MERGE
 }, mappedBy = "tags")
 private Set posts = new HashSet<>();

 public Set getPosts() {
  return posts;
 }

 public void setPosts(Set posts) {
  this.posts = posts;
 }

 public Long getId() {
  return id;
 }

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

 public String getName() {
  return name;
 }

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

}
Now Repository interfaces will be created for database operations.

1. PostRepository interface


package lk.hjsoft.springbootmanytomany.repository;

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

import lk.hjsoft.springbootmanytomany.model.Post;

@Repository
public interface PostRepository extends JpaRepository<Post, Long>{

}

2. TagRepository interface


package lk.hjsoft.springbootmanytomany.repository;

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

import lk.hjsoft.springbootmanytomany.model.Tag;

@Repository
public interface TagRepository extends JpaRepository<Tag, Long>{

}

Codes to test the application

Main-Class will be replaced with the following codes.


package lk.hjsoft.springbootmanytomany;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

import lk.hjsoft.springbootmanytomany.model.Post;
import lk.hjsoft.springbootmanytomany.model.Tag;
import lk.hjsoft.springbootmanytomany.repository.PostRepository;
import lk.hjsoft.springbootmanytomany.repository.TagRepository;

@SpringBootApplication
@EnableJpaAuditing
public class Application implements CommandLineRunner {
 
 @Autowired
 private PostRepository postRepo;
 
 @Autowired
 private TagRepository tagRepo;

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

 @Override
 public void run(String... args) throws Exception {
  
  Post p1 = new Post();
  p1.setTitle("first title");
  p1.setDescription("first description");
  p1.setContent("this is first content");
  
  Tag t1 = new Tag();
  t1.setName("spring");
  
  Tag t2 = new Tag();
  t2.setName("java");
  
  p1.getTags().add(t1);
  p1.getTags().add(t2);
  
  t1.getPosts().add(p1);
  t2.getPosts().add(p1);
  
  postRepo.save(p1);
 }

}
Running the application


mvn spring-boot:run
Check the database and data will be in the relevant tables.

You can download the source code from Github

Comments

Popular posts from this blog

Java Increment Operations: `n++` vs `n = n + 1` vs `n += 1`

In Java, incrementing a variable by one can be done in several ways: n++ , n = n + 1 , and n += 1 . While these expressions achieve the same end result, they differ slightly in syntax and use cases. Let's explore each one and discuss their performance. 1. n++ Post-Increment Operator : Increments the value of n by 1 after its current value has been used. Common Usage : Typically used in loops and other contexts where the current value needs to be used before incrementing. int n = 5; n++; // n is now 6 2. n = n + 1 Addition Assignment : Explicitly sets n to its current value plus 1. Readability : Straightforward and clear, though slightly more verbose. int n = 5; n = n + 1; // n is now 6 3. n += 1 Compound Assignment Operator : Equivalent to n = n + 1 , but more concise. Usage : Combines addition and assignment into one step. int n = 5; n += 1; // n is

Understanding C1 and C2 Compilers in Java

Understanding C1 and C2 Compilers in Java Understanding C1 and C2 Compilers in Java In Java, the Just-In-Time (JIT) compiler is a part of the Java Virtual Machine (JVM) that improves the performance of Java applications by compiling bytecode into native machine code at runtime. The JIT compiler includes two different compilers, known as the C1 and C2 compilers, each with distinct optimization strategies and purposes. C1 Compiler (Client Compiler) The C1 compiler, also known as the client compiler, is designed for fast startup times and lower memory consumption. It performs lighter and quicker optimizations, which makes it suitable for applications that require quick startup and responsiveness. Key characteristics of the C1 compiler include: Quick Compilation: Prioritizes fast compilation times over deep optimizations. Low Overhead: Consumes less memory and resources during compilation. Profile-Guided Optimization: Ca

When To Use Indexes In MySQL

When deciding when and how to create an index in your MySQL database, it's important to consider how the data is being used. Let's say you have a database of  students . We will create it like this: CREATE TABLE `students` ( `id` int ( 11 ) NOT NULL AUTO_INCREMENT , `first_name` varchar ( 255 ) DEFAULT NULL , `last_name` varchar ( 255 ) DEFAULT NULL , `class` varchar ( 255 ) DEFAULT NULL , PRIMARY KEY ( `id` ) ) ENGINE = InnoDB Indexes are best used on columns that are frequently used in where clauses, and in any kind of sorting, such as "order by". You should also pay attention to whether or not this information will change frequently, because it will slow down your updates and inserts. Since you wont frequently be adding students, you don't have to worry about the inserts Let's say that you will be looking up the students with a web interface and the end user will be typing in the students name to find them, since r