[Spring JPA] 양방향 매핑 시 연관관계 메서드 설정

Titu·2022년 2월 11일
0

Spring

목록 보기
4/5

결론

양방향 매핑시 연관관계를 양쪽에서 맺어주지 않으면, 한 트랜잭션 내에서 제대로 조회가 되지 않을 수 있다. 연관관계 메서드를 설정하자.

상황

상황을 간략히 아래 예시로 표현해보았다.

1. 부모 트랜잭션 A, 자식 트랜잭션 B, 자식 트랜잭션 C가 있는 상황에서 실행 순서는 A -> B -> C 순서로 진행된다.
2. 자식 트랜잭션 B에서 생성한 객체를 자식 트랜잭션 C에서 사용하고자 한다.
3. 자식 트랜잭션 C에서 객체를 조회하는 과정에서 에러가 발생했다.

// 부모 트랜잭션 A
@Transactional
public void parentMethod() {		
	childOneMethod();
    childTwoMethod();
}

// 자식 트랜잭션 B
@Transactional	
public void childOneMethod() {		
	Member member = new Member("m1", "Daisy");
    Team team = new Team("t1", "Seoul");
    member.setTeam(team);
	...
}

// 자식 트랜잭션 C
@Transactional
public void childTwoMethod() {		
	Team team = repository.findByName("Seoul");
    team.getMembers();				// 에러 발생
	...
}
@Entity
public class Member {
 
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 연관관계의 주인 (외래키를 저장하는 곳)
    private Team team;
}

@Entity
public class Team {
 
    @Id
    @GeneratedValue
    private Long id;
    
    @OneToMany(mappedBy = "team") // Member.team 연관관계의 주인 
    List<Member> members = new ArrayList<Member>();
 
}

트러블 슈팅

  • Spring 기본 설정에 의해 자식 트랜잭션은 부모 트랜잭션을 그대로 이어받고, 따라서 commit 역시 부모 트랜잭션과 같은 시점에 이루어진다. 따라서, 자식 트랜잭션 B에서 생성된 객체는 아직 DB에 commit되지 않은 상태이다.
  • 따라서 Team의 members는 아직 연관 관계가 설정되지 않은 상태이고, 같은 트랜잭션 내에서 조회할 시에 null 에러가 날 수 밖에 없다.

예시

// Team에는 연관 관계 설정을 하지 않았다.
 Team team = new Team();
 team.setName("TeamA");
 em.persist(team);
 

 Member member = new Member();
 member.setName("member1");'
 // Member에서는 연관 관계 설정을 했다.
 member.setTeam(team);
 em.persist(member)
 
 Team findTeam = em.find(Team.class, team.getId());
 // 커밋하지 않았기 때문에 조회하면 null이 나온다.
 findTeam.getMembers();

해결방안

원칙적으로는 연관관계의 주인쪽인 Member에서만 연관관계를 맺어도, 양방향으로 사용이 가능하다. 하지만, 양쪽에서 연관관계를 설정해주지 않으면 이와 같이 한 트랜잭션 내에서 제대로 조회가 되지 않는 문제가 발생한다. 이러한 상황을 방지하기 위해서는 연관관계 메서드를 사용해 양방향으로 연관관계를 매핑해줘야 한다.

@Entity
public class Member {
 
    @Id
    @GeneratedValue
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 연관관계의 주인 (외래키를 저장하는 곳)
    private Team team;
	
    /* 연관관계 메서드 설정 */
    public void setTeam(Team team){
    	this.team=team;
        team.getMembers().add(this);
    }
}

Reference: https://jiwondev.tistory.com/227

profile
This is titu

0개의 댓글