카테고리 없음

JPA (4) - 연관 관계 매핑 (양방향,단방향)

now0204 2024. 1. 11. 12:47

 

 

> 기본 용어 방향 : 양방향, 단방향 (객체에만 존재 참조가 양쪽에 있는지 한쪽에만 있는지)

> 다중성 : 다대일,일대다,일대일,다대다 

> 연관관계의 주인 : 객체 양방향 연관관계에서 주인이 필요하다 (외래키 관리 -> 변경시 DB에 반영될 객체)

 

1. 연관관계가 필요한 이유 

 

엔티티를 테이블에 맞추어 설계시 아래와 같음

@Entity
public class Member{

	@Id @GeneratedValue
    private Long id;
    
    @Column(name="USERNAME")
    private String name;
    
    @Cloumn(name="TEAM_ID")
    private Long teamId;

}

 

- 참조 대신 외래키를 직접 저장해서 사용함

 

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

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

//기본 시퀀스 전략이라 다른테이블이지만 1씩증가함..! (동일 시퀀스 공유)
//다르게 하고 싶으면 시퀀스 생성해서 각각하면될듯


//조회
Member findMember = em.find(Member.class,member.getId());

Team findTeam = em.find(Team.class,team.getId());

//member가 소속된 팀을 찾고싶은데 연관관계 딱히 없어서 가지고 있는 식별자로 따로 조회해야함

 

> 객체를 테이블에 맞추면 협력관계 (그래프 탐색)를 만들 수 없다.

 

2. 단방향 연관관계

 

출처: JPA기본(김영한) - 인프런

 

@Entity
public class Member{

	@Id @GeneratedValue
    private Long id;
    
    @Column(name="USERNAME")
    private String name;
    
   	@ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team teamId;

}

 

- 여러 멤버가 한개의 팀에 소속됨을 표현 

- JoinColumn은 실제 테이블에 외래키 저장할 칼럼 설정하는 것 (TEAM_ID에 TEAM의 기본키 저장)

- 기본키도 저장하고, 연관관계도 맺고! 

 

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

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

Member findMember = em.find(Member.class, member.getId));

Team findTeam = findMember.getTeam();

//** 
//그냥 이런식으로 작성하면, 1차캐쉬에서 가져옴 따라서 쿼리 보고싶으면
//flush(); clear()날리자

 

 

3. 양방향 연관관계와 연관관계의 주인

 

- 테이블의 연관관계는 기본적으로 fk하나로 join을 통해 둘다 식별가능

- 객체는 참조를 통해 식별하므로, 양방향을 위해선 (사실은 단방향 두번) 참조를 알고 있어야한다.

출처: JPA기본(김영한) - 인프런

@Entity
public class Member{

	@Id @GeneratedValue
    private Long id;
    
    @Column(name="USERNAME")
    private String name;
    
   	@ManyToOne
    @JoinColumn(name = "TEAM_ID")
    private Team teamId;

}

@Entity 
public class Team{

	@Id @generatedValue
    private Long id;
    
    private String name;
    
    @OneToMany(mappedBy = "team")
    List<Member> members = new ArrayList<Member>();

}
Team findTeam = em.find(Team.class, team.getId());

//Member의 Team fk와 Team의 pk join해서 Team 생성할때 쫙 가져옴

 

3.1 연관관계의 주인 mappedBy

 

> fk를 관리 -> 영속성 컨텍스트에서 변화를 감지하고 DB에 반영하는 쪽 정하는 것을 의미

> fk는 무조건 Many쪽에 생성됨! (테이블이 원래 그러하다) 또한 외래 키 하나로 두 테이블의 연관관계를 관리 

> 주인으로 지정된 참조가 변화하는 것은 감지하고, DB에 반영

> 종속된 것이 변화하면 DB반영 안함 (어플리케이션 레벨에서만 반영됨)

 

* Team으로 예를 들면 Team을 DB에서 find할때 Member List를 가지고 오는데 어플리케이션에서 List의 값을 변경해도 

  DB에 반영안됨 그냥 읽기전용 

 

* 만약 이런 개념이 없다면 DB는 Team에 변화를 DB에 반영할지, Member에 변화를 반영할지 알 수 없음! 

  -> 만약 Member에 setTeam은 없고 Team에 add만 있다면? 이런 불일치를 막기 위해 한쪽에만 위임 

 

3.2 양방향 매핑 규칙

 

> 객체 중 하나를 연관관계의 주인으로 지정 (외래키 관리)

> 주인이 아닌쪽은 읽기만 가능 

 

> 그러면 양방향에서 어디를 주인으로 지정해야하는가? :

     - 테이블 기준으로 fk가 있는 쪽을 주인으로 두자! 

     - 테이블은 무조건 다쪽에 fk가 생성 따라서 위 상황이면 Member를 주인으로 두자 

     - Team을 주인으로 둘 수도 있음! -> 하지만, 어차피 이런식으로 맵핑해서 테이블 생성하더라도 항상 Many쪽에 fk생성됨 

     - 관계 파악이 쫌 힘들 수 있고, 또한 Team을 수정했는데 Member도 같이 수정되는 이상현상 겪을 수도 있다.                                                                                      

 

3.3 양방향 매핑 시 자주하는 실수 

 

- 주인 아닌 쪽에 업데이트 

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

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

//주인 아닌 곳에만 수정 -> DB반영안됨 차라리 반대만 해라..
team.getMembers().add(member);

em.persist(member);

 

-> 이런 실수를 방지하고, 객체설계를 고려했을 때 둘다 초기화해두자 

-> 엔티티에 연관관계 편의 메서드를 지정해서 사용하도록 하자 (이때 어느쪽이 가질 지 정해야함)

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

//사실은 team을 set하면, 기본 team에서 member제거하고, 해야하는데 이정만해도 충분할 수도 있다함! 
//어차피 update됨 team은 그냥 읽기만하고

-> 양방향 매핑시 무한 루프 매우 주의해야함 

 

ex) toString(),JSON생성 라이브러리 

 

toString -> Team.toString() -> members.toString-> member.toStirng()..반복

JSON문제는 컨트롤러에서 엔티티 반환하는 것을 금지!! toString()은 적절하게 알아서 잘 빼고쓰던지해라 

아마 한 쪽만 빼주면 무한루프 안할 것 같긴함 

 

4. 정리

 

- 일단 단방향 매핑으로도 연관관계는 충분하다 

- 양방향 매핑은 반대 조회(그래프 탐색)이 꼭 필요할 때만 동적으로 추가하자 (어차피 테이블에 영향 없음)

- JPQL에서 특히 역방향 탐색할 일이 많다. 

- 또한 외래키 관리 기준으로 잡아야함 비지니스상 우위를 주인으로 정하면 안됨