스프링/스프링부트+JPA - 블로그
스프링부트+JPA 블로그 프로젝트 03 DB세팅, 모델링, 연관관계
H-V
2021. 8. 27. 21:15
유투버 '데어프로그래밍'님 강의 참조
1) 모델링 및 DB테이블 세팅
- User
- MVC 패턴중 M의 해당하는 'Model'. 어플리케이션에 사용되는 정보들을 나타낸다. 이 모델 객체가 비지니스의 로직과 상태를 가지고 있다
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@DynamicInsert
@Entity
public class User {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;//시퀀스,Auto_Increment 용, 사용자가 많을경우 Long
@Column(nullable = false, length = 100, unique = true)
private String username; //사용자가 쓰는 아이디
@Column(nullable = false, length = 100)
private String password;
@Column(nullable = false, length = 50)
private String email;
@CreationTimestamp
private Timestamp createDate;
@ColumnDefault("'user'")
private String role;
}
- 클래스 생성 후 반드시 yml에서 'ddl-auto' 부분을 체크 하여 최초 테이블 생성시에는 'create'로 선언이 되어있는지 확인 해야한다
- 나중에 작업을 이어나갈때는 반드시 'update'로 바꿔나야한다
- 아이디가 중복이 되면 안된다. 반드시 unique를 걸어주자!
- 서버를 시작시켜보면..
- Board
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(nullable = false, length = 100)
private String title;
@Lob//대용량 데이터용 - 섬머노트를 위한
private String content;
private int count;
@ManyToOne // Many = Board, User = One
@JoinColumn(name="userId")
private User user;
@CreationTimestamp
private Timestamp createDate;
}
- 여기서 ORM의 장점이 나온다. 자바는 오브젝트를 객체로 저장이 되지만 DB는 불가능 하다. 이것을 어노테이션을 통해 테이블명을 지정 해 줄 수 있고, User 오브젝트를 객체로 받았지만 ORM이 자동적으로 DB방식으로 바꿔서 저장을 하는 것. 즉 FK키 없이도 가능하다. (ORM 방식에서는 유저아이디값을 받아올때는 int userId로 하지 않는다)
- FK 없이 연관관계를 설정하는것이 @ManyToOne -> 한명의 User는 여러개의 Board를 쓸 수 있다
- Reply
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
public class Reply {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(nullable = false, length = 200)
private String content;
@CreationTimestamp
private Timestamp createDate;
@ManyToOne
@JoinColumn(name="boardId")
private Board board;
@ManyToOne
@JoinColumn(name="userId")
private User user;
}
- 연관관계의 중요성이 부각 된다. 또한 ManyToOne, OneToMany, OneToOne 은 정말 햇갈리게 한다!
- Reply 클래스에서 @ManyToOne이면 여러개의 답글은 한 게시글에 달린다, 여러개의 답글은 한 유저가 쓸 수 있다
2) 연관 관계
- 앞서 세가지의 테이블을 만들었는데 여기서 연관관계가 엮어있지만 끝난게 아니다. 그 이유는 연관관계의 주인을 설정해야한다
- 연관관계의 주인은 FK키를 누가 가졌느냐에 따라 정해 진다
- 예로 게시판 상세보기시에 나오는 정보는 앞서 만든테이블 모두를 거치게 된다. 스프링부트+JPA 방식이 아니었다면 세개 모드를 SELECT하여 조인하는 방식을 했겠지만 지금의 방식으로는 BOARD만 셀렉트하여 진행 가능 하다
- JPA+ORM이 모델링 되어있는 클래스를 보고 자동적으로 불러야할 또는 걸러야할 데이터를 DB에서 받기 때문이다. (쿼리문으로 SLELCT*FROM BOARD WHERE ID = 1로 날려도 JPA에서는 BOARD클래스에서 USER오브젝트를 보고 거기에 맞춰서 프로세스한다!)
- 그렇다면 현재 세개의 테이블을 사용을 하였고 게시판 상세보기를 클릭하게되면 모든 테이블을 거쳐서 나와야하는게 정상이다 즉, BOARD 클래스에 Reply 오브젝트 객체도 추가해야한다
- 하지만 답글같은 경우 JoinColum을 안해도 된다 왜냐 replyId값은 한 테이블의 한열에 여러개를 줄 수 없기 때문이다. (DB의 1정규화 -> 원자성을 가진다 를 반드시 유지해야 한다)
ID TITLE CONTENT USERID REPLYID CREATEDATE 1 안녕 반가워 1 여러개가 달릴 경우 아이디를 1,2,3,4,5 이렇게 줄 수 없다는 말! 2021.08.27 - 즉 게시물 상세보기를 예로들면 상세보기시에 답글은 누가, 몇개나 달렸는지만 보이면 되는 것. Board를 셀렉트할때 키없이 DB의 내용만 던져줘 라고 요청해야한다.
public class Board { ... @OneToMany(mappedBy="board") private List<Reply> replies;
- 게시글은 여러개가 달리기때문에 List로 객체화 시켰고 @OneToMany로 하나의 게시글에 여러개의 답글이 달린다
- 또한 mappedBy라는 기능을 통해 Reply클래스의 Board board 객체를 받아오고 값만 들고 오게 된다
- 쉽게 풀이하면 mappedBy="객체" 는 연관관계의 주인이 아니다(FK가 아님을 뜻함) 즉 DB에 칼럼을 만들지 마세요 가 된다
- 위에서 언급한대로 연관관계의 주인들은 유저의 id와 보드의 id가 되는 것!
- 또한 UI에 따라 Fetch 전략을 이해하여야한다
public class Board {
...
@ManyToOne(fetch = FetchType.EAGER) // Many = Board, User = One
@JoinColumn(name="userId")
private User user;
@OneToMany(mappedBy="board", fetch = FetchType.EAGER)
private List<Reply> replies;
- 반드시 필요한 테이블내용이면 EAGER, 필요할때만 가져오는 내용이면 LAZY(예:댓글 펼치보기는 상세보기시에 무조건 안나와도 되는 데이터)로 제약을 걸 수 있다.