Toy Project - 백신 예약 웹 애플리케이션 제작 3 (엔티티 작성 v1)

Kim Dae Hyun·2021년 8월 15일
0

Toy-Project

목록 보기
4/4
post-thumbnail

Github 소스코드

BaseEntity

일단 모든 엔티티의 생성시간만 공통으로 관리하기 위해 BaseEntity를 작성했습니다.
이후 수정시간 등이 공통으로 필요하다 생각되면 추가할 예정입니다.

@Getter
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass
public class BaseEntity {

    @CreatedDate
    @Column(updatable = false)
    protected LocalDateTime createAt;
}

UserEntity

모든 엔티티의 기본생성자는 access levelPROTECTED로 하여 막아주기로 하였고 Getter 정도는 열어주기로 했습니다.

Oauth2를 통해 본인인증만을 수행할 예정이기 때문에 일반 사용자에게 password는 필요없지만 ADMIN은 폼 로그인을 사용할 예정이기 때문에 추가했습니다.

Oauth2를 통해 얻어올 수 있는 정보는 그대로 넣어주고 받을 수 없는 정보는 추가로 폼을 통해 받을 예정입니다.

모든 엔티티, DTO, 요청객체 등 모두 Builder패턴을 이용했습니다.
Builder 패턴이 실수를 방지하고 인스턴스 생성에 보다 명확성을 부여해준다 생각해서 이번 프로젝트에 모두 적용해볼 생각입니다.

package com.threefam.reserve.domain.entity;

import com.threefam.reserve.domain.value.Gender;
import com.threefam.reserve.domain.value.Role;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.time.LocalDateTime;

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "user")
@Getter
public class User extends BaseEntity{

    @Column(name = "user_id")
    @Id @GeneratedValue
    private Long id;

    @Column(nullable = false)
    private String email;


    @Column(nullable = false)
    private String password;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    @Enumerated(EnumType.STRING)
    private Gender gender;

    @Column(nullable = false)
    private Integer age;

    @Column(nullable = false)
    private String address;

    @Column(nullable = false)
    private String detailAddress;

    @Enumerated(EnumType.STRING)
    @Column(nullable = false)
    private Role role;

    @Builder(builderMethodName = "createUser")
    public User(String email, String password, String name,
                Gender gender, Integer age, String address, String detailAddress, Role role) {
        this.email = email;
        this.password = password;
        this.name=name;
        this.gender = gender;
        this.age = age;
        this.address = address;
        this.detailAddress = detailAddress;
        this.role = role;
        this.createAt = LocalDateTime.now();
    }
}

HopitalEntity

HospitalEntity 객체에서 예약가능날짜를 객체 그래프로 조회하기 위해 양방향으로 설정해주었습니다.
(양방향 관계에서 side-effect를 방지하기 위해 편의 메서드를 정의했습니다. 어느 한쪽에는 데이터가 세팅되지 않는 .. 그런 ...)

모든 ToOne 관계는 Lazy로 설정해주었습니다. ( Eager No! )

JPA에서 Boolean타입을 사용하기 위해 @Type 어노테이션 사용

백신 엔티티 또한 HospitalEntity 객체에서 편하게 조회하기 위해 양방향으로 설정했습니다.

병원 엔티티와 백신 엔티티의 생명주기를 같이하기 위해 cascadeorphanRemoval 사용

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "hospital")
@Getter
public class Hospital extends BaseEntity{

    @Id @GeneratedValue
    @Column(name = "hospital_id")
    private Long id;

    @Column(name = "hospital_name")
    private String hospitalName;

