본문 바로가기

공부방/Spring

[JPA] PK가 아닌 필드를 참조하는 FK를 만들 때

결론: @JoinColumn(referencedColumnName = [참조하는 필드 이름])

오류 상황

공부 겸 진행중인 스프링 부트 프로젝트에서 생긴 문제이다. Solved라는 table에서 School의 필드를 참조하는 FK를 가져야 하는 상황이다. 각 Entity 코드는 다음과 같다.

  • School
@Entity
public class School implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name="school_id", nullable = false, unique = true)
    private Long schoolId;              // 학교 번호

    @OneToMany(mappedBy = "school")
    private Set<Solved> solvedSet = new HashSet<>();
    
    ...
    
}
  • Solved
@Entity
public class Solved {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "school_id")
    private School school;
    
    ...

}

처음에 생각으로는 @JoinColumn(name = "school_id")라고 입력했으니까 당연히 Hibernate가 school_id를 참조하지 않을까 생각했다. 책과 인터넷에 나오는 예제도 다 이런 식으로만 작성되어 있었고...
그런데 아니었다. Hibernate에서는 자꾸 school_id column이 아닌 id column을 참조하는 table을 만들었다. 테스트를 돌리면 table을 생성하는 과정에서 다음과 같은 쿼리를 확인할 수 있었다.

Hibernate: alter table solved add constraint FK3d1skgq7xw652uvo5mi5sbd26 foreign key (school_id) references school (id)

해결

결국 문제는 'Hibernate에게 어떻게 school_id를 참조하도록 강제하는가'였는데, 답은 간단했다. @JoinColumn에 referencedColumnName의 값으로 참조하길 원하는 필드 이름만 넣어주면 되는 것이다. 즉, Solved의 코드를 다음과 같이 수정하면 된다.

@Entity
public class Solved {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ManyToOne
    @JoinColumn(name = "school_id", referencedColumnName = "school_id")
    private School school;
    
    ...

}

테스트를 돌려봤더니 아주 잘 작동한다.

Hibernate: alter table solved add constraint FK3d1skgq7xw652uvo5mi5sbd26 foreign key (school_id) references school (school_id)

왜 오류가 발생했나?

공식문서를 찾아보니 referencedColumnName의 기본값은 Primary Key라고 한다.

Default (only applies if single join column is being used): The same name as the primary key column of the referenced table.

내가 따로 값을 설정해주지 않아서 자동으로 PK를 참조하게 된 것이다. PK외에 다른 필드를 참조해야할 경우에는 반드시! 필드 이름을 넘겨줘야겠다.

여담

여기서 주의해야할 점이 한 가지 있는데, Hibernate의 오류(?)인지 이 경우 School이 Serializable을 implements해야 에러 없이 잘 작동한다. Hibernate 프로젝트에 이슈로 올라와있다.

참고링크

JPA JoinColumns 사용시 주의 사항 - Yoo Young-mo님