현재 프로젝트에서 PostConstruct를 이용해서 Spring 이 실행될때 Member 권한을 자동으로 DB에 등록해주는 기능을 개발하고 있었다.
@Component
@RequiredArgsConstructor
public class MemberRoleInitializer {
private final MemberRoleRepository memberRoleRepository;
@PostConstruct
public void init() {
for (MemberRoleEnum memberRoleEnum : MemberRoleEnum.values()) {
memberRoleRepository.findByName(memberRoleEnum).orElseGet(
() -> {
MemberRole role = MemberRole.builder()
.name(memberRoleEnum)
.description(memberRoleEnum.getDescription())
.build();
return memberRoleRepository.save(role);
}
);
}
}
}
memberRoleEnum 목록을 전부 가져오고 Repository에 있는지 없는지 확인하고 없으면 그때 role을 등록하는 것이다.
권한이 많이 바뀌거나 추가될 일이 없어서, 약간 불 필요해보이는 로직일 수 있으나.. 개발단계여서 하나하나 DB에 등록하기 귀찮아서 만듬
근데 오류가 뜬다.
Caused by: org.springframework.dao.DataIntegrityViolationException: could not execute statement [ERROR: new row for relation "member_role" violates check constraint "member_role_name_check"
Detail: Failing row contains (1, ROLE_CREATE_MEMBER, 브랜드 내 멤버 생성 권한).] [insert into member_role (description,name) values (?,?)]; SQL [insert into member_role (description,name) values (?,?)]; constraint [member_role_name_check]
constraint "member_role_name_check" 에 맞지 않는다는데, 나는 constraint를 만든 적이 없다.
@Entity
@Getter
@Builder
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberRole {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 50)
@Enumerated(value = EnumType.STRING)
private MemberRoleEnum name;
@Column(columnDefinition = "TEXT")
private String description;
}
참고로 MemberRole 스펙이다.
왜일까?
Jpa Entity 에서 Enum 타입을 사용할 때 Enum 에 맞지 않는 값이 들어가는 것을 방지하기 위해 constraint 를 자동으로 삭제 및 생성한다.
하지만 PostConstruct 는 constraint 가 삭제 및 생성되기 전에 작동하는 듯하다.
그러니까
다음과 같은 기존 제약 조건이 있다.
- ROLE_TEST_1
1. MemberRoleEnum 에 ROLE_TEST_2 추가
2. MemberRoleEnum 을 DB Insert
3. 기존 제약 조건 삭제 및 재등록
- ROLE_TEST_1
- ROLE_TEST_2
이 순서로 작동하기 때문에 2번에서 막히는 것이다.
기존 제약 조건에 따라서 ROLE_TEST_1 만 등록이 가능한데 _2 를 등록하려고 하니까 막히는 것.
해결 방법
JdbcTemplate 을 사용하여 제약 조건을 삭제 및 추가하는 걸 우리가 직접.. 하면... 된다...
@Component
@RequiredArgsConstructor
public class MemberRoleInitializer {
private final MemberRoleRepository memberRoleRepository;
private final JdbcTemplate jdbcTemplate;
@PostConstruct
public void init() {
removeCheckConstraint();
addCheckConstraint();
initializeRoles();
}
private void removeCheckConstraint() {
jdbcTemplate.execute("ALTER TABLE member_role DROP CONSTRAINT IF EXISTS member_role_name_check");
}
private void addCheckConstraint() {
String[] enumValue = new String[MemberRoleEnum.values().length];
for (int i = 0; i < enumValue.length; i++) {
enumValue[i] = "'" + MemberRoleEnum.values()[i].name() + "'";
}
String checkConstraint = "CHECK (name IN(" + String.join(", ", enumValue) + "))";
jdbcTemplate.execute("ALTER TABLE member_role ADD CONSTRAINT member_role_name_check " + checkConstraint);
}
private void initializeRoles() {
for (MemberRoleEnum memberRoleEnum : MemberRoleEnum.values()) {
memberRoleRepository.findByName(memberRoleEnum).orElseGet(
() -> {
MemberRole role = MemberRole.builder()
.name(memberRoleEnum)
.description(memberRoleEnum.getDescription())
.build();
return memberRoleRepository.save(role);
}
);
}
}
}
1. removeCheckConstraint 로 기존 제약 조건 삭제.
2. addCheckConstraint 로 Enum 전부 돌며 제약 조건 등록
3. role 등록
이렇게 하면 된다.
하하 흠흠 호호