⭐️ Today's summary
오늘은 ORM 연관 맵핑에 대해 배웠다. 이 연관 관계를 맵핑하면서 엔티티 설계를 단방향 관계로 할지,
양방향 관계로 할지 중요하였다.
오늘은 그 부분에 대해서 다뤄보겠다.
⭐️ Problem
우선 엔티티 설계 하는것이 어려워서 엔티티 설계를 숙제로 내주셨는데, 아래에 엔티티 설계를 해보겠다.
⭐️ Try
[ Member.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Member {
@Id
@Column(name = "user_id", length = 30)
private String userId;
@Column(nullable = false, length = 100, name = "user_pwd")
private String userPw;
@Column(nullable = false, length = 15, name = "user_name")
private String userName;
private String email;
@Check(constraints = "gender IN ('M', 'F')")
@Column(length = 1)
private String gender;
private int age;
@Column(length = 13)
private String phone;
@Column(length = 100)
private String address;
@CreationTimestamp
@Column(name = "enroll_date", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime enrollDate;
@UpdateTimestamp
@Column(name = "modify_date", columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime modifyDate;
@Column(length = 1, nullable = false, columnDefinition = "varchar(1) default 'Y'")
@Check(constraints = "status IN ('Y', 'M')")
private String status;
@OneToMany(mappedBy = "member", cascade = CascadeType.ALL)
private List<Notice> noticeList = new ArrayList<>();
}
우선 엔티티 설정, 테이블명 설정, PK설정, 컬럼명 설정 등 해보았다.
그리고 양방향 설계는 아직 어떤것을 양방향으로 설계해야할지 몰라서 하나만 넣어보았다.
양방향 설계는 저기에 나와있듯이 Member엔티티가 Notice엔티티를 조회할 필요가 있을 때 사용하면 될거같다.
[ Profile.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Profile {
@Id
@Column(length = 30, name = "user_id")
private String userId;
@Column(name = "profile_image", length = 100)
private String profileImage;
@Column(length = 300)
private String intro;
// 외래키
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@MapsId
@JoinColumn(name = "user_id", nullable = false)
private Member member;
}
여기에선 외래키 설정을 해주었다. 외래키는 @JoinColumn을 이용하여 name="~~" 에 나오는 컬럼을 FK로 만들겠다는 말이다.
여기선 기본키와 외래키이며, 1:1 관계이다.
또한 @MapsId를 사용한 이유는 "이 필드는 외래키이자 기본키로 사용한다"라는 것을 JPA에게 알려준다.
즉, Profile.userId는 Member.userId와 동일한 값을 가지게 된다.
[ Notice.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Notice {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "notice_no")
private Long noticeNo;
@Column(name = "notice_title", nullable = false, length = 30)
private String noticeTitle;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "notice_writer", nullable = false, columnDefinition = "VARCHAR(30)")
private Member member;
@Column(name = "notice_content", length = 200, nullable = false)
private String noticeContent;
@CreationTimestamp
@Column(name = "create_date", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createDate;
}
여기선 @JoinColumn을 쓰게되면 외래키로 등록이되지만 타입을 주고싶은데 타입이 객체 타입이기때문에,
기본값으로 columnDefinition = "VARCHAR(30)" 이런식으로 타입을 줄 수 있다.
[ Board.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Board {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "board_no")
private long boardNo;
@Column(name = "board_title", nullable = false, length = 100)
private String boardTitle;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "board_writer", columnDefinition = "VARCHAR(30)")
private Member member;
@Column(name = "board_content", nullable = false, columnDefinition = "CLOB")
private String boardContent;
@Column(name = "origin_name", length = 100)
private String originName;
@Column(name = "change_name", length = 100)
private String changeName;
@Column(columnDefinition = "int default 0")
private int count;
@CreationTimestamp
@Column(name = "create_date", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP")
private LocalDateTime createDate;
@Column(length = 1, nullable = false, columnDefinition = "varchar(1) default 'Y'")
@Check(constraints = "status IN ('Y', 'M')")
private String status;
}
@Check 어노테이션을 사용하여 check 제약조건을 추가해 주었다.
[ Reply.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Check;
import org.hibernate.annotations.CreationTimestamp;
import java.time.LocalDateTime;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Reply {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "reply_no")
private long replyNo;
@Column(name = "reply_content", length = 400, nullable = false)
private String replyContent;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "ref_bno", columnDefinition = "int", nullable = false)
private Board board;
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "reply_writer", columnDefinition = "varchar(30)", nullable = false)
private Member member;
@CreationTimestamp
@Column(name = "create_date", updatable = false, columnDefinition = "TIMESTAMP DEFAULT CURRENT_TIMESTAMP", nullable = false)
private LocalDateTime createDate;
@Column(length = 1, nullable = false, columnDefinition = "varchar(1) default 'Y'")
@Check(constraints = "status IN ('Y', 'M')")
private String status;
}
Reply 엔티티에선 외래키가 두개이기때문에 두개의 연관관계를 지어보았다.
[ Tag.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
public class Tag {
@Id
@Column(name = "tag_id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long tagId;
@Column(name = "tag_name", length = 30, nullable = false, unique = true)
private String tagName;
}
[ BoardTage.java ]
package com.kh.jpa.entity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "BOARD_TAG")
public class BoardTag {
@Id
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "BOARD_NO", nullable = false, columnDefinition = "int")
private Board board;
@Id
@ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinColumn(name = "TAG_ID", nullable = false, columnDefinition = "int")
private Tag tag;
}
마지막으로 여기선 Board_ID와 TAG_ID가 기본키이면서 외래키이기때문에 위와같이 설정해보았다.
엔티티설계를 처음 해보았는데, 여러번 해보다보면 익숙해 지고, 객체 지향 설계를 할 수 있어서 좋을 것 같다.
'Weekly TIL' 카테고리의 다른 글
Weekly TIL - Day 32 (2) | 2025.05.16 |
---|---|
Weekly TIL - Day 31 (2) | 2025.05.15 |
Weekly TIL - Day 29 (0) | 2025.05.13 |
Weekly TIL - Day 28 (0) | 2025.05.12 |
Code Review (Weekly TIL - Day 27) (2) | 2025.05.11 |