트러블 슈팅 220917

u-nij·2022년 9월 16일
0

트러블 슈팅

목록 보기
1/6
post-thumbnail
post-custom-banner

실행 환경

  • Spring Boot 2.7.3
  • Java 11.0.9

상황

  • User 엔티티에 List<String> 타입의 hashtags 객체를 만드려고 함
@Entity
@Getter
public class User {

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

    private List<String> hashtags = new ArrayList<>();

}

발생한 에러

Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.MappingException: Could not determine type for: java.util.List, at table: User({테이블명}), for columns: [org.hibernate.mapping.Column({칼럼명})]

왜?

  • 테이블에 컬렉션을 저장할 수 없어서 발생한 에러

해결 방법

  • 값 타입 컬렉션 생성
  • 실무에서는 상황에 따라 값 타입 컬렉션 대신에 일대다 관계를 고려하는 것이 좋다
    • 일대다 관계를 위한 엔티티를 만들고, 해당 엔티티에서 값 타입을 사용
    • 영속성 전이(Cascade) + 고아 객체 제거를 사용해서 값 타입 컬렉션 처럼 사용
@Entity
@Getter
public class User {

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

	@ElementCollection
	@CollectionTable(name = "Hashtags", joinColumns = @JoinColumn(name = "user_id"))
	@Column(name = "hashtag") // 컬럼명 지정
    private List<String> hashtags = new ArrayList<>();

}

실행 결과

참고 사항

  • String을 이용해 "hashtag1, hashtag2, …" 이런 식으로 저장하면 안되나🤔? 했지만,
    -> 인프런 커뮤니티에 올라온 질문 중 하나를 보게 되었는데, 해시태그를 이용한 검색 기능을 구현하게 된다면 최소 값 타입 컬렉션(또는 엔티티)은 사용해야 한다고 한다.

    값 타입 컬렉션 사용시 제약 사항 (관련 포스팅)

    • 값 타입은 엔티티와 다르게 식별자 개념이 없다.
    • 값은 변경하면 추적이 어렵다.
    • 값 타입 컬렉션에 변경 사항이 발생하면, 주인 엔티티와 연관된 모든 데이터를 삭제하고, 값 타입 컬렉션에 있는 현재 값을 모두 다시 저장해야 한다.
    • 값 타입 컬렉션을 매핑하는 테이블은 모든 컬럼을 묶어서 기본키를 구성해야 함: null 입력X, 중복 저장X (현재 상황에서는, ‘user_id+hashtag’가 기본키)

내 선택: 엔티티로 승격

  • 연관된 데이터들을 user_id로 삭제하는 로직을 구현하기 위해, 추적을 쉽게 할 필요성을 느낌
  • UserHashtagUser: 다대일 양방향 매핑 사용
    • 일대다 단방향 매핑은 일(1)이 연관관계의 주인이다.
    • 하지만, DB 일대다 관계는 다(N)에 외래키가 들어가야 한다.
    • 일대일 단방향은 엔티티가 관리하는 외래키가 다른 페이블에 있게 됨 => 다대일 양방향 매핑
  • User 엔티티에서 영속성 전이, 고아 객체 제거 사용해, 라이프사이클을 같게 함
@Entity
@Getter
public class User {

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

	@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<UserHashtag> userHashtags = new ArrayList<>();

}
@Entity
@Getter
public class UserHashtag {

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

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user; // FK

    private String hashtag;

}

실행 결과

profile
삶은 달걀이다
post-custom-banner

0개의 댓글