연관 매핑

1. 심플한 테이블 디자인

요구 사항 분석


  • 회원은 상품을 주문할 수 있습니다.
  • 주문 시 여러 제품 유형을 선택할 수 있습니다.

도메인 모델 분석


  • 회원과 주문 간의 관계: 회원은 여러 주문을 할 수 있습니다(일대다).
  • 주문과 상품의 연결 : 하나의 주문으로 여러 상품을 선택할 수 있습니다. 반대로 동일한 상품을 여러 번 주문할 수도 있습니다. 다대다 관계를 일대다 관계로 변환하기 위해 주문한 제품이라는 모델을 만듭니다.


@Entity
public class Member {
	@Id @GeneratedValue //생략하면 AUTO
    @Column(name = "MEMBER_ID")
    private Long id;
    private String name;
    private String city;
    private String street;
    private String zipcode;
    
    //Getter,Setter 생략
}
@Entity
@Table(name = "ORDERS")
public class Order {
	@Id @GeneratedValue 
    @Column(name = "ORDER_ID")
    private Long id;
    
    @Column(name = "MEMBER_ID")
    private Long memberId;
    
    private LocalDateTime orderDate;
    
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    //Getter,Setter 생략
}
public enum OrderStatus {
	ORDER, CANCEL
}
@Entity
public class Item {
	@Id @GeneratedValue 
    @Column(name= "ITEM_ID")
    private Long id;
    
    private String name;
    private int price;
    private int stockQuantity;
    
    //Getter,Setter 생략
}
@Entity
public class OrderItem {
	@Id @GeneratedValue 
    @Column(name= "ORDER_ITEM_ID")
    private Long id;
    
    @Column(name = "ORDER_ID")
    private Long orderId;
    
    @Column(name = "ITEM_ID")
    private Long itemId;
    
    private int orderPrice;
    private int count;
    
    //Getter,Setter 생략
}

테이블 중심 디자인의 문제 = 파트너십을 만들 수 없습니다.


  • 위의 방법은 오브젝트 디자인을 테이블 디자인에 매칭 -> 외래키로 조인하여 관련 테이블을 찾아야 합니다.
  • 반면 객체는 참조를 사용하여 관련 객체를 찾습니다. 테이블의 중앙은 개체 그래프를 검색할 수 없습니다.
  • 참조가 없기 때문에 UML도 유효하지 않습니다.

2. 단방향 연결


@Entity
public class Member {
	@Id @GeneratedValue //생략하면 AUTO
    @Column(name = "MEMBER_ID")
    private Long id;
    private String username;
    
    @ManyToOne
    @JoinColumn(name="TEAM_ID")
    private Team team;
    
    //Getter , Setter 생략
}

구성원(다수) 또는 팀(하나)이므로 구성원 클래스를 기준으로 ManyToOne을 사용합니다.

이렇게 하면 구성원과 일치하는 팀을 검색할 때 팀 ID를 검색할 필요 없이 바로 해당 팀을 검색할 수 있습니다.

//저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Mamner member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);

//위의 식으로는 쿼리가 안나감
// em.persist로 영속성컨텍스트에서 서상하고 아래 find를 실행하면 1차쿼리에서 찾기때문
//확실하게 db내용을 확인하고싶다면 
//em.flush();
//em.clear();

Member findMember = em.find(Memner.class, member.getId());

Team findTeam = findMember.getTeam();//getTeam으로 바로 찾기가능
System.out.println("findTeam = "+findTeam.getName());

tx.commit();

또한 관계에 있을 때 쉽게 변경할 수 있습니다.

//새로운 팀B
Team teamB = new Team();
teamB.setUsername("TeamB");
em.persist(teamB);

//회원1에 새로운 팀 B 설정
member.setTeam(teamB);

3. 양방향 관계 및 관계의 소유자


여기서 테이블과 관련하여 외래 키를 사용하여 구성원 테이블에서 팀을 가져오거나 팀 테이블에서 구성원을 가져올 수 있습니다. 즉, 테이블 연결은 이미 양방향 관계입니다.

단, 오브젝트 링크에서는 구성원 테이블에서 팀을 호출할 수 있지만 팀에 구성원이 없기 때문에 팀 테이블에서 구성원을 호출할 수 없습니다.

이때 필요한 것이 양방향 매핑이다.

@Entity
public class Team {

 @Id @GeneratedValue
 private Long id;
 
 private String name;
 
 @OneToMany(mappedBy = "team")
 List<Member> members = new ArrayList<Member>();
 
}

mappedBy는 연결할 개체의 변수 이름을 씁니다.

그리고 일반적으로 new ArrayList<>(); 그것을 초기화

이렇게 하면 팀원을 호출할 수도 있습니다.

//저장
Team team = new Team();
team.setName("TeamA");
em.persist(team);

Mamner member = new Member();
member.setUsername("member1");
member.setTeam(team);
em.persist(member);

//db에서 값을 가져와야 하므로 아래식은 필수이다.
em.flush();
em.clear();

//양방향 호출
Member findMember = em.find(Memner.class, member.getId());
List<Member> members = findMember.getTeam().getMembers();

for(Member m : members){
	System.out.println("m = "+ m.getUsername());
    }

Team findTeam = findMember.getTeam();//getTeam으로 바로 찾기가능
System.out.println("findTeam = "+findTeam.getName());

tx.commit();

지도의 소유자와 mappedBy


  • 개체의 양방향 관계는 실제로 양방향 관계가 아니라 두 가지 다른 단방향 관계입니다.
  • 객체를 양방향으로 참조하려면 두 개의 단방향 연결을 만들어야 합니다.
  • A -> B (a.getB())
  • B -> A (b.getA())
  • 반면 테이블은 단일 외래 키로 두 테이블 간의 관계를 관리합니다.
  • SELECT * JOIN MEMBER M TEAM T T ON M.TEAM_ID = T.TEAM_ID
  • SELECT * TEAM T JOIN MEMBER M ON T.TEAM_ID = M.TEAM_ID

따라서 개체의 외래 키를 업데이트하려면 어떤 개체에서 업데이트해야 합니까?


=> 둘 중 하나만 외래 키를 관리해야 합니다. => 관계의 소유자

(양방향 매핑 규칙)

  • 개체의 두 관계 중 하나를 연결 소유자로 지정합니다.
  • 협회의 소유자만이 외래키를 관리(등록, 변경)할 수 있습니다.
  • 비소유자에 의해 할당 페이지) 읽기(검색)만 가능합니다.
  • 소유자는 mappedBy 속성을 사용하면 안 됩니다.
  • 소유자가 아닌 경우 mappedBy 속성으로 소유자를 지정합니다.

누가 이것을 소유하고 있습니까?


  • 외래 키를 호스트로 설정해야 합니다.
  • 무조건적인 다대일 대 다목적 테이블
  • 예) Member 및 Crew 테이블의 경우 구성원이 소유자여야 하며 Cars 및 Wheels 테이블의 경우 Wheel 테이블이 소유자여야 합니다.