account 등록 후 citizen을 등록하려고 보니 관련된 테이블들이 너무 많습니다.

JPA에 익숙하지 않아서 조금 낯선 상황입니다. 🥲

Untitled


고민점

  1. citizen 등록
    • 등록전 address 조회가 선행 되어야만 합니다.
    • 근데 jpa에서 등록하려고 보니 citizen에 address entity가 필요하고 address엔 sido, sigungu, eupmyeondong entity가 필요합니다.

🔥 연관관계에 대해 공부하지 않고 Jpa를 구성 했을때

Account

@Entity
public class AccountEntity {
    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    private Long accountId;
    private String email;
    private String password;
    @Enumerated(EnumType.STRING)
    private AccountEmailVerificationStatus emailVerificationStatus;
    @Enumerated(EnumType.STRING)
    private AccountActiveStatus activeStatus;
    @OneToMany(mappedBy = "accountEntity")
    private List<CitizenEntity> citizens = new ArrayList<>();

    private String emailVerificationToken;

    public AccountEntity() {}

    public AccountEntity(String email, String password, AccountEmailVerificationStatus emailVerificationStatus, AccountActiveStatus activeStatus) {
        this.email = email;
        this.password = password;
        this.emailVerificationStatus = emailVerificationStatus;
        this.activeStatus = activeStatus;
    }

    public AccountEntity(Account account) {
        this.email = account.email();
        this.password = account.password();
        this.emailVerificationStatus = account.emailVerificationStatus();
        this.activeStatus = account.activeStatus();
        emailVerificationToken = account.emailVerificationToken();
    }

    public Account toDomain() {
        return new Account(this.accountId ,this.email, this.password, this.emailVerificationToken, this.emailVerificationStatus, this.activeStatus);
    }

    public void update(AccountUpdateRequest updateRequest) {
        this.email = updateRequest.email();
    }

    public void update(Account account) {
        this.email = account.email();
        this.password = account.password();
        this.emailVerificationStatus = account.emailVerificationStatus();
        this.activeStatus = account.activeStatus();
        emailVerificationToken = account.emailVerificationToken();
    }

    public void delete() {
        this.activeStatus = AccountActiveStatus.DELETED;
    }
}

Citizen

@Entity
public class CitizenEntity {

    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    private Long citizenId;
    private String nickname;
    private String phoneNumber;
    private LocalDate createdAt;
    private CitizenActiveStatus activeStatus;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="account_id")
    private AccountEntity accountEntity;

    @OneToMany(mappedBy = "addressEntity", cascade = CascadeType.ALL)
    private List<AddressEntity> addressEntity;

    @OneToOne(mappedBy = "hobbyEntity", cascade = CascadeType.ALL)
    private HobbyEntity hobbyEntity;

    public CitizenEntity(Citizen citizen) {
        this.nickname = citizen.nickname();
        this.createdAt = citizen.createdAt();
        this.phoneNumber = citizen.phoneNumber();
        this.activeStatus = CitizenActiveStatus.ACTIVE;
    }

    public CitizenEntity() {}

    public Citizen toDomain() {

        return null;
    }

    public void delete() {
        this.activeStatus = CitizenActiveStatus.DELETED;
    }
}

Address

@Entity
public class AddressEntity {
    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    private Long addressId;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "citizen_id")
    private CitizenEntity citizen;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "si_do_id")
    private SiDoEntity siDo;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "si_gun_gu_id")
    private SiGunGuEntity siGunGu;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "eup_myeon_dong_id")
    private EupMyeonDongEntity eupMyeonDong;

}

Hobby

@Entity
public class HobbyEntity {
    @Id
    private Long id;
    private String hobby;
    private LocalDate createdAt;

}

1. Citizen과 Account

2. Citizen과 Hobby

3. Citizen과 Address

✨ 고친 점

@Entity
public class CitizenEntity {

    @Id
    @GeneratedValue(strategy = jakarta.persistence.GenerationType.IDENTITY)
    private Long citizenId;
    private String nickname;
    private String phoneNumber;
    private LocalDate createdAt;
    private CitizenActiveStatus activeStatus;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name="account_id")
    private AccountEntity accountEntity;

    @OneToMany(mappedBy = "addressEntity", cascade = CascadeType.ALL)
    private List<AddressEntity> addressEntity;

    @OneToMany(mappedBy = "hobbyEntity", cascade = CascadeType.ALL)
    private List<HobbyEntity> hobbyEntity;

    public CitizenEntity(Citizen citizen) {
        this.nickname = citizen.nickname();
        this.createdAt = citizen.createdAt();
        this.phoneNumber = citizen.phoneNumber();
        this.activeStatus = CitizenActiveStatus.ACTIVE;
    }

    public CitizenEntity() {}

    public Citizen toDomain() {

        return null;
    }

    public void delete() {
        this.activeStatus = CitizenActiveStatus.DELETED;
    }
}
@Getter
@Entity
@IdClass(CitizenHobbyId.class)
public class CitizenHobbyEntity {
    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "citizenId")
    private CitizenEntity citizen;

    @Id
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hobbyId")
    private HobbyEntity hobby;
}
public class CitizenHobbyId implements Serializable {
    private Long citizenId;
    private Long hobbyId;

    @Override
    public boolean equals(Object obj) {
        return super.equals(obj);
    }

    @Override
    public int hashCode() {
        return super.hashCode();
    }
}
@Entity
public class HobbyEntity {
    @Id
    private Long hobbyId;
    private String hobby;
    private LocalDate createdAt;

}

시도, 시군구, 읍면동

예시) 1154510100 : 서울특별시 금천구 가산동

시도는 무조건 전체 조회

시군구 조회

읍면동 조회

@Repository
@RequiredArgsConstructor
public class AddressDao implements AddressRepository {
    private final AddressJpaRepository addressJpaRepository;
    private final SiDoJpaRepository siDoJpaRepository;
    private final SiGunGuJpaRepository siGunGuJpaRepository;
    private final EupMyeonDongJpaRepository eupMyeonDongJpaRepository;

    @Override
    public AddressResponse save(Address address) {
        AddressEntity addressEntity = addressJpaRepository.save();
        return addressEntity.toDomain();
        return null;
    }

    @Override
    public List<SiDo> getSiDoList() {
        List<SiDoEntity> all = siDoJpaRepository.findAll();
        return all.stream().map(SiDoEntity::toDomain).collect(Collectors.toList());
    }

    @Override
    public List<SiGunGu> getSiGunGuList(String sido) {
        List<SiGunGuEntity> all = siGunGuJpaRepository.findBySido(sido);
        return all.stream().map(SiGunGuEntity::toDomain).collect(Collectors.toList()));
    }

    @Override
    public List<EupMyeonDong> getEupMyeonDongList(String sido, String sigungu) {
        List<EupMyeonDongEntity> all = eupMyeonDongJpaRepository.findBySidoAndSigungu(sido, sigungu);
        return all.stream().map(EupMyeonDongEntity::toDomain).collect(Collectors.toList());
    }
}

위와 같이 만들고 보니 시군구에선 시도 코드를, 읍면동에선 시도, 시군구 코드를 가지고 있는게 조회시 편할 것 같다는 생각이 들었습니다.

이걸 JPA로 어떻게 풀어야 하는지가 고민입니다.


참고

JPA 연관 관계 한방에 정리 (단방향/양방향, 연관 관계의 주인, 일대일, 다대일, 일대다, 다대다)