https://start.spring.io/에서
아래와 같이 설정해주었다.


converter에서 entity의 생성을 하지 않고 service에서 하도록 하는 경우도 있지만,
converter에서 엔티티의 생성을 하게 되면, service는 순수하게 비즈니스 로직에만 집중할 수 있어 단일 책임 원칙 측면에서 더 좋다.
로컬 DB를 만들어서 진행했다.

인텔리제이 프로젝트로 돌아와서, application.properties 파일은 지우고, 대신 application.yml 파일을 만들어서 아래와 같이 입력한다.

User 엔티티를 만들어주었다.

이때, domian 패키지 안에 enums 패키지를 만들어 Gender, SocialType(가입 연동 소셜 SNS 타입), UserStatus(탈퇴 등 여부) 에 대한 enum 타입을 지정해주었다.



그 외에도 아래 사진과 같이 다른 기본 엔티티들을 만들어주었다.

BaseEntity에서는 Created_at, Updated_at을 속성으로 두어 모든 엔티티 클래스에서 상속하도록 설정했다.



등등 다 적용해줌!
그리고 Spring boot Application 자체도 JpaAuditing사용이 가능하도록 변경을 해줘야 한다.

매핑 테이블은 따로 패키지에 모아서 관리하는 것이 보기에 편하다고 한다.
아래 사진과 같이 mapping 패키지를 만들어 그 안에 N:M 관계로 파생된 테이블에 대한 엔티티들을 생성해주었다.

연관관계의 주인 : 실제 데이터베이스에서 외래키를 가지는 엔티티(테이블)
단방향 매핑 : 단방향 매핑이란 연관 관계 주인에게만 연관 관계를 주입하는 것
양방향 매핑 : 양방향 매핑은 연관 관계 주인이 아닌 엔티티에게도 연관 관계를 주입하는 것
양방향 매핑을 사용하게 될 경우 버그가 생길 위험이 있지만,
양방향 매핑으로 인한 객체 그래프 탐색은 프로그래밍을 굉장히 편리하게 해준다고 한다.
아래와 같은 테이블 매핑 관계에서,
FoodOfUser 테이블의 경우 User와 food 모두에 대한 외래키를 갖고 있다.

FoodOfUser 엔티티에 연관 관계 매핑을 해보자.
package umc.spring.domain.mapping;
import lombok.*;
import umc.spring.domain.Food;
import umc.spring.domain.User;
import umc.spring.domain.common.BaseEntity;
import javax.persistence.*;
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class FoodOfUser extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// N : 1에서 N에 해당하는 엔티티가 1에 해당하는 엔티티와 연관 관계를 매핑할 때 @ManytoOne 어노테이션을 쓴다.
// (fetch = FetchType.LAZY)는 지연 로딩을 설정하는 것
// @JoinColumn은 실제 데이터베이스에서 해당 칼럼(외래키)의 이름을 설정하는 것
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User user;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "food_id")
private Food food;
}
그리고 UserMission 엔티티에 대해 단방향 연관 관계를 설정하겠다.

양방향 매핑의 장점은 객체 그래프 탐색으로 인한 이점이 있으며,
필수적인 기능인 cascade의 설정이 가능하다
따라서 양방향 매핑이 있어야 올바르게 cascade의 적용이 가능한데, 양방향 매핑을 할 경우 연관 관계 편의 메서드가 필요해진다.
User 엔티티에 양방향 매핑을 해보겠다.

package umc.spring.domain;
import lombok.*;
import umc.spring.domain.common.BaseEntity;
import umc.spring.domain.enums.Gender;
import umc.spring.domain.enums.SocialType;
import umc.spring.domain.enums.UserStatus;
import umc.spring.domain.mapping.FoodOfUser;
import umc.spring.domain.mapping.UserMission;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Enumerated(EnumType.STRING)
private Gender gender;
private Integer birthYear;
private Integer birthMonth;
private Integer birthDay;
private String address;
private Integer point;
@Enumerated(EnumType.STRING)
private UserStatus status;
private String email;
@Enumerated(EnumType.STRING)
private SocialType socialType;
private boolean certification;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<FoodOfUser> memberPreferList = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Review> reviewList = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserMission> userMissionList = new ArrayList<>();
}
양방향 매핑의 경우 1 : N에서 1에 해당하는 엔티티에게 설정한다.
@OneToMany 어노테이션을 통해 1에 해당하는 엔티티가 N에 해당하는 엔티티와 관계가 있음을 명시하고,
N에 해당하는 엔티티에서 ManyToOne이 설정 된 멤버변수를 mappedBy한다.
CascadeType.ALL이란 Member의 변화에 따라 Review, FoodOfUser 등의 엔티티가 영향을 받는다는 것을 의미한다.
-> 유저가 삭제 될 때, 유저를 참조하는 나머지 데이터도 같이 삭제된다. (에브리타임의 경우 탈퇴한다고 게시글이 사라지는 게 아니므로 이런 경우인지 기획된 것을 잘 봐야 한다!)
JPA에서 칼럼에 대한 세부적인 설정((유니크, 디폴트 값, null이 가능한지 등등)은 @Column을 이용해서 한다.
gender와 status는 enum으로 되어있어서
@Column(columnDefinition = "VARCHAR(10)") 와 같이 설정했다.
package umc.spring.domain;
import lombok.*;
import umc.spring.domain.common.BaseEntity;
import umc.spring.domain.enums.Gender;
import umc.spring.domain.enums.SocialType;
import umc.spring.domain.enums.UserStatus;
import umc.spring.domain.mapping.FoodOfUser;
import umc.spring.domain.mapping.UserMission;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
@Getter
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 15)
private String name;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "VARCHAR(10)")
private Gender gender;
private Integer birthYear;
private Integer birthMonth;
private Integer birthDay;
@Column(nullable = true, length = 100)
private String address;
private Integer point;
@Enumerated(EnumType.STRING)
@Column(columnDefinition = "VARCHAR(15) DEFAULT 'ACTIVE'")
private UserStatus status;
@Column(nullable = false, length = 50)
private String email;
@Enumerated(EnumType.STRING)
private SocialType socialType;
private boolean certification;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<FoodOfUser> memberPreferList = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Review> reviewList = new ArrayList<>();
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserMission> userMissionList = new ArrayList<>();
}

application.yml에서 auto를 create로 하고 Spring boot를 실행하면
JPA가 @Entity가 붙은 대상을 찾아내어 Database에 테이블을 만들게 된다.
주의할 점 : 기존 테이블을 모두 없애버리고 새로 만들게 되어서, 처음 Database를 만드는 것 아니라면 update로 해두는 것이 좋다.(update는 바뀐 부분에 대해서만 적용을 해준다.)
나머지 엔티티들에도 연관관계를 매핑해주고(맨 아래 깃허브 링크 참고!) Column 설정 등등을 다 해준 후 Application을 실행시켰는데,
create로 두고 처음으로 실행 시 여러 에러가 생기게 되는데, 기존 테이블이 없는데 삭제하려고 하다 보니 생긴 오류라서 무시하고

한번 다시 실행하면 오류 없이 테이블이 잘 생성되게 된다.

Datagrip에서도 확인해보면 테이블들이 잘 생성된 것을 확인해볼 수 있다!

(이제 다시 create-> update로 수정하러 가야지..)
[결과물]
https://github.com/SSWU-UMC-5TH-SPRING/Minyeong/tree/mission7