카테고리 없음

[JPA] @PostConstruct Enum DB 등록 문제

wonow_ 2024. 6. 27. 12:53

 

현재 프로젝트에서 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 등록

 

이렇게 하면 된다.

하하 흠흠 호호