JPA-06 AssociationMapping

yj k·2023년 4월 10일
0

jpa

목록 보기
6/14

연관관계란?

서로 다른 두 객체가 연관성을 가지고 관계를 맺는 것
데이터베이스 환경에서는 join이라는 구문을 사용하지만 객체 환경에서는 해당 어노테이션을 사용해서 매핑한다.

연관 관계의 분류

  • 방향(Direction)에 따른 분류
    참조에 의한 객체의 연관관계는 단방향이다.
    테이블의 연관 관계는 외래키(FK)를 이용하여 양방향 연관 관계의 특징을 가진다.
    객체간의 연관 관계를 양방향으로 만들고싶을 경우 반대 쪽에서도 필드를 추가해서 참조를 보관하면된다.
    하지만 엄밀하게 이는 양방향 관계가 아니라 단방향 관계 2개로 볼 수 있다.
  1. 단방향 연관 관계
  2. 양방향 연관 관계
  • 다중성(Multiplicity)에 대한 분류
    연관 관계가 있는 객체 관계 혹은 테이블 관계에서 실제로 연관을 가지는(매핑되는) 객체의 수 또는 행의 수에 따라 분류된다.
  1. 1:1(OneToOne) 연관 관계
  2. 1:N(OneToMany) 연관 관계
  3. N:1(ManyToOne) 연관 관계
  4. N:N(ManyToMany) 연관 관계

N:1(ManyToOne) 연관 관계

@JoinColumn

@joniColumn은 외래키를 매핑할 때 사용한다.

@joniColumn 속성
name : 매핑할 외래키의 이름
referencesColumnName : 외래키가 참조하는 대상 테이블의 컬럼명
foreignKey : 외래키 제약 조건을 직접 지정할 수 있으며 테이블 생성 시 사용된다.
unique, nullable, insertable, update, columnDefinition, table : @Column의 속성과 동일하다.

@Entity(name="many_to_one_menu_and_category")
@Table(name="TBL_MENU")
public class MenuAndCategory {
	
	
	@Id									
	@Column(name="MENU_CODE")	
	private int menuCode;
	@Column(name="MENU_NAME")
	private String menuName;
	@Column(name="MENU_PRICE")
	private int menuPrice;
	
	@JoinColumn(name="CATEGORY_CODE")
	private Category category;
    
    ... 생략

@ManyToOne

@ManyToOne은 N:1 관계에서 사용한다.

@ManyToOne 속성
optional : false로 설정하면 연관된 엔터티가 있어야한다.
cascade : 영속성 전이 기능을 사용한다.(연관된 엔터티를 함께 영속성으로 관리한다는 의미)
orphanRemoval : true로 설정하면 고아 객체 제거

N:1 연관관계 객체 삽입 상황에서
insert 구문 commit을 할 경우 flush하며 컨텍스트 내의 영속성 객체를 insert하는 쿼리를 동작시키는데 부모 테이블(TBL_CATEGORY)에 값이 먼저 들어있어야 자식 테이블(TBL_MENU)에 데이터를 넣을 수 있다.
@ManyToOne 어노테이션에 영속성 전이 설정을 해주어야한다.

영속성 전이란?
특정 엔터티를 영속화할 때 연관된 엔터티도 함께 영속화 한다는 의미이다.
cascade=CascadeType.PERSIST를 설정하면 Menu를 저장하기 전에 Category부터 저장하게된다.

영속성 전이가 필요한 필드 위에 적어준다.

@Entity(name="many_to_one_menu_and_category")
@Table(name="TBL_MENU")
public class MenuAndCategory {
	
	
	@Id									
	@Column(name="MENU_CODE")	
	private int menuCode;
	@Column(name="MENU_NAME")
	private String menuName;
	@Column(name="MENU_PRICE")
	private int menuPrice;
	
	@JoinColumn(name="CATEGORY_CODE")
	@ManyToOne(cascade=CascadeType.PERSIST)
	private Category category;
    