    // 양방향
    @OneToMany(mappedBy = "hospital",cascade = CascadeType.ALL)
    @JsonIgnoreProperties({"hospital"})
    private List<AvailableDate> availableDates = new ArrayList<>();

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "admin_id")
    private Admin admin;

    public void setAdmin(Admin admin) {
        this.admin = admin;
        admin.getHospitals().add(this);
    }

    @Column(nullable = false)
    private String address;

    @Column(nullable = false)
    private String detailAddress;

    @Column(name = "total_quantity")
    private Integer totalQuantity;

    public void cancel() {
        this.totalQuantity++;
    }

    @Column(name = "date_accept")
    private Integer dateAccept;
    @Column(name = "time_accept")
    private Integer timeAccept;

    public void setTotalVaccineQuantity(Integer qty) {
        this.totalQuantity = qty;
    }

    public void removeStock() {
        int restStock=this.totalQuantity-1;
        if(restStock==0){
            setEnabled(false);
        }
        if(restStock<0){
            throw new NotEnoughStockException("예약 가능한 수량이 부족합니다.");
        }

        this.totalQuantity=restStock;
    }

    public void updateDateAccept(Integer dateAccept){this.dateAccept=dateAccept;}

    public void updateTimeAccept(Integer timeAccept){this.timeAccept=timeAccept;}

    // true: y, false: n
    @Type(type = "yes_no")
    private Boolean enabled = true; // 예약 가능 여부

    @OneToMany(mappedBy = "hospital", cascade = CascadeType.PERSIST, orphanRemoval = true)
    @JsonIgnoreProperties({"hospital"})
    private List<Vaccine> vaccines = new ArrayList<>();

    public void setEnabled(boolean flag) {
        this.enabled = flag;
    }

    // 연관관계 편의 메서드
    private void addAdmin(Admin admin) {
        this.admin = admin;
        admin.getHospitals().add(this);
    }


    @Builder(builderMethodName = "createHospital")
    public Hospital(String hospitalName, String address, String detailAddress,Integer dateAccept,Integer timeAccept) {
        this.hospitalName = hospitalName;
        this.address = address;
        this.detailAddress = detailAddress;
        this.createAt = LocalDateTime.now();
        this.dateAccept=dateAccept;
        this.timeAccept=timeAccept;
    }
}

VaccineEntity

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "vaccine")
@Getter
public class Vaccine extends BaseEntity{

    @Id @GeneratedValue
    @Column(name = "vaccine_id")
    private Long id;

    @Column(name = "vaccine_name", nullable = false)
    private String vaccineName;

    @Column(nullable = false)
    private Integer quantity;

    public void cancel() {
        this.quantity++;
        this.enabled=true;
    }

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital hospital;

    @Type(type="yes_no")
    private boolean enabled = true;

    public void setEnabled(boolean flag) {
        this.enabled = flag;
    }

    @Builder(builderMethodName = "createVaccine")
    public Vaccine(String vaccineName, Integer quantity) {
        this.vaccineName = vaccineName;
        this.quantity = quantity;

        this.createAt = LocalDateTime.now();
    }
    // 연관관계 편의 메서드
    public void addHospital(Hospital hospital) {
        this.hospital = hospital;
        hospital.getVaccines().add(this);
    }

    //==비즈니스 로직==//

    //예약 취소 시, 사용
    public void addStock(){
        this.quantity+=1;
    }

    //예약 시, 사용
    public void removeStock(){
        int restStock=this.quantity-1;
        if(restStock==0){
            setEnabled(false);
        }
        if(restStock<0){
            throw new NotEnoughStockException("예약 가능한 수량이 부족합니다.");
        }

        this.quantity=restStock;
    }

    //병원 수정 시, 사용
    public void updateVaccineQty(Integer quantity){
        this.quantity=quantity;
    }
}

AvailableDateEntity (예약가능날짜)

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "available_date")
@Getter
public class AvailableDate {

    @Id
    @GeneratedValue
    @Column(name = "available_data_id")
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital hospital;

    @Column(nullable = false)
    private String date;

    // 양방향
    @OneToMany(mappedBy = "availableDate", cascade = CascadeType.ALL, orphanRemoval = true)
    @JsonIgnoreProperties({"availableDate"})
    private List<AvailableTime> availableTimes = new ArrayList<>();

    // 일일 수용 가능 인원
    @Column(name = "accept_count")
    private Integer acceptCount;

    public void cancel() {
        this.acceptCount++;
        this.enabled=true;
    }

    public void decreaseCount() {
        int restStock=this.acceptCount-1;
        if(restStock==0){
            setEnabled(false);
        }
        if(restStock<0){
            throw new NotEnoughStockException("예약 가능한 수량이 부족합니다.");
        }

        this.acceptCount=restStock;
    }

