아주 골치아픈 오류 발생...
nested exception is org.hibernate.id.IdentifierGenerationException: ids for this class must be manually assigned before calling save():
게시글 내용을 저장하려고 save()를 사용했는데 위와 같은 오류 메시지가 떴다.
postRepository.save(saveDto.toEntity());
필요한 부분만 뽑아오자면,
@Entity
@Getter
@Table(name = "tb_users")
public class User {
@Id
@Column(name = "user_id")
private String userId;
@Column(name = "user_password")
private String userPassword;
public User(String userId){
this.userId = userId;
}
}
@Entity
@Getter
@Table(name="tb_post")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "post_id")
private int postId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "seller_id", referencedColumnName = "user_id")
private User user;
@Builder
public Post(String postTitle, String postContent,
int price, String proposalYn, int regionId, int categoryId, String userId,
LocalDate createdAt, Date updatedAt, String status, String deletedYn,
int viewCnt, int likeCnt)
{
this.postTitle = postTitle;
this.postContent = postContent;
this.price = price;
this.proposalYn = proposalYn;
this.createdAt = Date.valueOf(createdAt);
this.updatedAt = updatedAt;
this.status = status;
this.deletedYn = deletedYn;
this.viewCnt = viewCnt;
this.likeCnt = likeCnt;
this.setRegion(regionId);
this.setCategory(categoryId);
this.setUser(userId);
}
}
builder 가 저리 길어도 되는 건지 모르겠음... 다른 블로그 보면 엄청 짧던데. 근데 저렇게 안 넣어주면 이상하게 default값이 아닌 null로 들어가서 불편하게 java에서 세팅을 해줘야 함. 지금 생각해보니 @Default 어노테이션을 사용 안 해서 그런 것 같기도 하다.
후보1) save() 함수를 호출하기 전에 id 값이 설정되어 있어야 하는데, User의 id값이 설정되어 있지 않기 때문에 오류가 났다.
-> id가 int가 아닌 String이기 때문에 @GeneratedValue를 사용하지 않고, 값을 User Entity에 넣어줬는데 이와 같은 오류가 났다. 다른 분들의 경우는 @GeneratedValue 를 작성해줌으로써 해결했다고 하는데, id가 String인 나에게는 해당되지 않는 듯하다. 또는 직접 식별자를 할당 해도 된다고 해서 User의 생성자를 통해서 userId를 세팅해줬다. 같은 방법으로 다른 것들은 다 잘 들어갔는데, userId만 값이 할당 되지 않음.
https://hangjastar.tistory.com/241
https://hoontinparis.tistory.com/121
후보2) @ManyToOne으로 Post와 User가 연관되어 있는 상태인데, CUD와 같이 데이터를 변경할 때는 @ManyToOne(cascade = CascadeType.ALL) cascade를 작성해줘야 한다고 한다. 나같은 경우는 ALL, DETACH 등 다 해봤는데 결국 안 됐다.
몇 시간동안 오류에 시달리다가 결국 PostRepository에 save() 메소드를 만들어 sql문을 직접 작성해서 저장해봤다. 하지만... userId를 제외하고 나머지 값은 제대로 저장되었다. 정말 무서웠다😱 지독한 오류에 걸린 느낌..?
@Query(value = "insert into tb_post(seller_id, post_title, post_content, created_at, updated_at, status, deleted_yn, price, price_proposal_yn, region_id, category_id, view_cnt, like_cnt) " +
"values (:#{#post.user.userId},:#{#post.postTitle}, :#{#post.postContent},:#{#post.createdAt},:#{#post.updatedAt}, :#{#post.status}, :#{#post.deletedYn}, :#{#post.price}, :#{#post.proposalYn}, :#{#post.region.regionId}, :#{#post.category.categoryId}, :#{#post.viewCnt}, :#{#post.likeCnt})",nativeQuery = true)
@Modifying
@Transactional
public int postSave(@Param(value = "post") Post post);
나는 이 오류의 근본적인 원인을 @ManyToOne 으로 둘을 연관 시켜놨기 때문에 발생한 것이라고 봤다. 따라서 그냥 그 근본이 되는 원인을 없애 버리기로 했다.
결국 IdentifierGenerationException 오류의 원인은 알겠지만, Entity를 유지하면서 이를 해결할 방법을 못 찾았다. 따라서 나는 @ManyToOne을 버리기로 했다.
@Entity
@Getter
@Table(name="tb_post")
public class Post {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "post_id")
private int postId;
@Column(name = "seller_id")
private String userId; // 수정 후
}
이렇게 한 뒤 연관된 코드를 다 수정해 주고, 다시 save()를 해보니 잘 됐다. 이게 맞나 싶다. 다음부터 id는 웬만하면 int나 Long으로 해서 @GeneratedValue를 사용해주는 것이 좋을 것 같다😇
12.04
유튜브 최범균 님의 JPA 강의를 보니 되도록이면 @Embeddable을 사용하는 것이 좋고, 연관 매핑은 신중히 사용해야 된다고 한다.
매핑을 통해 연관된 값을 바로바로 조회할 수 있다는 장점이 있지만, 그 장점만 생각하고 연관 매핑을 하는 것은 좋지 않다고 한다.