    ... 생략

1:N(OneToMany) 연관관계

@OneToMany

@OneToMany 1:N 관계에서 사용한다.
해당 참조할 필드 위에 적어준다.

@Entity(name="one_to_many_category_and_menu")
@Table(name="TBL_CATEGORY")
public class CategoryAndMenu {

	@Id
	@Column(name="CATEGORY_CODE")
	private int categoryCode;
	@Column(name="CATEGORY_NAME")
	private String categoryName;
	@Column(name="REF_CATEGORY_CODE")
	private Integer refCategoryCode;
	
	@JoinColumn(name="CATEGORY_CODE")
	@OneToMany(cascade=CascadeType.PERSIST)
	private List<Menu> menuList;
  • 1:N 연관관계의 경우 객체 조회 시 해당 테이블만 조회하고 연관된 메뉴 테이블은 아직 조회하지 않는다.
CategoryAndMenu categoryAndMenu = entityManager.find(CategoryAndMenu.class, categoryCode);
//해당 테이블만 조회된 상태
  • 출력구문 작성 후, 사용하는 경우 연관 테이블을 조회해오는 동작이 일어난다.
    (실제 필요 시에 연관 테이블을 조회(조인)한다.)
System.out.println(categoryAndMenu);
//출력구문 실행 시 조회가 필요하기 때문에 연관관계 테이블 조회까지 실행된다. 
  • 객체 삽입 시
    부모키가 존재하지 않으면 자식 테이블 값에 넣을 수 없다.

양방향(BiDirection) 연관관계

  • 양방향 연관관계 매핑
    데이터베이스의 테이블은 외래키 하나로 양방향 조회가 가능하지만 객체는 서로 다른 두 단방향 참조를 합쳐서 양방향이라고 한다.
    따라서 두 개의 연관관계 중 연관관계의 주인을 정하고, 주인이 아닌 연관관계를 하나 더 추가하는 방식으로 작성하게 된다.
    반대 방향으로도 access하여 객체 그래프 탐색을 할 일이 많은 경우 양방향 연관관계 매핑을 사용한다.(항상 사용하는 것이 아님)

  • 주인을 정하는 기준
    연관관계의 주인은 외래키(FK)를 가지고 있는 엔터티이다.(여기서는 MENU)

@OneToMany(mappedBy="category")

mappedBy : 연관관계의 주인을 정하기 위해서 연관관계의 주인이 아닌 객체에 mappedBy를 써서 연관관계의 주인 객체의 '필드명'을 매핑 시켜 놓으면 로직으로 양방향 관계를 적용할 수 있다.

@Entity(name="bidirection_category")
@Table(name="TBL_CATEGORY")
public class Category {

	@Id
	@Column(name="CATEGORY_CODE")
	private int categoryCode;
	@Column(name="CATEGORY_NAME")
	private String categoryName;
	@Column(name="REF_CATEGORY_CODE")
	private Integer refCategoryCode;
	@OneToMany(mappedBy="category")
	private List<Menu> menuList;
    
    ...생략
  • 연관관계의 주인의 경우 전과 똑같은 방식으로 연관관계 매핑을 처리하면된다.
    @JoinColumn(name="CATEGORY_CODE")
    @ManyToOne 을 사용
@Entity(name="bidirection_menu")
@Table(name="TBL_MENU")
public class Menu {
	
	
	@Id									
	@Column(name="MENU_CODE")	
	private int menuCode;
	@Column(name="MENU_NAME")
	private String menuName;
	@Column(name="MENU_PRICE")
	private int menuPrice;
	@JoinColumn(name="CATEGORY_CODE")
	@ManyToOne
	private Category category;
	@Column(name="ORDERABLE_STATUS")
	private String orderableStatus;
    
    ...생략

주의 사항!
toString() 오버라이딩 시 양방향 연관관계는 재귀호출이 일어나기 때문에 stackOverFlowError가 발생하게된다.
따라서 재귀가 일어나지 않게 하기 위해서는 엔터티의 주인이 아닌 쪽의 toString을 연관객체 부분이 출력되지 않도록 toString에서 삭제해야한다.

0개의 댓글