์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

STEP 02 - ์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘: User์™€ Board ์—”ํ‹ฐํ‹ฐ

๋ฏธ๋กœ910 2024. 10. 8. 09:09
๐Ÿ’ก
1. JPA๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ User์™€ Board ์—”ํ‹ฐํ‹ฐ ๊ฐ„์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.
2. @ManyToOne๊ณผ @OneToMany ์–ด๋…ธํ…Œ์ด์…˜์˜ ์‚ฌ์šฉ๋ฒ•๊ณผ ์˜๋ฏธ๋ฅผ ์ดํ•ดํ•œ๋‹ค.
3. ์ง€์—ฐ ๋กœ๋”ฉ(FetchType.LAZY)์˜ ๋™์ž‘ ๋ฐฉ์‹์„ ์ดํ•ดํ•œ๋‹ค.
4. @JoinColumn ์–ด๋…ธํ…Œ์ด์…˜์˜ ์—ญํ• ์„ ์„ค๋ช…ํ•  ์ˆ˜ ์žˆ๋‹ค.

 

Board ์—”ํ‹ฐํ‹ฐ ์ˆ˜์ •ํ•˜๊ธฐ - user ์†์„ฑ ์ถ”๊ฐ€

Board ์—”ํ‹ฐํ‹ฐ์— User์™€์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ฉ์‹œ๋‹ค.

์ˆ˜์ •๋œ Board ์—”ํ‹ฐํ‹ฐ ์ฝ”๋“œ
package com.tenco.blog_v1.board;

import com.tenco.blog_v1.user.User;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Timestamp;

@NoArgsConstructor
@Entity
@Table(name = "board_tb")
@Data
public class Board {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // ๊ธฐ๋ณธํ‚ค ์ „๋žต db ์œ„์ž„
    private Integer id;
    private String title;
    private String content;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id")
    private User user; // ๊ฒŒ์‹œ๊ธ€ ์ž‘์„ฑ์ž ์ •๋ณด

    // created_at ์ปฌ๋Ÿผ๊ณผ ๋งคํ•‘ํ•˜๋ฉฐ, ์ด ํ•„๋“œ๋Š” ๋ฐ์ดํ„ฐ ์ €์žฅ์‹œ ์ž๋™์œผ๋กœ ์„ค์ • ๋จ
    @Column(name = "created_at", insertable = false, updatable = false)
    private Timestamp createdAt;

    @Builder
    public Board(Integer id, String title, String content, User user, Timestamp createdAt) {
        this.id = id;
        this.title = title;
        this.content = content;
        this.user = user;
        this.createdAt = createdAt;
    }

}

 

์ฝ”๋“œ ์„ค๋ช…

  • @Entity: ์ด ํด๋ž˜์Šค๊ฐ€ JPA ์—”ํ‹ฐํ‹ฐ์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
  • @Table(name = "board_tb"): ์—”ํ‹ฐํ‹ฐ์™€ ๋งคํ•‘๋  ํ…Œ์ด๋ธ” ์ด๋ฆ„์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • @Id, @GeneratedValue: ๊ธฐ๋ณธ ํ‚ค ํ•„๋“œ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • @ManyToOne(fetch = FetchType.LAZY):
    • Board ์—”ํ‹ฐํ‹ฐ๊ฐ€ User ์—”ํ‹ฐํ‹ฐ์™€ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • ์ง€์—ฐ ๋กœ๋”ฉ(LAZY): ์‹ค์ œ๋กœ user ํ•„๋“œ๊ฐ€ ์‚ฌ์šฉ๋  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉ์„ ์ง€์—ฐ์‹œํ‚ต๋‹ˆ๋‹ค.
  • @JoinColumn(name = "user_id"):
    • ์™ธ๋ž˜ ํ‚ค ์ปฌ๋Ÿผ์˜ ์ด๋ฆ„์„ user_id๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
    • ์ด ์ปฌ๋Ÿผ์„ ํ†ตํ•ด User ์—”ํ‹ฐํ‹ฐ์™€ ์—ฐ๊ฒฐ๋ฉ๋‹ˆ๋‹ค.
  • private User user;: ์ž‘์„ฑ์ž ์ •๋ณด๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ํ•„๋“œ์ž…๋‹ˆ๋‹ค

 

