애정코딩 💻

WEB/JPA 2021.05.14 댓글 0 Joana

4. 자바 ORM 표준 JPA 프로그래밍 - 연관관계


목표

- 객체와 테이블 연관관계의 차이를 이해

- 객체의 참조와 테이블의 외래 키를 매핑


객체와 테이블 연관관계의 차이를 이해

객체를 테이블에 맞추어 모델링 했을 때 문제점 (참조 대신에 외래 키를 그대로 사용)

협력 관계를 만들 수 없다. 

테이블은 외래 키로 조인을 사용해서 연관된 테이블을 찾기 때문에 아래와 같이 번거로운 코드가 발생된다.

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

      Member member = new Member();
      member.setUsername("member1");
      // 외래키 식별자를 직접 다룬다.
      member.setTeamId(team.getId());
      em.persist(member);
      // 멤버의  팀을 찾고 싶을 때 번거로움
      Member findMember = em.find(Member.class,member.getId());
      Long findTeamId = findMember.getTeamId();
      Team findTeam = em.find(Team.class,findTeamId);

 

 

객체의 참조와 테이블의 외래 키를 매핑

단반향 연관관계

객체 지향 모델링 (객체 연관관계 사용)

아래와 같이 Member에서 Team을 바로 불러올 수 있다.

  @ManyToOne
  @JoinColumn(name = "TEAM_ID")
  private Team team;
      member.setTeam(team);
      em.persist(member);

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

      tx.commit();

 

양반향 연관관계와 연관관계의 주인

양쪽에서 참조해서 볼 수 있게 연관관계를 맺는것

테이블 관계는 변함이 없다 테이블은 외래 키로 조인을 사용해서 서로를 조회할 수 있다. (방향이 없다) 

Team 클래스에 아래와 같이 연관관계를 매핑해주었다. mappedBy 에서 사용한 team은 연관관계가 맺어있는 Member 클래스에서 @ManyToOne을 걸어준 변수의 이름을 넣어서 JPA에게 알려준다.

    @OneToMany(mappedBy = "team")
    private List<Member> memberList = new ArrayList<>();
	Member findMember = em.find(Member.class, member.getId());
	List<Member> memberList = findMember.getTeam().getMemberList();
	for(Member mem : memberList){
		System.out.println(mem.getUsername());
	}

위의 코드를 보면 findMember.getTeam 을 찾아 해당 Team에 MemberList를 조회한다. 이처럼 서로의 정보를 조회할 수 있는 것이 양반향 연관관계 이다.

 

연관관계와 주인과 mappedBy

객체의 양방향 관계는 서로 다른 단방향 관계 2개이다. -> 개체를 양방향으로 참조하려면 단뱡향 연관관계를 2개 만들어야 한다.

테이블은 외래 키 하나로 두 테이블의 연관관계를 관리한다. (키 하나로 양쪽 조인)

 

양방향 매핑 규칙

- 객체의 두 관계중 하나를 연관관계의 주인으로 지정

- 연관관계의 주인만이 외래 키를 관리( 등록, 수정 )

- 주인이 아닌쪽은 읽기만 가능 하고 mappedBy 속성을 사용하여 주인을 지정해야 한다.

 

그렇다면 누구를 주인으로 정해야 할까?

외래 키가 있는 곳을 주인으로 정해라!

Member - Team 이 있다면 다 인 Member를 주인으로 한다.

Team은 읽기만 가능한 가짜 매핑이다.

비지니스 로직을 기준으로 주인을 선택하면 안된다. [다] 쪽에 주인을 걸어주는게 좋다.

 

양방향 매핑시 가장 많이 하는 실수

-> 연관관계의 주인에 값을 입력하지 않음

 

예를들어 team.getMembers().add(member) 로 주인이 아닌 곳에만 값을 넣어주면

아래와 같이 Member테이블에 TEAM_ID 가 매핑되지 않는다.

 

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

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

	team.getMembers().add(member);

	// 연관관계의 주인에 값을 설정해주어야 한다.
	member.setTeam(team);

	em.persist(member);

 

하지만 순수 객체 관계를 고려하면 항상 양쪽다 값을 입력해야 한다.

 

아니면 연관관계 편의 메소드를 생성한다.

어디서 넣든 상관없다. 한쪽에서만 해주는게 좋다 양쪽에서 해주면 무한루프의 위험이 있다.

	private List<Member> members = new ArrayList<>();
    
	public void addMember(Member member){
	member.setSteam(this);
	member.add(member);
   	}

 

toString(), lombok, JSON 생성라이브러리 를 사용시 무한루프를 조심하자.

 

 

단반향 매핑만으로도 이미 연관관계 매핑은 완료된것이다.

처음 설계할 때 우선 단반향관계로 한다. 양방향은 반대 방향으로 조회 기능이 추가된 것 뿐이다.

JPQL에서 역방향으로 탐색할 일이 많아서 필요할 때 추가해도 된다. 테이블에 영향을 주지 않기 때문!

 

 

반응형