본문 바로가기

Spring/JPA

자바 ORM 표준 JPA 프로그래밍 #4(연관관계)

들어가며

엔티티들은 대부분 다른 엔티티와 연관 관계가 있다. JAVA에서는 연관관계를 참조를 이용해서 맺고, 데이터베이스는 외래키를 이용해서 맺는다. 따라서 JPA를 이용해 연관관계를 맺기 위해서는 객체의 참조와 테이블의 외래키를 매핑해야 한다.

연관관계 핵심 키워드

  • 방향
    단방향, 양방향이 잇다. 이는 외래키 개념이 없는 객체 관계에서만 존재하는데 간단히 객체 A가 B를 참조하지만 B는 A를 참조하지 않는 것이 단방향 둘 다 참조하는 것이 양방향 관계이다.
  • 다중성
    1:N, N:1, N:M의 관계가 있다. 예를 들어 회원과 팀은 1:N, 팀과 회원은 N:1, 상품과 주문의 관계는 N:M이다.(한번에 여러 상품을 주문할 수 있고, 상품 또한 서로 다른 주문에 포함 될 수 있으므로)
  • 연관관계 주인
    객체를 양방향 연관관계를 만들기 위해서는 연관관계의 주인이 필요하다.
    예를 들어 팀과 회원의 관계가 있다. 데이터베이스에서는 N쪽에 해당하는 테이블이 외래키를 갖는다. 하지만 객체간 서로 참조할 경우 N쪽 객체의 id를 외래키를 사용할지 1쪽 객체의 id를 외래키로 사용할지 정해줘야 한다. 이 때 N쪽에 해당하는 객체가 항상 외래키의 주인이다.

단방향 연관관계

설명보다는 코드로 보는 것이 이해가 쉬우므로 코드를 통해 보자

@Entity
public class Member {
	@Id
    @Column(name = "member_id")
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
    public void setTeam(Team team) {
    	this.team = team;
    }
}

@Entity
public class Team {
	@Id
    @Column(name = "team_id")
    private Long id;
}

위의 코드를 보면 알 수 있듯 Team 객체는 어떤 Member 객체가 자신의 팀인지 알 수 없다. 하지만 Member 객체는 자신의 팀이 누구인지 알 수 있다. 이렇게 한 쪽만 연관 객체를 아는 것을 단방향 연관관계라고 한다.

  • @ManyToOne
    다대일이라는 매핑 정보다. 회원 입장에서 보면 팀은 N쪽 이기에 다대일 관계이다.
  • @JoinColumn
    외래 키를 매핑할 때 사용한다. 여기서 회원쪽이 N이므로 team_id를 외래키로 갖는다.

양방향 연관관계

@Entity
public class Member {
	@Id
    @Column(name = "member_id")
    private Long id;
    
    @ManyToOne
    @JoinColumn(name = "team_id")
    private Team team;
    
    public void setTeam(Team team) {
    	if (this.team != null) {
        	this.team.getMembers().remove(this);
        }
    	this.team = team;
        team.getMembers().add(this);
    }
}

@Entity
@Getter
public class Team {
	@Id
    @Column(name = "team_id")
    private Long id;
    
    @OneToMany(mappedBy = "team")
   	private List<Member> members = new ArrayList<>();
}
  • mappedBy 속성으로 연관관계의 주인을 설정한다. (주인은 mappedBy 속성이 없다.)
  • 객체간 연관관계를 맺을 때 team.getMembers()가 가능하도록 논리적으로 연결지어줘야 한다.
    이때 회원의 팀이 결정되는 순간 Team 객체 안에 Member 객체를 넣을 수 있도록 setTeam 메소드 내에 로직을 넣도록 하자.
  • 또한 팀을 새롭게 설정할 경우 다른 팀에서 같은 회원을 가질 수 없도록 기존 관계를 제거야해야 한다,

추가적으로 다대다 연관관계는 한계가 있다. 예를 들어 회원이 상품을 주문하면 연결 테이블에 단순히 주문한 회원 아이디만 담고 끝나지 않는다. 보통은 연결 테이블에 주문 수량 컬럼이나 주문 날짜 같은 컬럼이 더 필요하다. 이런 상황에서 주문 테이블이나 상품 테이블에 컬럼을 추가할 수 없다.
따라서 중간에 테이블 하나를 더 두어 일대다 다대일 관계로 풀어내야한다.