User ์—”ํ‹ฐํ‹ฐ ์ž‘์„ฑํ•˜๊ธฐ

User ์—”ํ‹ฐํ‹ฐ์— Board์™€์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜์—ฌ, ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.
package com.tenco.blog_v1.user;

import com.tenco.blog_v1.board.Board;
import jakarta.persistence.*;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;

import java.sql.Timestamp;
import java.util.List;

@NoArgsConstructor
@Data
@Entity
@Table(name = "user_tb")
public class User {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(unique = true) // ์œ ๋‹ˆํฌ ์ œ์•ฝ ์กฐ๊ฑด ์„ค์ •
    private String username;
    private String password;
    private String email;

    @CreationTimestamp // ์—”ํ‹ฐํ‹ฐ ์ƒ์„ฑ์‹œ ์ž๋™์œผ๋กœ ํ˜„์žฌ ์‹œ๊ฐ„ ์ž…๋ ฅ
    private Timestamp createdAt;

    // ๋‹จ๋ฐฉํ–ฅ, ์–‘๋ฐฉํ–ฅ ๋งคํ•‘(mappedBy)
    // @OneToMany(mappedBy = "user", fetch = FetchType.LAZY) // ์ง€์—ฐ ๋กœ๋”ฉ ์„ค์ •
    @OneToMany(mappedBy = "user", fetch = FetchType.EAGER) // ์ฆ‰์‹œ ๋กœ๋”ฉ ์„ค์ •
    private List<Board> boards;

    @Builder
    public User(Integer id, String username, String password, String email, Timestamp createdAt, List<Board> boards) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.email = email;
        this.createdAt = createdAt;
        this.boards = boards;
    }

}

 

์ฝ”๋“œ ์„ค๋ช…

  • @OneToMany(mappedBy = "user", fetch = FetchType.LAZY):
    • User ์—”ํ‹ฐํ‹ฐ๊ฐ€ Board ์—”ํ‹ฐํ‹ฐ์™€ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„์ž„์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • mappedBy = "user": Board ์—”ํ‹ฐํ‹ฐ์˜ user ํ•„๋“œ์— ์˜ํ•ด ๋งคํ•‘๋จ์„ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • ์ง€์—ฐ ๋กœ๋”ฉ(LAZY): ์‹ค์ œ๋กœ boards ํ•„๋“œ๊ฐ€ ์‚ฌ์šฉ๋  ๋•Œ๊นŒ์ง€ ๋กœ๋”ฉ์„ ์ง€์—ฐ์‹œํ‚ต๋‹ˆ๋‹ค.
  • private List<Board> boards;: ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๊ฒŒ์‹œ๊ธ€ ๋ชฉ๋ก์„ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

 

์—ฐ๊ด€ ๊ด€๊ณ„๊ฐ€ ์„ค์ •๋œ ์—”ํ‹ฐํ‹ฐ์— ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฝ์ž…ํ•˜์—ฌ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ๋งคํ•‘๋˜๋Š”์ง€ ํ™•์ธ.

์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ ์ž…๋ ฅ
-- ์‚ฌ์šฉ์ž ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
INSERT INTO user_tb(username, password, email, created_at) VALUES('๊ธธ๋™', '1234', 'a@nate.com', NOW());
INSERT INTO user_tb(username, password, email, created_at) VALUES('๋‘˜๋ฆฌ', '1234', 'b@nate.com', NOW());
INSERT INTO user_tb(username, password, email, created_at) VALUES('๋งˆ์ด์ฝœ', '1234', 'c@nate.com', NOW());

