JPA 외래 키 제약 조건_컬럼 이름 충돌과 참조 관계

Kim jisu·2025년 4월 15일
0

 Debugging Note

목록 보기
21/37

JPA 외래 키 제약 조건 오류와 그 해결 과정 정리.

문제 상황

대회 참가 신청 기능을 구현하는 중에 다음과 같은 외래 키 제약 조건 오류가 발생했습니다:

ERROR: insert or update on table "p_competition_participant_mapping" violates foreign key constraint "fk2fmijmnit5jt9n66umfraes5i"
Detail: Key (participant_id)=(d5e9c84c-77f1-4d52-b4f7-279be15d89af) is not present in table "p_competition_participant".

문제의 핵심은 p_competition_participant_mapping 테이블의 participant_id 컬럼이 p_competition_participant 테이블의 특정 컬럼을 참조하려고 하는데, 해당 값이 존재하지 않다는 것입니다.

엔티티 구조

문제를 이해하기 위해 관련 엔티티 클래스를 살펴보겠습니다:

Participant 엔티티

@Entity
@Table(name = "p_competition_participant")
public class Participant extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @Column(name = "participant_id", unique = true, nullable = false)
    private UUID participantId;

    @OneToMany(mappedBy = "participant", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<CompetitionParticipantMapping> competitionMappings = new ArrayList<>();

    // 생성자, 메서드 등...
}

CompetitionParticipantMapping 엔티티

@Entity
@Table(name = "p_competition_participant_mapping")
public class CompetitionParticipantMapping extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private UUID id;

    @ManyToOne
    @JoinColumn(name = "competition_id", nullable = false)
    private Competition competition;

    @ManyToOne
    @JoinColumn(name = "participant_id", referencedColumnName = "participant_id", nullable = false)
    private Participant participant;

    @Enumerated(EnumType.STRING)
    private Status status = Status.APPLY;

    // 생성자, 메서드 등...
}

문제 원인 분석

오류 메시지와 엔티티 코드를 분석해보니 몇 가지 문제점이 발견되었습니다:

  1. 컬럼 이름 충돌: Participant 엔티티에서 participant_id라는 컬럼 이름이 JPA의 관례적인 외래 키 명명법과 충돌하고 있었습니다. JPA에서는 보통 {테이블명}_{컬럼명} 형태로 외래 키 컬럼을 자동 생성하는데, 여기서는 수동으로 설정한 값과 충돌이 발생했습니다.

  2. 외래 키 참조 불일치: CompetitionParticipantMapping 엔티티에서 participant_id 컬럼이 Participant 엔티티의 participant_id 컬럼을 참조하도록 설정했지만, 데이터베이스 스키마에서는 id 컬럼을 참조하도록 설정되어 있었습니다.

  3. 트랜잭션 관리: 참가자 엔티티를 생성하고 즉시 매핑 테이블에 참조하는 과정에서 트랜잭션 관리가 제대로 이루어지지 않았습니다.

문제 해결 시도

1. 엔티티 필드 및 매핑 수정

첫 번째 시도는 CompetitionParticipantMapping 엔티티에서 중복된 participant_id 필드를 제거하고 매핑을 수정하는 것이었습니다:

@ManyToOne
@JoinColumn(name = "participant_id", nullable = false)
private Participant participant;

이 방식은 기본적으로 Participant 엔티티의 id 필드를 참조하게 됩니다.

2. 별도 필드 추가 및 insertable, updatable 설정

두 번째 시도는 매핑 엔티티에 별도의 필드를 추가하고 insertable=false, updatable=false 속성을 사용하는 것이었습니다:

@ManyToOne
@JoinColumn(name = "participant_id", insertable = false, updatable = false)
private Participant participant;

@Column(name = "participant_id")
private UUID participantId;

@Builder
public CompetitionParticipantMapping(Competition competition, Participant participant) {
    this.competition = competition;
    this.participant = participant;
    this.participantId = participant.getParticipantId(); // participant의 participantId 사용
}

하지만 이 접근법은 다음과 같은 오류를 발생시켰습니다:

Column 'participant_id' is duplicated in mapping for entity 'CompetitionParticipantMapping'

3. 컬럼 이름 변경으로 해결

최종적인 해결책은 컬럼 이름을 변경하여 충돌을 방지하는 것이었습니다:

// Participant 엔티티
@Column(name = "participant_user_id", unique = true, nullable = false)
private UUID participantId;

// CompetitionParticipantMapping 엔티티
@ManyToOne
@JoinColumn(name = "participant_id", referencedColumnName = "participant_user_id", nullable = false)
private Participant participant;

이렇게 수정하여 컬럼 이름의 충돌을 방지하고, referencedColumnName 속성을 사용하여 명시적으로 참조할 컬럼을 지정했습니다.

문제 해결의 핵심 포인트

1. 명확한 컬럼 이름 사용

JPA에서는 같은 이름의 컬럼이 다른 의미로 사용될 때 혼란이 발생할 수 있습니다. 특히 {테이블명}_id 형태의 컬럼 이름은 외래 키로 자동 매핑되는 경우가 많으므로 주의해야 합니다. 더 명확한 이름(예: participant_user_id)을 사용하면 이런 혼란을 방지할 수 있습니다.

2. referencedColumnName 속성 활용

@JoinColumnreferencedColumnName 속성을 사용하면 외래 키가 참조하는 컬럼을 명시적으로 지정할 수 있습니다. 기본값은 참조 엔티티의 기본 키(@Id)이지만, 다른 컬럼을 참조해야 할 경우 이 속성을 활용할 수 있습니다.

@JoinColumn(name = "participant_id", referencedColumnName = "participant_user_id", nullable = false)

결론

JPA에서 외래 키 제약 조건 문제는 컬럼 이름 충돌, 참조 관계 불일치, 트랜잭션 관리 등 다양한 원인으로 발생할 수 있습니다. 이러한 문제를 해결하기 위해서는:

  1. 명확하고 충돌 없는 컬럼 이름을 사용합니다.
  2. referencedColumnName 속성을 활용하여 참조 관계를 명시적으로 설정합니다.

JPA는 강력한 ORM 프레임워크이지만, 복잡한 관계 매핑에서는 세심한 주의가 필요합니다. 특히 컬럼 이름과 참조 관계 설정에 있어서 명확성과 일관성을 유지하는 것이 중요합니다.

profile
Dreamer

0개의 댓글