이번에 프로젝트를 하면서 File 테이블의 ID는 Long, Integer로 사용하는 것이 아닌 KeyManager라는 테이블을 참조해서 FI + (오늘의 날짜) + 인덱스 이렇게 문자열로 생성해서 넣어주게 되었다.
package NexaDs_Knu.practice.Data.Entity.Util;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Embeddable;
import java.io.Serializable;
import java.time.LocalDateTime;
@Embeddable
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyManagerId implements Serializable {
private String keyType;
private String date;
}
이렇게 ID는 문자열로 타입과, 문자열로 변환된 날짜를 넣어준 KeyManagerId를 사용하고
package NexaDs_Knu.practice.Data.Entity;
import NexaDs_Knu.practice.Data.Entity.Util.KeyManagerId;
import lombok.*;
import javax.persistence.*;
@Entity
@Table(name = "KeyManager")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class KeyManagerEntity {
@EmbeddedId
private KeyManagerId id;
private Long sequence;
}
이렇게 테이블을 작성하였다.
이렇게 되면 ID는 KeyType과 날짜가 되어 이 두가지가 중복된 데이터는 들어올 수 없게 되었다.
@Query("SELECT k FROM KeyManagerEntity k WHERE k.id.keyType = :keyType AND k.id.date = :date")
KeyManagerEntity findByKeyTypeAndDate(
@RequestParam("keyType") String keyType,
@RequestParam("date") String date);
)
이렇게 JPQL을 이용하여 KeyType과 Date에 맞는 KeyManagerEntity를 가져오고
public static String toPrimaryKeyId(String keyType){
String today = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
KeyManagerEntity keyManager = keyManagerRepository.findByKeyTypeAndDate(keyType, today);
Long sequence;
if(keyManager == null)
{
keyManager = new KeyManagerEntity(new KeyManagerId(keyType, today), 1L);
keyManagerRepository.save(keyManager);
sequence = 1L;
}
else
{
keyManager.setSequence(keyManager.getSequence() + 1);
sequence = keyManager.getSequence();
keyManagerRepository.save(keyManager);
}
return keyType + today + String.format("%05d", sequence);
}
이렇게 그에 따라 ID를 생성해주게 만들었다.
그리고 이 방법을 사용하던 도중에 에러가 발생했다.
자바스크립트에서 해당 방법을 사용하는 API를 반복문으로 실행하는 구문이 있었는데, 이 과정에서
이런 에러가 발생한 것이다.
현재 toPrimaryKeyId에서 동일한 ID를 생성해주고 있는 것이었다.
왜 이런 일이 생겼는지 생각해보면, 스프링의 쓰레드에서 동시에 메서드에 접근하고 +1된 값을 넣어주기 전에 같은 ID를 만들어 버린 이유일 것이다.
어찌보면 당연한 일이었다.
이 부분을 critical section으로 만들었어야 했는데, '어차피 자바스크립트 반복문으로 들어오니 대충 순서대로 들어오겠지' 라고 생각한 나의 잘못일 것이다.
해결 방법은 이 부분을 atomic하게 바꿔주는 어떤 방법이든 될 수 있겠지만, 나는 가장 간단한 @Synchronized 어노테이션을 달아주었다.
이렇게만 바꿔주었더니, 더 이상 에러가 발생하지 않았다.
스프링이 쓰레드 기반으로 동작된다는 것을 다시 한 번 느낄 수 있는 에러였다.
'현장실습' 카테고리의 다른 글
Mariadb와 Springboot를 Docker에 올리기 (0) | 2023.07.28 |
---|