[JPA] 단방향 연관관계

홍건의·2022년 3월 25일
0

JPA 학습

목록 보기
4/11

Intro

방향(Direction)

다중성(Multiplicity)

연관관계 주인(Owner)

(1) 방향

기디비 때 ERD에서 일대다, 일대일 그 개념과 유사하지만 다른 점도 있다.
단지 여기서는 양쪽 테이블이 있을 때
ex)
참조관계가 오로지

회원-> 팀

아니면

팀 -> 회원

만의 경우라면 단방향이 되며,

회원->팀
팀->회원

이렇게 양쪽에서 모두 바라보고 있으면 양방향이 된다.

(2) 다중성

DB에서 매핑 카디널리티 일대다, 다대다하던 개념과 똑같다.

(3) 연관관계 주인

"객체를 양방향 연관관계로 만들면 연관관계의 주인을 정해야 한다."

단방향 연관관계

(1) 객체 관점

public class Member {
   Long id;
   Team team;
   String userName;
}

public class Team {
   Long id;
   String name;
}

이 있다고 가정하자.

Member 객체는 Member.team필드(멤버변수)로 Team객체와 연관관계를 맺는다.
이 코드에서 Member객체와 Team객체는 단방향 관계다. Member는 Team필드를 통해서 팀을 알 수 있지만, 거꾸로 Team에서는 Member를 알 수 없다.
예를 들어, member->team은 getTeam()으로 가능하지만 반대방향은 할 수 없다.

(2) 테이블 관점

create table MEMBER (
    MEMBER_ID int PRIMARY KEY 
    TEAM_ID int foreign key
    USERNAME text
 )

create table TEAM (
    TEAM_ID int PRIMARY KEY,
    NAME text
)

가 있다고 가정하자.

MEMBER 테이블은 TEAM_ID 외래키로 TEAM 테이블과 연관관계를 맺는다.
MEMBER 테이블의 TEMA_ID로 TEAM 테이블과 JOIN할 수 있고 반대로 TEAM에서도 MEMBER와 JOIN 할 수 있다.
즉, MEMBER테이블의 TEAM_ID 외래키만으로 MEMBER ⋈ TEAM도 가능하고,
TEAM ⋈ MEMBER도 가능하다.
따라서 양방향 관계다.

(3) 객체 연관관계와 테이블 연관관계의 가장 큰 차이

참조를 통한 연관관계는 언제나 단방향이다. 객체 간에 연관관계를 양방향으로 만들고 싶으면 반대쪽에도 필드를 추가해서 참조를 보관해야한다.

결국 연관관계를 하나 더 만들어야한다. 이렇게 양쪽에서 서로 참조하는 것을 양방향 연관관계라고 한다.
하지만 더 정확히 얘기하면 ★양방향 관계가 아니라 단방향 관계 2개★다.

즉, 객체는 참조로 연관관계를 맺고, 테이블은 외래키로 연관관계를 맺는다.

단방향 연관관계 매핑

@Entity 
public class Member {
  
	@Id
    @Column(name = "MEMBER_ID")
    private String id;
    
    private String username;
    
    @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 String id;
    
    private String name;
  • 객체 연관관계 : Member 객체의 Member.team 필드 사용
  • 테이블 연관관계 : Member 테이블의 MEMBER.TEAM_ID 외래키 칼럼을 사용

Member.team과 MEMBER.TEAM_ID를 매핑하는 것이 연관관계 매핑이다.

@ManyToOne
@JoinColumn(name = "TEAM_ID")
private Team team;
  • ManyToOne
    이름 그대로 다대일 관계를 의미. 연관관계를 매핑할 때 다중성을 나타내는 어노테이션은 필수로 사용해야 한다. 기본 FetchType은 Eager (xxToOne이므로).

