- 객체와 테이블 연관관계의 차이를 이해❗ 객체를 테이블에 맞추어 데이터 중심으로 모델링하면, -> 협력 관계를 만들 수 없다 !!
- 객체의 참조와 테이블의 외래키를 매핑
- 용어 이해
- 📝 방향: 단방향, 양방향
- 📝 다중성: 다대일, 일대다, 일대일, 다대다 이해
- 📝 연관관계의 주인: 객체 양방향 연관관계는 관리 주인이 필요
가장 중요하고 기본이 되는 연관관계이다.
객체 지향 모델링 ) Team의 Id가 아니라 Team의 참조값을 그대로 가져왔다.
Member 입장에서는 Many, Team 입장에서는 One이므로 Member 엔티티의 Team 필드에 애노테이션은 @ManyToOne이 된다. 여기서의 필드 Team은 DB의 Member 테이블의 TEAM_ID(FK)와 매핑이 되므로, @JoinColumn 애노테이션을 이용해 어떤 column과 join해야하는지 명시해준다.
객체 지향 모델링을 해주었으므로, 연관관계 저장 및 조회가 다음과 같이 객체지향스럽게 reference로 가지고 오는 것을 통해 진행된다.
//팀 저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);
//회원 저장
Member member = new Member();
member.setName("member1");
member.setTeam(team); //단방향 연관관계 설정, 참조 저장
em.persist(member);
//조회
Member findMember = em.find(Member.class, member.getId());
//참조를 사용해서 연관관계 조회
Team findTeam = findMember.getTeam()
✅ 테이블의 연관관계는 사실상 외래키 하나로 양방향이 다 있는 것이다.
(FK로 join하면 member가 속한 team을 알 수 있고, 반대로 team의 member들도 찾을 수 있음.)
문제는 객체다. 이전 예제에서 봤듯이, team에서는 member를 참조할 수 있는 방법이 없었다.
그래서 위 그림과 같이 team에 members라는 List를 넣어줘야 양쪽에서 서로를 참조할 수 있다.
//조회
Team findTeam = em.find(Team.class, team.getId());
int memberSize = findTeam.getMembers().size(); //역방향 조회
객체와 테이블이 관계를 맺는 차이
❗ 둘 중 하나로 외래키를 관리해야 한다 !
양방향 매핑 규칙
객체의 두 관계 중 하나를 연관관계의 주인으로 지정
연관관계의 주인만이 외래키를 관리(등록, 수정)
⭐ 주인이 아닌쪽은 읽기만 가능
주인은 mappedBy 속성 사용 X
주인이 아니면 mappedBy 속성으로 주인 지정
양방향 매핑시 가장 많이 하는 실수
(연관관계 주인에 값을 입력하지 않음)
양방향 연관관계 주의 - 실습
@Entity
class Member {
...
public void changeTeam(Team team) {
this.team = team;
team.getMembers().add(this);
}
}
연관관계 주인을 정하는 기준
비즈니스 로직을 기준 X, 외래키의 위치를 기준으로 정해야 함
Member 클래스
package hellojpa;
import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;
@Entity
public class Member {
@Id @GeneratedValue
@Column(name = "MEMBER_ID")
private Long id;
private String name;
private String city;
private String street;
private String zipcode;
@OneToMany(mappedBy = "member")
private List<Order> orders = new ArrayList<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public String getZipcode() {
return zipcode;
}
public void setZipcode(String zipcode) {
this.zipcode = zipcode;
}
}
Order 클래스
package hellojpa;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "ORDERS")
public class Order {
@Id @GeneratedValue
@Column(name = "ORDER_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "MEMBER_ID")
private Member member;
@OneToMany
@JoinColumn(name = "ORDER_ITEM_ID")
private List<OrderItem> orderItems = new ArrayList<>();
private LocalDateTime orderDate;
@Enumerated(EnumType.STRING)
private OrderStatus status;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public LocalDateTime getOrderDate() {
return orderDate;
}
public void setOrderDate(LocalDateTime orderDate) {
this.orderDate = orderDate;
}
public OrderStatus getStatus() {
return status;
}
public void setStatus(OrderStatus status) {
this.status = status;
}
public void addOrderItem(OrderItem orderItem) {
orderItems.add(orderItem);
orderItem.setOrder(this);
}
}
OrderItem 클래스
package hellojpa;
import javax.persistence.*;
@Entity
public class OrderItem {
@Id @GeneratedValue
@Column(name = "ORDER_ITEM_ID")
private Long id;
@ManyToOne
@JoinColumn(name = "ORDER_ID")
private Order order;
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
@ManyToOne
@JoinColumn(name = "ITEM_ID")
private Item item;
private int orderPrice;
private int count;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public int getOrderPrice() {
return orderPrice;
}
public void setOrderPrice(int orderPrice) {
this.orderPrice = orderPrice;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
JpaMain 클래스
package hellojpa;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
public class JpaMain {
public static void main(String[] args) {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin(); // 데이터베이스 트랜잭션 시작함
try {
// 실제 애플리케이션 동작 code
Order order = new Order();
order.addOrderItem(new OrderItem());
tx.commit();
} catch (Exception e) {
tx.rollback();
} finally {
em.close();
}
emf.close();
}
}