배울 것: @ManyToOne,@OneToMany,@JoinColumn,연관관계의 주인,(mappedBy="필드이름"), 양방향 연관관계에서 양방향의 객체까지 고려
객체는 참조를 이용해 관계를 맺고 테이블은 외래키를 이용해 관계를 맺는다. 이때 참조는 단방향이지만 외래키는 join한 범위를 모두 알 수 있으므로 사실상 양방향이라 할 수 있다.(A join B 와 B join A 의 범위는 같다.)
아래 그림에서 Member.getTeam() 으로 멤버는 팀을 알지만 Team에서는 Member를 알 수 없으므로 단방향이다.
Jpa에서 위와 같은 Entity를 정의할 떄
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
@Column(name = "USERNAME")
private String username;
@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
}
이와 같은 방식으로 정의한다. 이떄 DB의 테이블에서 어떤 필드를 외래키로 할지 알아햐 하기에 @JoinColumn을 붙여준다.
--@JoinColumn을 생략하면 기본전략이 사용된다.
필드명_참조하는 테이블의 칼럼명이 자동으로 외래키로 사용된다.--
두 방법이 있다.
member.getTeam() 처럼 객체를 통해 조회하는 방법이다.
String jpql="select m from Member m join m.team t where" + "t.name=:teamName";
List<Member> resultList =em.createQuery(jpql,Member.class)
.setParameter("teamName","팀1");
.getResultList();
엔티티매니저.createQuery에 jpql을 넣어 조회하는 방법이다.
em.update() 같은 메소드는 없다. jpa는 영속상태의 엔티티가 수정된다면 트랜잭션이 commit될때 알아서 쿼리를 실행해 DB에서의 내용도 수정한다.
위 그림처럼 member에서 team을, team에서 members를 알 수 있는게 양방향의 연관관계이다. 하지만 table에서는 단방향일 떄와 변함없이 외래키만
연결되어 있으면 된다.
양방향 관계가 있는 두 테이블 중 외래키를 등록,수정,삭제 등의 관리를 할 수 있는 주체를 정해야 한다. 이것이 왜 필요할까?
예를 들어 Team테이블에서 Member를 관리해야 한다고 가정하자. Team에는 Member_ID가 존재하지 않으므로 물리적으로 다른 테이블의 필드를 관리해야 괴리가 생긴다. 때문에 외래키가 있는 곳의 테이블을 주인으로 선택해야 한다. 주인이 아니면 @OneToMany(mappedBy="team")과 같이 주인이 아님을 작성해야 한다.
--다만 객체에서는 연관관계의 주인이 아니더라도 참조 객체에 접근할 수 있으므로 객체에서는 양쪽의 데이터를 모두 update하는 것이 좋다.
ex)
member1.SetTeam("Team1") 을 한다고 해서 자동으로
Team1.getMemeber() 의 값에 반영되지는 않기에 Team1.getMember().add("Team1")을 해주어야 한다.
--또한 member의 팀을 Team1에서 Team2로 바꾼 경우, member 객체의 Team을 바꾸는 것은 물론 Team1의 List< Member >또한 바꿔야 한다. 이처럼 양방향의 연관관계는 정교,꼼꼼한 로직이 필요하다.
위에서 언급했듯 양방향 연관관계에서 서로를 어긋나지 않게 참조하게 위해
setTeam,addMember 같은 편의 메소드를 작성할 수 있는데, 이떄 무한루프에 빠지지 않도록 검사하는 것이 안전하다.
일대다 단방향의 경우 외래키가 자기 테이블에 없기에 연관관계를 반영하기 위한 쿼리를 한 번 더 수행해야 한다. 떄문에 가능하면 다대일 양방향 매핑을 이용하도록 하자.
일대일 양방향 매핑의 경우 외래키가 주테이블,대상 테이블 중 어디에나 위치할 수 있다. 다만 장단점이 있다.
@ManyToMany
@JoinTable(name,joinColumns,inverseJoinColumns),
관계형 DB는 다대다 관계를 표현할 수 없다. 때문에 중간에 연결 테이블을 두어 일대다와 다대일의 관계로 풀어낸다.
위 그림처럼 연결테이블에 ID만이 아닌 다른 관리필드까지 필요한 경우가 많다. 때문에 OneToMany,ManyToOne으로 연결된 위 그림처럼 연결테이블을 만든다. 식별자도 연결테이블의 ID를 따로 만들고 member와 product의 id는 외래키로만 사용하는 것이 더 단순하고 명확한 방법이다.