먼저 중요 패키지 버전을 보면 아래와 같다.
- springboot 2.1.5.RELEASE
- spring-data 2.1.5.RELEASE
- spring-core 5.1.7.RELEASE
- hibernate-core 5.3.10.Final
- mysql 5.7.21-log
entity는 아래와 같습니다.
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "T_NOTIFICATION")
public class Notification {
@EmbeddedId
private NotificationId id;
@CreatedDate
@Column(name = "CREATE_DT", nullable = false, updatable = false, columnDefinition = "datetime(3)")
private Instant createdDate;
@LastModifiedDate
@Column(name = "RECEIVER_RESPONSE_DT", nullable = true, updatable = false, columnDefinition = "datetime(3)")
private Instant receiverResponseDate;
@Column(name = "NOTI_REASON", nullable = false, length = 64)
private String notiReason;
@ColumnDefault("1")
@Column(name = "SEND_COUNT", nullable = false, columnDefinition = "tinyint")
private int sendCount;
@Column(name = "REQUEST_USER", nullable = false, length = 36)
private String requestUser;
@Column(name = "SEND_MESSAGE", nullable = false, columnDefinition = "text")
private String message;
@Builder.Default
@OneToMany(mappedBy = "notification", fetch = FetchType.EAGER, cascade = {CascadeType.ALL}, orphanRemoval = true)
private Collection<NotificationMessage> notificationMessages = new ArrayList<NotificationMessage>();
}
@Embeddable
@AllArgsConstructor
@NoArgsConstructor
@Data
public class NotificationId implements Serializable {
private static final long serialVersionUID = 1L;
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "NOTI_SEQ", unique = true, updatable = false, nullable = false)
private BigInteger NotiSeq;
@Column(name = "PROJECT_ID", nullable = false, length = 36)
private String projectId;
@Column(name = "RECEIVER_ID", nullable = false, length = 128)
private String receiverId;
@Column(name = "NOTI_GID", nullable = false, length = 128)
private String notiId;
}
@Data
@Builder
@AllArgsConstructor(access = AccessLevel.PROTECTED)
@NoArgsConstructor
@Entity
@EntityListeners(AuditingEntityListener.class)
@Table(name = "T_NOTIFICATION_MESSAGE")
public class NotificationMessage {
@Id
@Column(name = "MESSAGE_ID", nullable = false, length = 100)
private String messageId;
@ColumnDefault("SEND")
@Column(name = "STATE", nullable = false, length = 10)
private String state;
@CreatedDate
@Column(name = "SEND_DT", nullable = false, columnDefinition = "datetime(3)")
private Instant sendDate;
@Column(name = "END_DT", nullable = true, columnDefinition = "datetime(3)")
private Instant endDate;
@Column(name = "SEND_RESULT_CODE", nullable = true, columnDefinition = "smallint(5)")
private Integer resultCode;
@Column(name = "SEND_RESULT_REASON", nullable = true, length = 1024)
private String resultReason;
@Column(name = "SEND_RESULT_MESSAGE", nullable = true, columnDefinition = "text")
private String resultMessage;
@ManyToOne
@JoinColumns(value = {
@JoinColumn(name = "NOTI_SEQ", referencedColumnName = "NOTI_SEQ"),
@JoinColumn(name = "PROJECT_ID", referencedColumnName = "PROJECT_ID"),
@JoinColumn(name = "RECEIVER_ID", referencedColumnName = "RECEIVER_ID"),
@JoinColumn(name = "NOTI_GID", referencedColumnName = "NOTI_GID"),
}, foreignKey = @ForeignKey(name = "FK_T_NOTIFICATION_TO_T_NOTIFICATION_MESSAGE"))
private Notification notification;
}
Notification class에 EmbeddedId를 설정하여 복합키를 사용하고 있고 NotificationId class에 정의된 복합키중에 notiSeq는 @GeneratedValue로 설정하여 auto increment를 사용하려고 하였습니다. 실제 코드에서는
@Override
public Notification insertOrUpdatePushMessage(Message message) {
Notification notification = convertMessageToNotification(message);
return this.repository.saveAndFlush(notification);
}
Message를 받아서 Notification으로 변경해서 save()를 하죠. 그런데 문제는 save()하고 return된 Notification 객체에는 notiSeq가 Null이 들어오는 것이었습니다.
사실 hibernate의 batch를 사용하고 있어서 auto increment를 가져오는 타이밍이 persistant와 안 맞나 보다 하고 batch도 사용하지 않아 봤는데 실패!!! 그럼 flush를 안 해서 그런가??? 하고 위 코드처럼 saveAndFlush()로 바꿔도 실패!!! 멘붕 중에 아래 블로그를 찾았다.
(JPA - 3) 엔티티 매핑
앞에서 영속성 컨텍스트에 객체(엔티티)를 저장하는 것을 보았다.객체와 관계형 데이터베이스의 다른 개념을 어떻게 매핑하고 데이터가 반영되는지 알아보자. 객체(엔티티)를 영속성 컨테스트에 저장(persist) 후 트랜젝션이 커밋되는 시점에각 데이터베이스에 맞는 SQL을 생성하여 데이터베이스로 보내진다.객체를 어떻게 엔티티로 만들고 테이블과 매핑하는지 알아보
kihoonkim.github.io
두 방식 중 무엇을 사용하든 복합키에는 @GeneratedValue를 통해 기본키를 자동생성할 수 없다.
반드시 직접 할당 전략을 사용해야 된다.
오!!! 복합키는 안 되는 거구나.... 허무함과 안도감이 교차했다. 나중에 왜 그런지 hibernate문서를 좀 더 찾아봐야겠지만 일단 믿고 가기로 했다.
결론은 복합키에 unique한 auto increment를 사용하는 게 모순이라고 판단해서 auto increment만 PK로 변경해서 진행 중이다.
'Programming > JPA' 카테고리의 다른 글
N + 1 문제 해결 1 (0) | 2020.06.11 |
---|---|
N + 1 문제 원인 (0) | 2020.06.05 |
왜 JPA를 써야할까? (0) | 2020.06.05 |
JPA 기본 Annotation 정리 (7) | 2019.07.04 |
JPA 기본 키 전략 (0) | 2019.06.27 |