다대일(N:1) - 양방향매핑시 주의점

PPakSSam·2022년 1월 7일
0
post-thumbnail

다대일(N:1) 목차

다대일(N:1) - 양방향 연관관계 목차


양방향 매핑시 주의할 점

1. 양방향 매핑 시 가장 많이 하는 실수

첫번째 코드

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setName("member1");

// 역방향(주인이 아닌 방향)만 연관관계 설정
team.getMembers().add(member);
em.persist(member);
  • Team의 members는 주인이 아니므로 TEAM_ID에 영향을 미치지 않는다.
  • 따라서 위의 코드는 member에 TEAM_ID가 NULL이다.

두번째 코드

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setName("member1");
team.getMembers().add(member);

// 연관관계의 주인에 값 설정
member.setTeam(team);
em.persist(member);
  • Member의 team이 주인이므로 TEAM_ID에 영향을 미친다.
  • 따라서 위의 코드처럼 해야 member의 TEAM_ID에 값이 들어간다.

2. 순수 객체 상태를 고려해서 항상 양쪽에 값을 설정

세번째 코드

Team team = new Team();
team.setName("teamA");
em.persist(team);

Member member = new Member();
member.setName("member1");
member.setTeam(team);
em.persist(member);

em.clear();
Member findMember = em.find(Member.class, member.getId());
  • 위의 코드처럼 작성하면 DB의 관점에서는 아무런 문제가 되지 않는다.
  • SELECT 쿼리를 날린 경우 findMember의 team의 members의 크기는 1일 것이다.
  • 그러나 순수 객체 상태(SELECT 쿼리를 날리지 X)에서는 team의 members에는 아무것도 들어있지 않다. -> 나중에 비즈니스 로직에서 문제발생 가능성이 있다.
  • 따라서 세번째코드보다는 두번째 코드처럼 team의 getMembers에도 값을 넣어줘야한다.

그리고 세번째코드보다 더 좋은 방법이 있는데 연관관계 편의 메소드를 만드는 것이다.

[Member]

@Entity
 public class Member { 
		 
	@Id @GeneratedValue
	private Long id;

	@Column(name = "USERNAME")
	private String name;

	private int age;

	@ManyToOne
	@JoinColumn(name = "TEAM_ID")
	private Team team

	// 연관관계 편의 메소드
	public Team setTeam(Team team) {
	    this.team = team;
 	    team.getMembers().add(this);
    	}
}

위의 코드처럼 연관관계 편의 메소드를 만들어 놓으면
member.setTeam(team);을 할 때 team의 members에도 값을 넣어주니
굳이 team.getMembers().add(member);의 코드를 작성할 필요가 없어진다.

3. 양방향 매핑시에 무한 루프를 조심

예시 : toString(), lombok, JSON 생성 라이브러리

[Member]

@Entity
public class Member {

    @Id @GeneratedValue
    private Long id;

    @Column(name="USERNAME")
    private String username;   

    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;

    @Override
    public String toString() {
        return "Member{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", team=" + team +
                '}';
    }

}

[Team]

@Entity
public class Team {

    @Id @GeneratedValue
    @Column(name="TEAM_ID")
    private Long id;

    @Column(name = "NAME")
    private String name;

    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();

    @Override
    public String toString() {
        return "Team{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", members=" + members +
                '}';
    }

}

이런 경우 Team이든 Member든 toString()을 호출하면 무한루프가 생긴다.
1. member의 toString() 호출
2. member의 toString()에서 team의 toString() 호출
3. team의 toString()에서 members를 호출 -> members는 member의 toString()을 호출
4. 이렇게 무한 반복

profile
성장에 대한 경험을 공유하고픈 자발적 경험주의자

0개의 댓글