-- ๊ฒŒ์‹œ๊ธ€ ๋ฐ์ดํ„ฐ ์‚ฝ์ž…
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('์ œ๋ชฉ1', '๋‚ด์šฉ1', 1, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('์ œ๋ชฉ2', '๋‚ด์šฉ2', 1, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('์ œ๋ชฉ3', '๋‚ด์šฉ3', 2, NOW());
INSERT INTO board_tb(title, content, user_id, created_at) VALUES('์ œ๋ชฉ4', '๋‚ด์šฉ4', 3, NOW());

user_id ์ปฌ๋Ÿผ์„ ํ†ตํ•ด Board์™€ User๊ฐ€ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

 

Hibernate๊ฐ€ ์ƒ์„ฑํ•œ DDL
Hibernate: 
    drop table if exists board_tb cascade 
Hibernate: 
    drop table if exists user_tb cascade 
Hibernate: 
    create table board_tb (
        id integer generated by default as identity,
        user_id integer,
        created_at timestamp(6),
        content varchar(255),
        title varchar(255),
        primary key (id)
    )
Hibernate: 
    create table user_tb (
        id integer generated by default as identity,
        created_at timestamp(6),
        email varchar(255),
        password varchar(255),
        username varchar(255) unique,
        primary key (id)
    )
Hibernate: 
    alter table if exists board_tb 
       add constraint FKgxwryj58kh66twbp656wo5gnn 
       foreign key (user_id) 
       references user_tb

board_tb ํ…Œ์ด๋ธ”์— user_id ์ปฌ๋Ÿผ์ด ์™ธ๋ž˜ ํ‚ค(Foreign Key)๋กœ ์„ค์ •๋˜์–ด user_tb ํ…Œ์ด๋ธ”์˜ id ์ปฌ๋Ÿผ๊ณผ ์—ฐ๊ด€๋ฉ๋‹ˆ๋‹ค.

 

ํ•ต์‹ฌ ๊ฐœ๋… ์ •๋ฆฌ

1) ์—ฐ๊ด€ ๊ด€๊ณ„ ๋งคํ•‘ ์„ค์ •

  • @ManyToOne: Board ์—”ํ‹ฐํ‹ฐ์—์„œ User ์—”ํ‹ฐํ‹ฐ์™€์˜ ๋‹ค๋Œ€์ผ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • @OneToMany: User ์—”ํ‹ฐํ‹ฐ์—์„œ Board ์—”ํ‹ฐํ‹ฐ์™€์˜ ์ผ๋Œ€๋‹ค ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • mappedBy: ์—ฐ๊ด€ ๊ด€๊ณ„์˜ ์ฃผ์ธ์„ ์„ค์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋ฉฐ, ์—ฐ๊ด€ ๊ด€๊ณ„์˜ ์ฃผ์ธ์ด ์•„๋‹Œ ์ชฝ์—์„œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

2) ์ง€์—ฐ ๋กœ๋”ฉ (FetchType.LAZY)

  • ์ง€์—ฐ ๋กœ๋”ฉ(LAZY): ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋ฅผ ์‹ค์ œ๋กœ ์‚ฌ์šฉํ•  ๋•Œ๊นŒ์ง€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์กฐํšŒ๋ฅผ ์ง€์—ฐ์‹œํ‚ต๋‹ˆ๋‹ค.
  • ์ฆ‰์‹œ ๋กœ๋”ฉ(EAGER): ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋กœ๋”ฉ๋  ๋•Œ ์—ฐ๊ด€๋œ ์—”ํ‹ฐํ‹ฐ๋„ ํ•จ๊ป˜ ๋กœ๋”ฉํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์ด์œ : ๋ถˆํ•„์š”ํ•œ ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์„ ๋ฐฉ์ง€ํ•˜์—ฌ ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

3) @JoinColumn ์–ด๋…ธํ…Œ์ด์…˜

  • ์—ญํ• : ์™ธ๋ž˜ ํ‚ค ์ปฌ๋Ÿผ์„ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•˜์—ฌ, ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งคํ•‘ํ•ฉ๋‹ˆ๋‹ค.
  • ์‚ฌ์šฉ ์œ„์น˜: ์—ฐ๊ด€ ๊ด€๊ณ„์˜ ์ฃผ์ธ(Owner) ์ชฝ ์—”ํ‹ฐํ‹ฐ์˜ ํ•„๋“œ์— ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.