[Spring] @JoinColumn의 name과 referencedColumnName

Jihyoung·2023년 5월 22일
1

Spring

목록 보기
4/5
post-thumbnail

프로젝트 시에 엔티티 설계 도중 발생한 문제를 해결하게 된 부분에 대해 기록해 보려고 합니다.

문제 상황

하나의 엔티티에서 두 개의 엔티티를 매핑하는 과정에서 아래와 같은 에러가 발생했습니다.

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: 
[PersistenceUnit: default] Unable to build Hibernate SessionFactory; 
nested exception is org.hibernate.MappingException: 
Column 'id' is duplicated in mapping for entity 'com.codaily.bankingserver.userFriend.domain.UserFriend' (use '@Column(insertable=false, updatable=false)' 
when mapping multiple properties to the same column)

아래 코드는 처음 엔티티를 설계했을 때 인데 해당 코드를 살펴 보면 매핑 시에 @JoinColumn 을 사용하면서 name 옵션에 대한 이해 없이 설계했기 때문에 발생한 문제였습니다.
그랬기 때문에 Column 'id' is duplicated in mapping for entity 의 오류가 발생할 수 밖에 없었던 것이죠 😱

@Entity
@Getter
@NoArgsConstructor
public class UserFriend{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="id")
    private User userId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="id")
    private User friendId;
    
    @CreationTimestamp
    private Timestamp createdAt;
    
    @UpdateTimestamp
    private Timestamp updatedAt;

    @Builder
    public UserFriend(User userId, User friendId) {
        this.userId = userId;
        this.friendId = friendId;
    }
}

그래서 @JoinColumn 의 옵션에 대해 알아보고 이를 적절히 활용하여 수정해보았습니다.


@JoinColumn

먼저 @JoinColumn은 외래 키를 매핑할 때 사용합니다.
해당 어노테이션의 속성값들은 아래와 같습니다.

속성기능기본값
name매핑할 외래 키 컬럼명(이름)필드명_[참조하는 테이블의 기본 키 컬럼명]
referencedColumnName외래 키가 참조하는 대상 테이블의 컬럼명참조하는 테이블의 기본 키(PK) 컬럼명
foreignKey (DDL)외래 키 제약조건을 직접 지정할 때 사용하며, 테이블을 생성할 때만 사용한다.
unique
nullable
insertable
updatable
columnDefinition
table
@Column의 속성과 동일하다.

여기서 referencedColumnName 의 경우에는 referencedColumnName을 생략하면 대상 테이블의 PK로 자동 지정되기 때문입니다.


해결 과정

위의 내용을 토대로 아래와 같은 생각을 거쳐 수정해 보았습니다.

@ManyToOne 을 이용해서 User와 UserFriend가 연관 관계를 맺을 때 User에서의 PK는 id이고,
name 옵션으로 PK에 접근을 하게 되면 Column 'id' is duplicated in mapping for entity 에러가 발생하기 때문에 referencedColumnName 옵션을 줘서 참조하는 대상의 컬럼을 임의로 지정해 줌으로써 문제를 해결할 수 있었습니다.

	@ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName="id")
    private User user;
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(referencedColumnName="id")
    private User friend;

위와 같이 설정해주게 되면 default 값인 테이블 컬럼명(id)으로 user_id, friend_id가 각각 생성 됐습니다.

그러나 이와 같이 임의로 PK가 아닌 다른 컬럼에 직접적으로 지정해주는 것은 정규화 관점에서 권장되는 방법이 아니라고 합니다.


따라서 위의 과정에 착안하여 user_id, friend_id로 생성된 테이블 컬럼명으로 name 옵션을 적용해서 수정한 결과 올바른 연관 관계를 맺고, 문제를 해결할 수 있었습니다.

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="user_id")
    private User userId;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="friend_id")
    private User friendId;
    

Reference

profile
로그를 생활화

1개의 댓글

comment-user-thumbnail
2023년 5월 30일

Jpa 사용시 자주 겪을만한 문제네요 글 잘보고 갑니다😊

답글 달기