    @Type(type = "yes_no")
    private Boolean enabled = true;

    public void setEnabled(boolean flag) {
        this.enabled = flag;
    }

    // 양방향 연관관계 편의 메서드
    public void addHospital(Hospital hospital) {
        this.hospital = hospital;
        hospital.getAvailableDates().add(this);
    }

    @Builder(builderMethodName = "createAvailableDate")
    public AvailableDate(String date,Integer acceptCount){
        this.date=date;
        this.acceptCount=acceptCount;
    }

    //병원 상세내용 수정 시, count update
    public void updateAcceptCount(Integer acceptCount){
        this.acceptCount=acceptCount;
    }
}

AvailableTimeEntity

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "available_time")
@Getter
public class AvailableTime {

    @Id
    @GeneratedValue
    @Column(name = "available_time_id")
    private Long id;

    @Column(nullable = false)
    private int time;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "available_date_id")
    private AvailableDate availableDate;

    // 한 타임동안 수용 가능한 인원
    private Integer acceptCount;

    public void cancel() {
        this.acceptCount++;
        this.enabled=true;
        this.availableDate.cancel();
    }

    public void decreaseCount() {
        int restStock=this.acceptCount-1;
        if(restStock==0){
            setEnabled(false);
        }
        if(restStock<0){
            throw new NotEnoughStockException("예약 가능한 수량이 부족합니다.");
        }
        this.availableDate.decreaseCount();
        this.acceptCount=restStock;
    }

    @Type(type = "yes_no")
    private Boolean enabled = true;

    public void setEnabled(boolean flag) {
        this.enabled = flag;
    }

    // 양방향 연관관계 편의 메서드
    public void addAvailableDate(AvailableDate availableDate) {
        this.availableDate = availableDate;
        availableDate.getAvailableTimes().add(this);
    }

    private void setAvailableTime(int time) {
        this.time = time;
    }

    @Builder(builderMethodName = "createAvailableTime")
    public AvailableTime(int time, Integer acceptCount) {
        this.time = time;
        this.acceptCount = acceptCount;
    }

    public void updateAcceptCount(Integer acceptCount){
        this.acceptCount=acceptCount;
    }
}

ReserveItemEntity (예약서)

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "reserve_item")
@Getter
public class ReserveItem extends BaseEntity {

    @Column(name = "reserve_item_id")
    @Id
    @GeneratedValue
    private Long id;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "hospital_id")
    private Hospital Hospital;

    @Enumerated(value = EnumType.STRING)
    private ReserveStatus status = ReserveStatus.COMP;

    @Column(nullable = false)
    private String vaccineName;
    @Column(nullable = false)
    private String reserveDate;
    @Column(nullable = false)
    private int reserveTime;

    @Builder(builderMethodName = "createReserveItem")
    public ReserveItem(
            User user, Hospital Hospital, ReserveStatus status, String reserveDate, int reserveTime, String vaccineName) {
        this.user = user;
        this.Hospital = Hospital;
        this.status = status;
        this.reserveDate = reserveDate;
        this.reserveTime = reserveTime;
        this.vaccineName = vaccineName;
        this.createAt = LocalDateTime.now();
    }

    //==비즈니스 로직==//
    //예약 날짜 및 예약 시간 update
    public void updateDateAndTime(String reserveDate,int reserveTime){
        this.reserveDate=reserveDate;
        this.reserveTime=reserveTime;
    }
}

AdminEntity

admin에서 객체 그래프로 병원을 조회할 수 있도록 양방향 설정

@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Entity
@Table(name = "admin")
@Getter
public class Admin extends BaseEntity {

    @Id @GeneratedValue
    private Long id;

    private String name;

    @OneToMany(mappedBy = "admin", cascade = CascadeType.ALL)
    @JsonIgnoreProperties({"admin"})
    private List<Hospital> hospitals = new ArrayList<>();

    @Builder(builderMethodName = "createAdmin")
    public Admin(String name) {
        this.name = name;
    }
}
profile
좀 더 천천히 까먹기 위해 기록합니다. 🧐

0개의 댓글