티스토리 뷰

 

JPA에서는 ORM(Object-Relation Mapping)을 사용합니다. SQL을 통해서 DB에 접근하는 것이 아니라, 객체와 DB의 테이블을 Mapping 하여 데이터를 다룹니다.

 

객체와 테이블을 Mapping 하기 때문에 연관관계라는 것이 등장하게 됩니다.

연관관계란 객체와 테이블의 외래키(Foreign Key)를 연결해주는 것을 의미합니다. 연관관계를 맺어줌으로써 객체를 통해 DB의 테이블에 접근할 수 있는 것이죠.


단방향 연관관계

단방향 연관관계란 이름에서도 알 수 있듯이, 한 개의 방향으로만 연관관계가 맺어져 있음을 의미합니다. 한쪽 방향으로만 연관관계가 맺어져 있다면 A -> B로 조회가 가능하지만, B -> A로의 조회는 불가능합니다.

Member 엔티티

회원은 하나의 팀에 소속할 수 있습니다. 즉 회원과 팀은 N:1 관계를 맺습니다. 따라서 Member를 통해 Team과의 관계를 맺어줄 때 @ManyToOne을 사용합니다. 

@Entity
public class Member {

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

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Team getTeam() {
        return team;
    }

    public void setTeam(Team team) {
        this.team = team;
    }

Team 엔티티

Team 엔티티는 Member 엔티티와 달리 Member에 접근하는 필드가 존재하지 않습니다. 이를 단방향 연관관계라고 합니다.

Member에서는 Team을 조회하는 필드가 존재하지만, Team에서 Member로는 조회하는 필드가 존재하지 않습니다.

@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Team 조회

Member 엔티티는 Team에 접근할 수 있는 필드가 존재합니다. 즉 Member -> Team 방향으로 단방향 연관관계를 맺고있다라고 할 수 있습니다.

@Entity
public class Main {
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

 

따라서 DB로부터 Member를 가져와서 getTeam()을 통해 Team 엔티티를 조회할 수 있습니다.

Member findMember = em.find(Member.class, 1L);
Team findTeam = findMember.getTeam();
System.out.println("findTeam.id = " + findTeam.getId());
System.out.println("findTeam.name = " + findTeam.getName());

양방향 연관관계

연관관계에서 제일 중요한 것은 단방향 연관관계입니다. 단방향 연관관계만 먼저 맺어주고 추후에 필요할 때 양방향 연관관계를 사용하면 됩니다. 양방향 연관관계는 단방향 연관관계 2개를 사용함을 의미합니다.

Member 엔티티

기존 단방향 연관관계에서 사용한 엔티티와 동일합니다.

@Entity
public class Member {

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

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Team getTeam() {
        return team;
    }

    public void setTeam(Team team) {
        this.team = team;
    }
}

Team 엔티티

Team -> Member를 조회하기 위해 members 필드가 추가되었습니다. members 필드를 통해 자신과 연관관계 상태로 맺어진 Member 엔티티를 모두 조회할 수 있습니다.

 

Member 엔티티와 달리 @OneToMany 애너테이션이 사용되었습니다. Member와 Team은 N:1 관계이기 때문에 Team 입장에서는 자신의 Team에 속한 모든 Member를 조회할 수 있어야 합니다. 여기서 mappedBy가 사용되었는데 아래에서 설명하겠습니다.

@Entity
public class Team {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "TEAM_ID")
    private Long id;

    private String name;

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

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List<Member> getMembers() {
        return members;
    }

    public void setMembers(List<Member> members) {
        this.members = members;
    }
}

 

양방향 연관관계를 통해 Member -> Team 조회, Team -> Member 조회 둘 다 가능하게 되었습니다. 이 때 연관관계 주인을 설정해주어야 합니다. 연관관계 주인이란 두 단방향 연관관계 중에서 어느 것을 통해 DB의 테이블을 수정할 지를 결정하는 것입니다. 연관관계 주인은 N:1 관계에서 N쪽에 해당하는 필드로 설정해야 합니다. Member가 N의 입장이기 때문에 여기를 연관관계로 설정해주어야 합니다. Member 엔티티에서 Team 엔티티에 접근하는 필드가 연관관계의 주인이 됩니다. 

 

Team 엔티티를 보면 mappedBy = "team"으로 연관관계 주인을 설정해주었습니다. (mappedBy = "연관관계 주인 필드명")

@Entity
public class Member{
    @ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team team;
}

@Entity
public class Team {
    @OneToMany(mappedBy = "team")
    private List<Member> members = new ArrayList<>();
}

조회

단방향 연관관계에서는 Member -> Team 조회만 가능했지만, 양방향 연관관계에서는 Team -> Member 또한 조회가 가능하게 됩니다.

Member findMember = em.find(Member.class, 1L);
Team findTeam = findMember.getTeam();
List<Member> findMembers = findTeam.getMembers();

System.out.println("findTeam.id = " + findTeam.getId());
System.out.println("findTeam.name = " + findTeam.getName());

for (Member mem : findMembers) {
    System.out.println("mem.getId() = " + mem.getId());
    System.out.println("mem.getId() = " + mem.getName());
    System.out.println("mem.getTeam() = " + mem.getTeam());
}

결론

양방향 연관관계를 사용하기 앞서, 단방향 연관관계부터 잘 맺어주어야 합니다. 추후에 양방향 조회에 필요한 필드만 추가해 주면 되기 때문에 단방향 연관관계가 더욱 중요합니다. 그리고 연관관계 주인을 외래키를 갖는 엔티티의 필드로 설정해 주는 것 또한 잊으면 안 됩니다.

Total
Today
Yesterday
최근에 올라온 글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30