JPA 5장 - 연관관계 매핑 기초

정민주·2024년 10월 10일

스프링 스터디

목록 보기
12/17

현재 김영한 <<자바 orm 표준 jpa 프로그래밍>> 책을 토대로 ppt를 만들며 학부연구생 일을 하고 있는데, ppt에 넣기엔 너무 자세하거나, 내가 더 추가적으로 공부 하고 싶은 것 들은 블로그에 정리해볼 예정이다.

일단 공통적으로 사용될 엔티티는 다음과 같다.

0. 예시 엔티티 연관관계

@Entity
@Data
public class Chap5_Jpa_Member {

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

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

    @ManyToOne
    @JoinColumn(name="team_id")
    private Chap5_Jpa_Team team;
}
@Entity
@Data
public class Chap5_Jpa_Team {
    @Id
    @Column(name = "TEAM_ID")
    private String id;

    @Column(name="TEAM_NAME")
    private String name;
}

1. @JoinColumn

해당 개념을 공부했을 때, 제일 헷갈리는 것이 각각의 위치와 name 이란 공통된 속성이 너무나 헷갈렸다.

해당 애노테이션의 속성으로는 다음과 같은 내용이 존재한다.

여기서 우리가 집중해야 할 부분은 바로 "name"과 "referencedColumnName"이란 속성이다.

  • name은 단지 매핑할 외래 키의 이름, 즉 단순히 "현재 테이블의" 컬럼명을 만들어주는 것 이다.

  • referencedColumnName는 해당 외래 키가, "대상 테이블의 어떤 컬럼을 참조하는지" 를 지정해주는 것 이다.

  • 따라서 name은 마음대로 지정해주어도 되지만, referencedColumnName는 마음대로 지정해주면 예외가 발생하게 된다!

예시를 보자.

1-1. name 속성

현재 주인인 Member 테이블을 다음과 같이 수정했다고 가정한다.

@Entity
@Data
public class Chap5_Jpa_Member {

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

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

    @ManyToOne
    @JoinColumn(name="team_id_test")
    private Chap5_Jpa_Team team;
}

Team 테이블을 변경되지 않는다.

이 상태로 애플리케이션을 실행하게 되면 다음과 같은 쿼리문이 생성된다.

너무 조그맣다. 핵심이 되는 부분은 다음과 같다.

Hibernate: create table chap5_jpa_member (member_id varchar(255) not null, member_username varchar(255), team_id_test varchar(255), primary key (member_id)) engine=InnoDB

현재 해당 쿼리문은 우리가 지정했던 team_id_test라는 컬럼을 해당 Member 테이블에 생성되었다는 것을 의미한다.

그렇다면 외래키 설정은 잘 되었는가?

Hibernate: alter table chap5_jpa_member add constraint FKpguxc2i8kmmqod3ht4mv3jk5y foreign key (team_id_test) references chap5_jpa_team (team_id)

위에서 생성해준 Team_id_test 컬럼이 Team 테이블의 team_id를 자동으로 담고 있는 것을 알 수 있다!

1-2. referencedColumnName 속성

이번에는 대상 테이블의 컬럼명을 지정해줘야 하는 해당 속성을 사용해보자!

위의 name 속성은 사용자가 아무렇게나 이름을 설정해줘도 괜찮았다.
해당 속성은 어떨까?

이렇게 설정 후 애플리케이션을 돌려보면

에러가 난다!!

Caused by: org.hibernate.MappingException: A '@JoinColumn' references a column named 'team_id_test' but the target entity 'example.chap_5.jpa.Chap5_Jpa_Team' has no property which maps to this column

해당 에러 메시지는 @JoinColumn 애노테이션이 참조하는 외래 키 컬럼 이름이 대상 엔티티(Chap5_Jpa_Team)에 매핑되는 속성이 없다는 것을 의미한다.

그래서 해당 referencedColumnName 속성을 다시 해당 Team 테이블 내의 id 컬럼명인 "team_id"로 수정해서 애플리케이션을 재작동시켜주면
에러 없이 잘 돌아간다.😊


2. mappedBy 속성

해당 속성은 name 속성과는 개념이 살짝 반대된다! @JoinColumn의 name 속성은 사용자가 주 엔티티에서 외래키를 갖고 있을 컬럼명을 직접 지정해주는 용도였다면,

이 mappedBy 속성은 대상 엔티티에서, 외래키를 갖고 있는 주 엔티티와 매핑된 객체의 필드명을 알려주는 용도이다!

말이 너무 길다... 그런데 이게 내가 이해한 정확한 표현이라고 생각한다.

한 번 테스트 해보자!

이번엔 Member 엔티티는 변경하지 않는다.

현재 mappedBy 속성은 Member 클래스 내 외래키를 담는 team 필드와 연결 되어있다고 한다. 해당 속성을 Member 엔티티 내 외래키를 담는 컬럼명으로 바꾸어보자!

애플리케이션을 돌린 결과는,,, 당연히 에러가 뜬다.

Caused by: org.hibernate.AnnotationException: Collection 'example.chap_5.jpa.Chap5_Jpa_Team.member' is 'mappedBy' a property named 'team_id' which does not exist in the target entity 'example.chap_5.jpa.Chap5_Jpa_Member'

jpa나 orm에서 의미하는 '프로퍼티'란 클래스 객체 내 필드를 뜻한다고 한다.

3. 엔티티 저장 시 영속 상태

해당 책을 읽던 와중 해당 문구를 발견했다.

Jpa에서 엔티티를 저장할 때 모든 엔티티는 영속 상태여야 한다.

이 말은 다음과 같다!

우린 예시 코드에서 Member 엔티티와 Team 엔티티가 연관관계가 있다고 필드에서 나타냈다.

즉 Team 엔티티 역시 영속 상태여야 Member 엔티티를 영속 상태로 만들 수 있단 의미이다!!!

3-1. 테스트

한 번 테스트 해볼까?

잘 된다. 😊

3-1. 엥 그런데?

해당 코드에서는 member 엔티티를 영속화 할 때 분명 team 엔티티는 비영속 상태이다. 그런데 코드는 잘 동작한다. 왜 그럴까?

왜냐면, 아직 DB로 커밋을 하기 전이기 때문이다!! 이건 6장에서 더 자세하게 나올 내용인데 이런 상태에서는 UPDATE 쿼리가 자동적으로 실시된다.

해당 내용은 6장에서 더 자세하게 다루겠다.

그래서 사실 책의 문구는 다음과 같이 수정하면 더 정확할 것 같다.

Jpa에서 엔티티를 저장하는 쿼리를 DB로 보낼 때, 모든 엔티티는 영속 상태여야 한다.


4. 정리

  • @JoinColumn은 엔티티 내의 컬럼명을, mappedBy 는 객체의 필드명을 본다고 생각하면 된다.

  • Jpa에서 엔티티를 저장하는 쿼리를 DB로 보낼 때, 모든 엔티티는 영속 상태여야 한다.


출처 : https://ttl-blog.tistory.com/126
출처 : 김영한 <<자바 orm 표준 jpa 프로그래밍>> 5장

0개의 댓글