본문 바로가기

Spring

JPA 양방향 매핑 Entity 한 번에 저장하기

Team과 Student를 1:N 관계로 하고,

Team에서 해당 팀에 속한 학생들의 리스트를 보고 싶었고

Student에서는 그 학생이 속한 팀에 접근하고 싶었다.

 

그래서 @OneToMany, @ManyToOne 양방향 매핑을 사용하였다.

 

기존 코드

// Team.java
public class Team {
    @OneToMany(mappedBy = "team")
    private List<Student> studentList;
}
// Student.java
public class Student {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "team_id")
    private Team team;
}

위는 관련되지 않은 것을 제외한 Entity의 코드이다.

 

teamRepository.save(team);
studentRepository.saveAll(studentList);

그리고 이렇게 팀과 학생을 각각 저장해주었다.

결과는 의도한 대로 나왔다.

Hibernate: insert into team (name, team_id) values (?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)

 

 

다만 이 부분에 대해서 따로따로 save()를 호출해서 저장하지 않고

연관되어 있는 것을 한 번에 저장할 수 있지 않을까 하는 생각이 들었다.

 

 

개선

public class Team {
    @OneToMany(mappedBy = "team", cascade = CascadeType.ALL)
    private List<Student> studentList;

    public void addStudentList(List<Student> studentList) {
        this.studentList = studentList;
    }
}

한 번에 저장하게 하기 위해서

먼저 영속성이 전이될 수 있도록 @OneToMany에 cascade를 추가했다.

cascade의 자세한 내용은 생략하고 우선 간단하게 ALL로 설정했다.

그리고 setter의 역할을 하는 메소드를 만들어서 studentList를 넣어주었다.

setter 사용을 지양하기 위해 이름은 addStudentList로 했다.

 

team.addStudentList(studentList);
teamRepository.save(team);

이렇게 team에 studentList를 넣고 team만 저장하여 save()를 한 번만 호출했다.

쿼리는 똑같이 날아간다.

Hibernate: insert into team (name, team_id) values (?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
Hibernate: insert into student (name, team_id, student_id) values (?, ?, ?)
{"teamId":1,"teamName":"team!","studentList":["kim","lee","choi","shin"]}

그리고 호출 결과는 위와 같다.

 

글을 작성하기 위해서 만든 프로젝트라 간단하게 만드려고 했다.

그래서 처음엔 DTO를 생략하고 Entity를 바로 넘겨줬었는데, 순환 참조 문제가 생겼었다.

Entity를 직접 주고받게 하지 않고 DTO로 주고받도록 수정해서 문제를 해결했다.

 

참고

https://data-make.tistory.com/730

https://hongchangsub.com/jpa-cascade-2/