  • JoinColumn(name = "TEAM_ID")
    조인 칼럼은 외래 키를 매핑할 때 사용한다. name 속성에는 매핑할 외래 키(테이블) 이름을 지정한다. 회원과 팀 테이블은 TEAM_ID로 연관관계를 맺으므로 이 값을 지정하면 된다. 이 어노테이션은 생략할 수 있다.

단방향 연관관계 사용

저장

public void testSave() {
	Team team1 = new Team("team1", "팀");
    em.persist(team1);
    
    Member member1 = new Member("member1", "회원1");
    member1.setTeam(team1);	// 연관관계 설정 (mem1 -> team1)
    em.persist(member1);
    
    Member member2 = new Member("member2", "회원2");
    member2.setTeam(team1);	// 연관관계 설정 (mem2 -> team1)
    em.persist(member2);
}

Member는 Team을 참조하고 저장했다. JPA는 참조한 팀의 식별자를 외래키로 사용해서 적절한 등록 쿼리를 생성한다.

조회

연관관계가 있는 Entity를 조회하는 방법은 크게 2가지다.

  • 객체 그래프 탐색 (연관관계를 통해 조회)
  • 객체지향 쿼리 사용(JPQL)

(1) 객체 그래프 탐색

Member member = em.find(Member.class, "member1");	// member엔티티를 찾고
Team team = member.getTeam();	// 객체 그래프 탐색 (member에서..)
sout(team.getName());

이처럼 객체를 통해 연관된 Entity를 조회하는 것을 객체 그래프 탐색이라고 한다.

(2) 객체지향 쿼리 사용
예를 들어서 member를 조회하고 싶은데 team1에만 소속된 member를 조회한다고 가정하자.
그러면 Team Entity를 검색조건으로 사용해야 한다. SQL은 연관된 테이블을 조인해서 검색 조건을 사용하면 된다.
JPQL도 조인을 지원한다.

prvate static vodi queryLogicJoin(EntityManager em) {
	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();
                            
                         
    for(Member member : resultList) {
    	sout(member.getUserName());

JPQL은 from Member m join m.team t 부분을 보면 회원이 팀과 관계를 가지고 있는 필드(m.team)을 통해서 Member와 Team을 조인했다. 그리고 where절을 보면 조인한 t.name을 검색조건으로 사용해서 팀1에 속한 회원만 검색했다. 다음 실행한 JPQL을 보자. 참고로 teamName과 같이 :로 시작하는 것은 파라미터로 바인딩 받는 문법이다.

위 JPQL문을 바탕으로 실행되는 SQL문은 아래와 같다.

SELECT M.* FROM MEMBER MEMBER
INNER JOIN
	TEAM TEAM ON MEMBER.TEAM_ID = TEAM1.ID
WHERE
	TEAM1_.NAME = '팀1'

수정

private static void updateRelation(EntityManager em) {
	
    Team team2 = new Team("team2", "팀2");
    em.persist(team2);
    
    Member member = em.find(Member.class, "member1");
    member.setTeam(team2);
}

실행되는 SQL은 다음과 같다.

UPDATE MEMBER
SET
	TEAM_ID = 'team2', ...
WHERE
	ID = 'member1'

수정은 em.update() 같은 메소드가 없다. 단순히 불러온 Entity의 값만 변경해두면 트랜잭션을 커밋할 때 Flush가 일어나면서 Dirty Checking 기능이 작동한다. 그리고 변경사항을 DB에 자동으로 반영한다. 추가로, 연관관계를 수정할 때도 마찬가지 참조하는 대상만 변경하면 나머지는 JPA가 자동으로 처리한다.

삭제

연관된 Entity를 삭제하려면 기존에 있던 연관관계를 먼저 제거하고 삭제해야 한다. 그렇지 않으면 외래키 제약조건으로 인해, DB에서 오류가 발생한다. 팀1에는 회원1과 회원2가 소속되어 있다. 이때 팀1을 삭제하려면 연관관계를 먼저 끊어야한다.

member1.setTeam(null);
member2.setTeam(null);
em.remove(team);
profile
Backend Developer

0개의 댓글

관련 채용 정보