JPA one-to-many and many-to-one annotation example in Spring Data JPA

This blog is a continuation of  Spring Boot JPA - association mapping

Today we will see one-to-many and many-to-one relationship mapping in spring boot.

   

One-to-many association

In Database, a one-to-many relationship occurs when one row in a table is mapped to many rows in another table. For example, a student can score marks in different subjects.

Let's create a student and mark table like the below one where one student can have many marks.

spring boot jpa one to many example in curd example student and mark

The mark table contains the student_id as the foreign key to the student table 


One-To-Many annotation

The @OneToMany annotation defines the one-to-many relationship between two entities in Spring data.

In a bidirectional relationship, the parent entity contains the @OneToMany annotation and the child entity specifies the attributes mappedBy in the @OneToMany annotation.


Entity classes

we will update the Student class created in the previous blog and create a Mark entity class. 


import java.util.List;

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.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student {

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

   private String name;

   @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @JoinColumn(name = "parentid", referencedColumnName = "id")
   private Parent parent;

   @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
   @JoinColumn(name = "studentid", referencedColumnName = "id")
   private List < Mark > marks;
}



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

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Mark {

   @Id
   @Column(name = "id")
   @GeneratedValue(strategy = GenerationType.IDENTITY)
   private int mid;

   private int studentid;

   private int subjectid;

   private int mark;

}


JPA Repository

The repository classes handle database operations(like insert, update, delete) by extending the JpaRepository and it must be annotated with @Repository so that our spring boot application detects it during component scanning.


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

import com.example.relational.entity.Student;

@Repository
public interface StudentRepository extends JpaRepository < Student, Integer > {

}

import java.util.List;

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

import com.example.relational.entity.Mark;

public interface MarkRepository extends JpaRepository < Mark, Integer > {

}


Testing the Repository 

Let's write some JUnit to test our repository.


import static org.junit.jupiter.api.Assertions.*;

import java.util.List;
import java.util.Optional;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.relational.entity.Parent;
import com.example.relational.entity.Student;
import com.example.relational.repository.StudentRepository;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class StudentRepositoryTest {

  @Autowired
  StudentRepository studentRepository;

  @Test
  public void getMarksForStudent() {
    Student student = getStudent(2);
    System.out.println("Student =" + student);
    assertNotNull(student);
  }

  @Test
  public void sumOfStudentMarks() {
    Student student = getStudent(2);
    int sum = student.getMarks().stream().mapToInt(mark -> mark.getMark()).sum();
    assertNotNull(student);
    assertEquals(181, sum);
  }

  private Student getStudent(int id) {
    Optional < Student > studentOptional = studentRepository.findById(id);
    return studentOptional.orElseGet(null);
  }

}

we get the below logs on console when we run the above JUnit.


Hibernate: select student0_.id as id1_2_0_, student0_.name as name2_2_0_, student0_.parentid as parentid3_2_0_ from Student student0_ where student0_.id=?
Hibernate: select parent0_.id as id1_1_0_, parent0_.fatherName as fatherna2_1_0_, parent0_.motherName as motherna3_1_0_ from Parent parent0_ where parent0_.id=?
Hibernate: select marks0_.studentid as studenti3_0_0_, marks0_.id as id1_0_0_, marks0_.id as id1_0_1_, marks0_.mark as mark2_0_1_, marks0_.studentid as studenti3_0_1_, marks0_.subjectid as subjecti4_0_1_ from Mark marks0_ where marks0_.studentid=?
Student =Student(id=2, name=Mukesh, parent=Parent(pid=1, fatherName=Karthick, motherName=Divay), marks=[Mark(mid=2, studentid=2, subjectid=1, mark=45), Mark(mid=8, studentid=2, subjectid=3, mark=76), Mark(mid=12, studentid=2, subjectid=2, mark=60)])

import static org.junit.jupiter.api.Assertions.*;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.relational.entity.Mark;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MarkRepositoryTest {

  @Autowired
  MarkRepository markRepository;

  @Test
  public void getAllMArks() {
    List < Mark > marks = markRepository.findAll();
    System.out.println(marks);
    assertEquals(4, marks.size());
  }

  @Test
  public void insertMark() {
    Mark mark = Mark.builder().studentid(5).studentid(1).mark(60).build();
    Mark markInDB = markRepository.save(mark);
    System.out.println(markInDB);
    assertNotNull(markInDB);
  }
}

we get the below logs on console when we run the above JUnit.

spring boot jpa one to many hibernate logs example



Many-to-one association

The many-to-one relationship exists when many rows in a table are linked to one row in another table. 

In Spring Data, the @ManyToOne annotation is used to define the many-to-one relationship between entities.

For example, in a student grading system, each subject can have marks of all the students in the class where one subject has multiple student marks. This is a many-to-one relationship.

We have already created a mark table in the above one-to-many mapping, so let's create the subject table.

spring boot jpa many to one example annotation entity diagram

The mark table contains the subject_id (foreign key) pointing to the subject table primary key.

Entity class

Let's update the mark class we have created already as below


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.ManyToOne;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Mark {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int mid;

  private int studentid;

  private int mark;

  @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
  @JoinColumn(name = "subjectid", referencedColumnName = "id")
  private Subject subject;
}

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 lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Entity
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Subject {

  @Id
  @Column(name = "id")
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int sid;

  private String title;

}

Create Repositories

we have added a method to fetch mark by subject title in the mark repository and created subject repostiroy.


import java.util.List;

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

import com.example.relational.entity.Mark;

public interface MarkRepository extends JpaRepository < Mark, Integer > {

  public List < Mark > findBySubjectTitle(String subjectName);

}

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

import com.example.relational.entity.Subject;

@Repository
public interface SubjectRepository extends JpaRepository < Subject, Integer > {

}

Test Repository 

Let write some Junit to test our mark repository. 



import static org.junit.jupiter.api.Assertions.*;

import java.util.List;

import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.junit4.SpringRunner;

import com.example.relational.entity.Mark;
import com.example.relational.entity.Subject;

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class MarkRepositoryTest {

  @Autowired
  MarkRepository markRepository;

  @Autowired
  SubjectRepository subjectRepository;

  @Test
  public void getAllMArks() {
    List < Mark > marks = markRepository.findAll();
    System.out.println(marks);
    assertEquals(12, marks.size());
  }

  @Test
  public void insertMark() {
    Subject subject = subjectRepository.getById(1);

    Mark mark = Mark.builder().subject(subject).studentid(5).mark(60).build();
    Mark markInDB = markRepository.save(mark);

    System.out.println(markInDB);
    assertNotNull(markInDB);
  }

  @Test
  public void getMarksBySubjectTitle() {
    List < Mark > marks = markRepository.findBySubjectTitle("Java");
    System.out.println(marks);
    assertEquals(4, marks.size());
  }
}

we can see below when we run our getMarksBySubjectTitle test method.

spring boot jpa many to one example annotation console log


Post a Comment

Previous Post Next Post

Recent Posts

Facebook