Spring/Spring Data JPA

양방향 연관관계와 연견관계의 주인 설정

밍구밍구밍 2024. 5. 6. 11:11

 

1. [테이블 연관관계]

- 테이블 연관관계에서는 FK (TEAM_ID) 하나로 TEAMMEMBER 의 서로 연관 관계를 모두 알 수 있다.

 

2. [양방향 객체 연관관계] = List<> 초기화

- Team 클래스에 List <members> 를 넣어줘야 서로 관계를 모두 알 수 있다

@Id @GeneratedValue
@Column(name = "TEAM_ID")
private Long id;

private String name;

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

(mappedBy = "team") : 해당 코드를 추가 하였다. 해당 코드의 의미는 Member 클래스의 team 으로 맵핑이 되어 있다는 뜻이다. 

 

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

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

( getter setter 를 추가 한다).

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

//    @Column(name = "TEAM_ID")
//    private Long teamId;

    // 외래키 설정
    @ManyToOne // 연관관계 매핑 시 @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 연관 관계 설정할 컬럼 조인 @JoinColumn (현재 Member 클래스의 TEAM_ID 는 PK 이다)
    private Team team;

 ( Member 코드를 살펴보면 private Team team <- 이 코드와 매핑)

 

try {

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

    Member member = new Member();
    member.setUsername("member1");
    member.setTeam(team);
    em.persist(member);

    em.flush();
    em.clear();

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

    List<Member> members = findmember.getTeam().getMembers();

    for (Member m : members) {
        System.out.println("m = " + m.getUsername());
    }

    tx.commit();
} 

(main 코드에서 getTeam() 으로 getMember 의 정보를 가지고 올 수 있는 것을 확인 할 수 있다.)

getTeam().getMembers() : Team 에서 Member 의 정보를 가지고 올 수 있다.

 

 

 

** 중간 정리 : 객체와 테이블이 관계를 맺는 차이 (*개념)

1) 객체 연관관계 = 2개 (단방향 + 단방향 = 양방향)

- 회원 -> 팀 (첫번쨰 연관관계 )

- 팀 -> 회원 (두번쨰 연관관계 )

>> 정리 : 객체 연관관계는 위의 단방향 연관관계가 두 가지가 있으며 이것을 양방향 연관관계라고 정의 할 수 있다.

               사실 양방향이라고 보기보다는 서로 다른 단방향 연관관계가 2개가 있는 것이다,

 

2) 테이블 연관관계 = 1개

MEMBER 입장에서 TEAM 테이블의 TEAM_ID 를 알고 싶다면 MEMBER FK(TEAM_ID) 를 통해 TEAM 테이블의 PK(TEAM_ID) 를 알 수 있다. 

 반대로, TEAM 테이블에서 MEMBER 테이블의 TEAM_ID 를 알고 싶다면 TEAM 테이블의 PK(TEAM_ID) 를 통해 MEMBER 테이블의 FK(TEAM_ID) 를 알 수 있다. 

 이처럼 테이블의 연관관계는 외래키를 설정 해놓으면 두 테이블간의 연관관계를 외래키 하나로 양쪽의 연관관계를

알 수 있는 것이다.

 

3. 연관관계의 주인

만약 등록 및 수정(변경) 시 반드시 Member 와 Team 둘 중 하나의 객체를 MEMBER 테이블과 연관관계를 위해 FK로 등록 해줘야 한다. 

 

** 양방향 매핑 규칙
  • 객체의 두 관계 중 하나를 연관관계의 주인(FK)으로 지정
  • 연관관계의 주인만이 외래키를 관리(등록, 수정)
  • 주인이 아닌쪽은 읽기만 가능 (수정 X)
  • 주인은 mappedBy 속성 사용 불가
  • 주인이 아니면 mappedBy 속성으로 주인을 지정
*** 누구를 주인으로?
  • 외래 키(FK) 가 있는 곳을 주인으로 정해야 된다.
  • 아래의 코드에서는 Member.team 이 연관관계의 주인
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "MEMBER_ID")
    private Long id;

    @Column(name = "USERNAME")
    private String username;

//    @Column(name = "TEAM_ID")
//    private Long teamId;

    // 외래키 설정
    @ManyToOne // 연관관계 매핑 시 @ManyToOne
    @JoinColumn(name = "TEAM_ID") // 연관 관계 설정할 컬럼 조인 @JoinColumn (현재 Member 클래스의 TEAM_ID 는 PK 이다)
    private Team team;

 

 

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

1) 연관관계의 주인에 값을 입력하지 않음

 

 

>> 정상적인 코드 (주인쪽에 데이터 값을 넣어준다.)

 

양방향 연관관계에서는 진짜 매핑(연관관계 주인) 과 가짜 매핑(주인 반대편) 둘 다 값을 설정 하는 것이 좋다

try {

    // 저장

    // 읽기 전용
    Team team = new Team();
    team.setName("TeamA");
    // team.getMembers().add(member); // team.
    em.persist(team);

    // 실제 등록&수정 하는 주인
    Member member = new Member();
    member.setUsername("member1");
    member.setTeam(team); // 진짜 매핑(주인)에 값 설정
    em.persist(member);

    team.getMembers().add(member); // 가짜 매핑에 값 설정

 

 

+ 아래 처럼 연관관계 편의 메서드를 사용하는 것도 방법이다.

public void setTeam(Team team) {
    this.team = team;
    team.getMembers().add(this); // 가짜 매핑 값 설정 기능 추가
}

(* 편의 메서드 : setTeam() 내부에 team.getMembers().add(this) 를 추가 하면 외부에서 setTeam() 진짜 매핑 메서드를 호출 할 때 가짜 매핑의 값도 동시에 실행 된다.)

 

try {

    // 저장

    // 읽기 전용
    Team team = new Team();
    team.setName("TeamA");
    em.persist(team);

    // 실제 등록&수정 하는 주인
    Member member = new Member();
    member.setUsername("member1");
    member.setTeam(team); // 진짜 매핑(주인)에 값 설정
    em.persist(member);

    // team.getMembers().add(member); 연관관계 편의 메서드로 인하여 해당 코드 생략 가능!

(* 메인 클래스에서 가짜 매핑 값 설정 코드 생략 가능)

참고로 이러한 양방향 관계 매핑을 설정 할때는 메서드 이름을 변경하여 특수한 메서드라는 것을 표시 하는 것이 좋다

ex) setName() -> changeName() 등