1.데이터 및 구조 설계 [EntityId]

dasd412·2022년 1월 29일
0

포트폴리오

목록 보기
3/41

클래스 코드

//복합키 엔티티의 Id를 생성할 때, 타입 정확성을 위해 만든 보조 클래스
public class EntityId<R,Long> {
    /*
    R에는 엔티티 객체의 클래스 정보를 넣고 Long 에는 해당 엔티티의 식별자를 넣습니다.
     */
    private final Class<R> reference;
    private final Long id;

    /*생성자를 private 으로 잠그고 정적 팩토리 메소드 of()를 활용하였다.
     왜냐하면 이 클래스는 <R,Long> 형인자 자료형 객체인데 생성자로 만들면 타이핑하기 힘들기 때문이다.
     */
    private EntityId(Class<R> reference, Long id) {
        this.reference = reference;
        this.id = id;
    }

    public static <R, Long> EntityId<R, Long> of(Class<R> reference, Long id) {
        //입력 값 검증은 모델 단에서 하는게 효율적이다.
        checkNotNull(reference,"entity reference must be provided");
        checkNotNull(id,"entity id must be provided");

        return new EntityId<>(reference, id);
    }


    public Long getId() {
        return id;
    }
    @Override
    public int hashCode() {//클래스 참조 값을 해싱한 값 리턴.
        return Objects.hash(reference);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        EntityId<?, ?> target = (EntityId<?, ?>) obj;
        return Objects.equals(reference, target.reference) && Objects
                .equals(id, target.id);
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
                .append("reference", reference.getSimpleName())
                .append("entityId", id)
                .toString();
    }
}

EntityId는 Entity 클래스들의 생성자에 쓰인다.

@Entity
@Table(name = "Writer", uniqueConstraints = @UniqueConstraint(columnNames = {"writer_id", "email"}))
public class Writer {

    public Writer(EntityId<Writer, Long> writerEntityId, String name, String email, Role role) {
        checkArgument(name.length() > 0 && name.length() <= 50, "name should be between 1 and 50");
        this.writerId = writerEntityId.getId();
        this.name = name;
        this.email = email;
        this.role = role;
    }

}

사용 예시 (음식 저장)

saveDiaryService.saveFoodOfWriterById(EntityId.of(Writer.class, me.getId()), EntityId.of(DiabetesDiary.class, diary.getId()), EntityId.of(Diet.class, diet.getDietId()), "cola");

장점

기본 Long 클래스를 사용하는 것보다는 타입 안전하다. class 참조를 엔티티 생성자에서 확인하기 때문에 자신 class에 해당하는 id인지 컴파일 타임에 잡아낼 수 있다.

단점
1. 사용 예시에도 알 수 있듯이, 코드가 더 길어진다.
2. 사용 예시에도 알 수 있듯이, of (ooo.class, id)에서 class를 제대로 넣었는데 id는 여전히 Long이다. 헷갈리면 버그의 원인이 된다... 뭔가 더 안전한 방법이 없을까.

profile
시드 레벨 스타트업의 2호 직원으로서 백엔드 시스템의 70%를 설계 및 개발하였고, TIPS 5억 투자 유치에 기여한 서버 개발자입니다. (Go/Python/MSA/Spring)

0개의 댓글