๐ GPT์๊ฒ ๋ฌผ์ด๋ณธ ์ง๋ฌธ ๋ชฉ๋ก
1) ์ฐ๊ด๊ด๊ณ ๋งคํ (1:N vs N:1)
JPA ์์ N๋1์ ํ
์ด๋ธ์์ ์์ํ
์ด๋ธ์ FK๊ฐ ์๋ฏ์ด ๊ฐ์ฒด๊ด์ ๊ด๊ณ์์๋ N ์ชฝ์ด ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ ๊ฐ์ ธ๊ฐ์์
๋ฐ๋ฉด์ 1๋N์ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ 1์ชฝ์ด ๊ฐ์ ธ๊ฐ์์ (ex. TEAM, MEMBER)
๊ตณ์ด ์ด๋ ๊ฒ ํ๋ ์ด์ ๊ฐ ์์๊น?
@OneToMany
@JoinColumn(name="TEAM_ID")
List<MEMBER> members = new ArrayList<>();
2) mappedBy ๊ฐ ํ์ํ ์ด์
JPA ๊ฐ์ฒด๊ฐ์ ์๋ฐฉํฅ ์ฒ๋ฆฌ๋ฅผ ํ ๋ (๊ฐ์ฒด : MEMBER, TEAM)
@OneToMany(mappedBy="team")
์ด๋ ๊ฒ ์ฒ๋ฆฌ๋ฅผ ํด์ฃผ์์ ์ ํด์ฃผ๋๊ฑฐ์ง?
์ํด์ฃผ๋ฉด ์๊ธฐ๋ ๋ฌธ์ ๊ฐ ๋ญ์ผ?
์ด์ฐจํผ ์ฐ๊ด๊ด๊ณ ๋ฉ์๋๋ก ๋๊ธฐํ์ฒ๋ฆฌ ํด์ฃผ์์.
/* ์ฐ๊ด๊ด๊ณ ๋ฉ์๋ */
private void addTeam(Team team) {
this.team = team; team.getMembers().add(this);
}
๐ ๋ต๋ณ ์์ฝ
- `mappedBy`๋ฅผ ์ํด์ฃผ๋ฉด JPA๋ ์์ชฝ ๋ชจ๋๋ฅผ ์ฐ๊ด๊ด๊ณ์ ์ฃผ์ธ์ผ๋ก ์๊ฐํ ์ ์๋ค. ์ด๋ด๊ฒฝ์ฐ ์์ชฝ์์ FK๋ฅผ ๊ด๋ฆฌํ๋ ค ํ ์ ์๋ค.
- ์์์น ๋ชปํ ์กฐ์ธํ ์ด๋ธ์ด ์์ฑ๋ ์ ์๋ค.
- ํ๋ก์ ๊ฐ์ฒด์ ์์ฑ์ด๋ Lazy Loading ์ค์ ์ ๋ฌธ์ ๊ฐ ์๊ธธ ์ ์๋ค.
3) ์ง์ฐ๋ก๋ฉ vs ์ฆ์๋ก๋ฉ
JPA์์ ์ง์ฐ๋ก๋ฉ๊ณผ ์ฆ์๋ก๋ฉ์ ์ด๋ค ๊ฐ๋ ์ผ๊น?
N+1 ๋ฌธ์ ์๋ ๊ด๋ จ์ด ์๋ค๊ณ ๋ค์๋ค.
4) ์ฐ๊ด๊ด๊ณ ๋งคํ 1๋1 ๊ด๊ณ์์ ์ฃผํ ์ด๋ธ๊ณผ ๋์ํ ์ด๋ธ ์ค ์ธ๋ํค ์ ์
1๋1๊ด๊ณ์์ ์ฃผํ ์ด๋ธ์ ์ธ๋ํค๊ฐ ์๋ ๊ฒ๊ณผ ๋์ํ ์ด๋ธ์ ์ธ๋ํค๊ฐ ์๋ ๊ฒ์ ์ฅ๋จ์ ์?
(ex. ์ฃผํ ์ด๋ธ : MEMBER, ๋์ํ ์ด๋ธ : LOCKER)
๋์ํ ์ด๋ธ์ ์ธ๋ํค๊ฐ ์์ผ๋ฉด ๋์ค์ MEMBER : LOCKER ๋ฅผ 1๋N ๊ด๊ณ๋ก ๋ง๋ค๊ณ ์ถ์ ๋ LOCKER์ FK ์ ์ฝ์กฐ๊ฑด์์ UNIQUE๋ง ๋นผ๋ฉด๋๋๊น ์ค๊ณ๋ ํ ์ด๋ธ์ ๋ง์ด ๊ณ ์น ํ์๊ฐ ์์ด์ ์ข์๊ฑด ์๊ฒ ์ด ๋จ์ ์ ํ๋ก์ ๊ธฐ๋ฅ์ ํ๊ณ๋ก ์ง์ฐ๋ก๋ฉ์ผ๋ก ์ค์ ํด๋ ํญ์ ์ฆ์๋ก๋ฉ ๋๋ค๋๋ฐ ๋ฌด์จ๋ง์ด์ง? ๊ทธ ์ธ์ ์ฅ์ ์ด๋ ๋จ์ ์ด ์์๊น?
์ฃผํ ์ด๋ธ์ ์ธ๋ํค๊ฐ ์์ผ๋ฉด ์ฅ์ : ์ฃผ ํ ์ด๋ธ๋ง ์กฐํํด๋ ๋์ ํ ์ด๋ธ์ ๋ฐ์ดํฐ๊ฐ ์๋์ง ํ์ธ ๊ฐ๋ฅ ๋จ์ : ๊ฐ์ด ์์ผ๋ฉด ์ธ๋ํค์ NULL ํ์ฉ ์ด๊ฑฐ ๋ง๋? ๊ฐ์ฒด์งํฅ ๊ฐ๋ฐ์๊ฐ ์ ํธํ๊ณ JPA๋งคํ์ด ํธ๋ฆฌํ๋ค๊ณ ํ๋๋ฐ ์ ๊ทธ๋ฐ๊ฑฐ์ง?
5) ๊ณ์ธต๊ตฌ์กฐ์ ํ ์ด๋ธ JPA ์ํฐํฐ ์ค๊ณ
package jpashop.jpaorder.domain;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Category {
@Id @GeneratedValue
private Long id;
private String name;
@ManyToMany
private List<Item> items = new ArrayList<>();
@ManyToOne
@JoinColumn(name = "PARENT_ID")
private Category parent;
@OneToMany(mappedBy = "parent")
private List<Category> child = new ArrayList<>();
}
์์ ์ ์ฐธ์กฐํ๋ ๊ณ์ธต๊ตฌ์กฐ๋ @Entity๊ฐ ์ด๋ ๊ฒ ์ค๊ณ๋๋๊ฒ ๋ง๋?
6) JPA ๊ตฌ๋ (Persistence, EntityManagerFactory, EntityManager, EntityTransaction)
Persistence, EntityManagerFactory, EntityManager, EntityTransaction์ ๊ฐ๋ ๊ณผ JPA ๊ตฌ๋ํ๋ฆ์ ๋ํด ์์ธํ ์ค๋ช ํด์ค
๐ ๋ต๋ณ ์์ฝ
JPA ๊ตฌ๋๋ฐฉ์
๐ JPA ๊ตฌ๋ ๋ฐฉ์JPA์ ๊ตฌ๋ ๋ฐฉ์์ Persistence → EntityManagerFactory → EntityManager์ ๊ณ์ธต์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฆ ๋๋ค. ์ฃผ์ ํด๋์ค์ ๊ตฌ๋ ๋ฐฉ์์ ๋จ๊ณ๋ณ๋ก ์ค๋ช ํฉ๋๋ค.1. JPA์ ์ฃผ์ ํด๋์ค ๋ฐ ์ญํ Persistence:JPA
dev-jk93.tistory.com
7) JPA PK ์์ฑ์ ๋ต @GeneratedValue(strategy = GenerationType.โ)
JPA์ PK ์์ฑ์ ๋ต(@GeneratedValue(strategy = GenerationType.?))์ ๋ํด ์์ธํ ์ค๋ช ํด์ค
๐ ๋ต๋ณ ์์ฝ
JPA ๊ตฌ๋๋ฐฉ์
๐ JPA ๊ตฌ๋ ๋ฐฉ์JPA์ ๊ตฌ๋ ๋ฐฉ์์ Persistence → EntityManagerFactory → EntityManager์ ๊ณ์ธต์ ๊ตฌ์กฐ๋ฅผ ๋ฐ๋ฆ ๋๋ค. ์ฃผ์ ํด๋์ค์ ๊ตฌ๋ ๋ฐฉ์์ ๋จ๊ณ๋ณ๋ก ์ค๋ช ํฉ๋๋ค.1. JPA์ ์ฃผ์ ํด๋์ค ๋ฐ ์ญํ Persistence:JPA
dev-jk93.tistory.com
8) Domain Model Pattern VS Transaction Script Pattern
1,2 ์์๋ฅผ ํตํด์ ๋๋ฉ์ธ ๋ชจ๋ธ ํจํด vs ํธ๋์ญ์ ์คํฌ๋ฆฝํธ ํจํด์ ์ค๋ช ํด์ค
// 1
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List orderItems = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL)
private Delivery delivery;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; // ์: ORDERED, CANCELED
//== ๋น์ฆ๋์ค ๋ก์ง ==//
// ์ฃผ๋ฌธ ์์ฑ
public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) {
Order order = new Order();
order.setMember(member);
order.setDelivery(delivery);
for (OrderItem orderItem : orderItems) {
order.addOrderItem(orderItem);
}
order.setOrderDate(LocalDateTime.now());
order.setStatus(OrderStatus.ORDERED);
return order;
}
// ์ฃผ๋ฌธ ์ทจ์
public void cancel() {
if (delivery.getStatus() == DeliveryStatus.COMPLETE) {
throw new IllegalStateException("์ด๋ฏธ ๋ฐฐ์ก ์๋ฃ๋ ์ฃผ๋ฌธ์ ์ทจ์ํ ์ ์์ต๋๋ค.");
}
this.setStatus(OrderStatus.CANCELED);
for (OrderItem orderItem : orderItems) {
orderItem.cancel(); // ์ฃผ๋ฌธ์ํ๋ ์ทจ์
}
}
// ์ด ์ฃผ๋ฌธ ๊ฐ๊ฒฉ ๊ณ์ฐ
public int getTotalPrice() {
return orderItems.stream()
.mapToInt(OrderItem::getTotalPrice)
.sum();
}
}
// 2
@Entity
public class Order {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List orderItems = new ArrayList<>();
@OneToOne(cascade = CascadeType.ALL)
private Delivery delivery;
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status; // ์: ORDERED, CANCELED
// Getter, Setter๋ง ์กด์ฌ
public void addOrderItem(OrderItem orderItem) {
this.orderItems.add(orderItem);
orderItem.setOrder(this);
}
}
@Service
@Transactional
public class OrderService {
private final OrderRepository orderRepository;
private final MemberRepository memberRepository;
public OrderService(OrderRepository orderRepository, MemberRepository memberRepository) {
this.orderRepository = orderRepository;
this.memberRepository = memberRepository;
}
// ์ฃผ๋ฌธ ์์ฑ
public Long createOrder(Long memberId, Delivery delivery, List orderItems) {
// ํ์ ์กฐํ
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new IllegalArgumentException("ํ์์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
// ์ฃผ๋ฌธ ์์ฑ
Order order = new Order();
order.setMember(member);
order.setDelivery(delivery);
for (OrderItem orderItem : orderItems) {
order.addOrderItem(orderItem);
}
order.setOrderDate(LocalDateTime.now());
order.setStatus(OrderStatus.ORDERED);
// ์ฃผ๋ฌธ ์ ์ฅ
orderRepository.save(order);
return order.getId();
}
// ์ฃผ๋ฌธ ์ทจ์
public void cancelOrder(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("์ฃผ๋ฌธ์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
if (order.getDelivery().getStatus() == DeliveryStatus.COMPLETE) {
throw new IllegalStateException("์ด๋ฏธ ๋ฐฐ์ก ์๋ฃ๋ ์ฃผ๋ฌธ์ ์ทจ์ํ ์ ์์ต๋๋ค.");
}
// ์ํ ๋ณ๊ฒฝ
order.setStatus(OrderStatus.CANCELED);
// ์ฃผ๋ฌธ์ํ ์ทจ์
for (OrderItem orderItem : order.getOrderItems()) {
orderItem.cancel(); // ์ฃผ๋ฌธ์ํ ์ทจ์ ๋ก์ง ํธ์ถ
}
}
// ์ด ์ฃผ๋ฌธ ๊ฐ๊ฒฉ ๊ณ์ฐ
public int getTotalPrice(Long orderId) {
Order order = orderRepository.findById(orderId)
.orElseThrow(() -> new IllegalArgumentException("์ฃผ๋ฌธ์ด ์กด์ฌํ์ง ์์ต๋๋ค."));
return order.getOrderItems().stream()
.mapToInt(OrderItem::getTotalPrice)
.sum();
}
}
๐ ๋ต๋ณ ์์ฝ
๋๋ฉ์ธ ๋ชจ๋ธ ํจํด vs ํธ๋์ญ์ ์คํฌ๋ฆฝํธ ํจํด
๐ฅ ๋๋ฉ์ธ ๋ชจ๋ธ ํจํด์ด๋?๋๋ฉ์ธ ๋ชจ๋ธ ํจํด(Domain Model Pattern)์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋๋ฉ์ธ ๊ฐ์ฒด(์ํฐํฐ) ๋ด๋ถ์ ํฌํจ์ํค๋ ์ค๊ณ ํจํด์ ๋๋ค. ์ฆ, ๋น์ฆ๋์ค ๊ท์น, ๋ฐ์ดํฐ ๋ณ๊ฒฝ, ๋ฐ
dev-jk93.tistory.com
9) em.persist VS em.merge
JPA์์ em.persist ์ em.merge์ ์๋๋ฐฉ์์ ์์ธํ ์ค๋ช ํด์ค
๐ ๋ต๋ณ ์์ฝ
em.persist vs em.merge
package jpabook.jpashop.repository;import jakarta.persistence.EntityManager;import jpabook.jpashop.domain.item.Item;import lombok.RequiredArgsConstructor;import org.springframework.stereotype.Repository;import java.util.List;@Repository@RequiredArgsConstru
dev-jk93.tistory.com
10) JPA ์์๊ด๊ณ ์ ๋ต ์ค์ (@Inheritance(strategy = InheritanceType.โ))
์์๊ด๊ณ์ ๊ฒฝ์ฐ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์์๋
1. ์ํผํ์ & ์๋ธํ์ ํ ์ด๋ธ๋ก ๋๋๊ธฐ
2. ๋จ์ผ ํ ์ด๋ธ
3. ๊ฐ๊ฐ์ ํ ์ด๋ธ
3๊ฐ์ง ํ์ ์ผ๋ก ์ค๊ณ๊ฐ ๊ฐ๋ฅํ์ง?
JPA์์๋ @Inheritance(strategy = InheritanceType.?)๋ก ์ ๋ต์ ์ค์ ํ ์ ์๋ค๊ณ ์๊ณ ์๋๋ฐ ์์ธํ ์ค๋ช ํด์ค
์๋ ์ง๋ฌธ๋ ๋ต๋ณํด์ค.
1. @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)๋ ๋ถ๋ชจ ํด๋์ค๋ฅผ ์ถ์ํด๋์ค (abstract)๋ก ํด์ผํ๋ ์ด์ )
2. JOIN์ ๋ต๊ณผ ๋จ์ผํ ์ด๋ธ ์ ๋ต์ ์ฅ๋จ์ (JOIN์ ์ ๊ทํ๋ก ์ธํด ์ ์ฅ๊ณต๊ฐ ํจ์จํ๊ฐ ์์ง๋ง ์ฟผ๋ฆฌ ์ฑ๋ฅ์ด ๋จ์ด์ง๋ค๋์ง)
3. 'TABLE_PER_CLASS' ์ ๋ต์ ์ฌ์ฉํ๋ฉด ์๋๋ ์ด์
4. 'SINGLE_TABLE' ์ ๋ต์ ๊ฒฝ์ฐ๋ ๋ถ๋ชจํด๋์ค ๋จ๋ ์ผ๋ก ์์ฑ๋ ์ผ์ด ์๊ธฐ ๋๋ฌธ์ ๋ถ๋ชจํด๋์ค๋ ์ถ์ํด๋์ค (abstract)๋ก ํด์ฃผ๋ ๊ฒ์ด ์ข์์ง
11) em.flush(), em.clear() ์๋ฏธ
em.flush(), em.clear()์ ์๋ฏธ
๐ ๋ต๋ณ ์์ฝ
em.flush() → ์์์ฑ ์ปจํ ์ด๋์ ์๋ ์ฟผ๋ฆฌ๋ฅผ DB์ ๋ ๋ฆผ.
em.clear() → ์์์ฑ ์ปจํ ์ด๋๋ฅผ ๋ ๋ฆผ.
11) ์์๊ด๊ณ ์ํฐํฐ์ @DiscriminatorColumn, @DiscriminatorValue
JPA ์์๊ด๊ณ ์ํฐํฐ์์ @DiscriminatorColumn, @DiscriminatorValue๋?
ITEM, BOOK, MOVIE, ALBUM ์ํฐํฐ๋ฅผ ์๋ก ๋ค์ด์ ์ค๋ช ํด์ค
12) @MappedSuperClass
@MappedSuperClass๋?
๊ฐ์๊ธฐ DBA๊ฐ ์์คํ
์ปฌ๋ผ์ ๋ชจ๋ ํ
์ด๋ธ์ ์ถ๊ฐํด์ผํ๋ค๊ณ ํ๋ฉด?
๐ ๋ต๋ณ ์์ฝ
DB๋ ์ํผํ์ &์๋ธํ์ ํํ๋ก ์ค๊ณ๋์ด ์์ง ์์ง๋ง ๊ฐ์ฒด๋ ์์ํํ๋ก ์ฌ์ฉ?
* ํน์ง :
- ์์๊ด๊ณ ๋งคํ X
- ์ํฐํฐ X, ํ ์ด๋ธ๊ณผ ๋งคํ X
- ๋ถ๋ชจ ํด๋์ค๋ฅผ ์์๋ฐ๋ ์์ ํด๋์ค์ ๋งคํ์ ๋ณด๋ง ์ ๊ณต
- ์กฐํ, ๊ฒ์ ๋ถ๊ฐ(em.find(BaseEntity) ๋ถ๊ฐ)
- ์ง์ ์์ฑํด์ ์ฌ์ฉํ ์ผ์ด ์์ผ๋ฏ๋ก ์ถ์ํด๋์ค ๊ถ์ฅ
- ํ ์ด๋ธ๊ณผ ๊ด๊ณ์๊ณ , ๋จ์ํ ์ํฐํฐ๊ฐ ๊ณตํต์ผ๋ก ์ฌ์ฉํ๋ ๋งคํ ์ ๋ณด๋ฅผ ๋ชจ์ผ๋ ์ญํ
- ์ฃผ๋ก ๋ฑ๋ก์ผ, ์์ ์ผ, ๋ฑ๋ก์, ์์ ์์ ๊ฐ์ ์์คํ ์ปฌ๋ผ, ์ฆ ์ ์ฒด ์ํฐํฐ์์ ๊ณตํต์ผ๋ก ์ ์ฉํ๋ ์ ๋ณด๋ฅผ ๋ชจ์ ๋ ์ฌ์ฉ
- ์ฐธ๊ณ : @Entityํด๋์ค๋ ์ํฐํฐ๋ @MappedSuperclass๋ก ์ง์ ํ ํด๋์ค๋ง ์์ ๊ฐ๋ฅ.
13) @SequenceGenerator(์์ฑ), @GeneratedValue(generator = ?)
@Entity @SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq", initialValue = 1, allocationSize = 50)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
private Long id;
}
@SequenceGenerator(์์ฑ), @GeneratedValue(generator = ?) ์ ๋ํด ์์ธํ ์ค๋ช ํด์ค
14) Fetch ์ ๋ต(๊ธฐ๋ณธ๊ฐ EAGER / LAZY)
Member member = em.find(Member.class, 1L);๋ฅผ ํ๋ฉด Member๋ง ์กฐํ๋๋?
@ManyToOne TEAM๋ ๊ฐ์ด ์กฐํ๋๋?
์ ์ง๋ฌธ์ em.find() vs em.getReference() ์ ๊ด๋ จ์ด ์๋?
'JPA' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
JPA @MappedSuperClass (0) | 2024.12.28 |
---|---|
JPA์์ ๋จ๋ฐฉํฅ ๋งคํ์ ์ฐ์ ์ผ๋ก ํ๋ ์ด์ (๋จ๋ฐฉํฅ โ ํ์์ ์๋ฐฉํฅ) (0) | 2024.12.09 |
JPA ์ํฐํฐ ์๋ฐฉํฅ ๊ด๊ณ์์ ์ฐ๊ด๊ด๊ณ ์ฃผ์ธ์ด ํ์ํ ์ด์ (1) | 2024.12.09 |
JPA ์ํฐํฐ์ ๊ฐ์ฒด์งํฅ์ ์ค๊ณ (0) | 2024.12.09 |
JPA PK ์์ฑ์ ๋ต @GeneratedValue(strategy = GenerationType.โ) (0) | 2024.12.09 |
๋๊ธ