엔티티 설계 및 관계 매핑

정훈희·2022년 10월 27일
0

Spring

목록 보기
13/24
post-thumbnail
post-custom-banner

참고

엔티티 관계 매핑

JPA는 연관관계에 있는 상대 테이블의 PK를 멤버변수로 갖지 않고, 엔티티 객체 자체를 통째로 참조

예시

// JDBC(Mybatis)
private Integer categoryNo;

// JPA
private Category category

용어

관계의 방향

  • 단방향관계와 양방향관계가 있음

다중성

관계에 있는 두 엔티티는 다음 중 하나의 관계를 갖음

  • Many To One - N : 1
  • One To Many - 1 : N
  • One To One - 1 : 1
  • Many To Many - M : N

연관 관계의 주인

연관 관계를 갖는 두 테이블에 대해서 외래키를 갖는 테이블이 연관관계의 주인이됨

주인만 왜래키를 관리(등록, 수정, 삭제)할 수 있고, 주인이 아닌 엔티티는 읽기만 가능

  • 1:N의 관계에서는 테이블 상으로는 N쪽이 FK를 가지고 있음
  • 그러므로 1쪽이 주인인 형태는 권장하지 않음 image

관계 매핑하기

  • @ManyToOne - 단방향
    @Entity
    @Table(name="category")
    public class Category {
    	@Id
    	@Column(name="no")
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	private Integer no;
    	
    	@Column( name="name", nullable=false, length=100 )
    	private String name;
    
    	// getter , setter 생략
    }
    
    @Entity
    @Table( name="book")
    public class Book {
    	@Id
    	@Column(name="no")
    	@GeneratedValue( strategy = GenerationType.IDENTITY )
    	private Integer no;
    	
    	@Column(name="title", nullable=false, length=200)
    	private String title;
    	
    	@ManyToOne
    	@JoinColumn(name ="category_no")
    	private Category category;
    
    	// getter , setter 생략
    }
    Book 엔티티에서만 Category를 참조중이므로 여기만 ManyToOne 사용 여러 책들이 하나의 카테고리에 속하므로 N:1의 관계 JoinColumn은 FK 매핑시 사용, name 속성에는 매핑할 FK 이름 넣기 Category 엔티티의 no를 FK로 가지므로 name속성을 category_no로 작성 테이블 연관관계 image
  • @OneToMany - 양방향
    @Entity
    @Table(name="category")
    public class Category {
    	@Id
    	@Column(name="no")
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	private Integer no;
    	
    	@Column( name="name", nullable=false, length=100 )
    	private String name;
    
    	// Category는 Book을 List로 가짐
    	// OneToMany에 mappedBy속성을 추가하여 JPA에게 연관관계의 주인이 아님을 알려줌
    	// -> 카테고리에 여러개의 책이 속하므로 OneToMany를 달아주고
    	// books 필드는 category에 의해 매핑되므로, mappedBy="category" 작성
    	// 참고로 "category"는 Book 엔티티에서 Category를 참조할 때 작성한 필드명
    	@OneToMany(mappedBy="category")
    	private List<Book> books = new ArrayList<Book>();
    
    	// getter , setter 생략
    }
    Category는 Book을 List로 가짐 OneToMany에 mappedBy속성을 추가하여 JPA에게 연관관계의 주인이 아님을 알려줌 -> 카테고리에 여러개의 책이 속하므로 OneToMany를 달아주고 books 필드는 category에 의해 매핑되므로, mappedBy="category" 작성
    • 참고로 "category"는 Book 엔티티에서 Category를 참조할 때 작성한 필드명

      ```java
      @ManyToOne
      @JoinColumn(name ="category_no")
      private Category category;
      ```

      테이블 연관 관계는 동일

  • @OneToOne 한 유저는 한 개의 블로그를 만들 수 있는 서비스를 개발한다고 가정 → 1:1관계 두가지 선택지가 있음
    1. 한 유저가 여러개의 블로그를 가질 수 있도록 확장성을 고려하여 블로그에서 유저의 PK를 FK로 갖기

    2. 유저를 조회 했을 때 블로그까지 조회할 수 있도록 유저에서 블로그의 PK를 FK로 갖기

      → 아래 예제는 2번으로 선택(FK를 가지고 있는 테이블인 유저가 주 테이블)

      [단방향]

      @Entity
      @Table(name="user")
      public class User {
      	@Id
      	@GeneratedValue(strategy=GenerationType.AUTO)
      	@Column(name="no")
      	private Integer no;
      	
      	@Column(name="id")
      	private String id;
      
      	@OneToOne
      	@JoinColumn(name="blog_no")
      	private Blog blog;
      }
      
      @Entity
      @Table(name="blog")
      public class Blog {
      	@Id
      	@GeneratedValue(strategy=GenerationType.AUTO)
      	@Column(name="no")
      	private Integer no;
      	
      	@Column(name="name")
      	private String name;
      }

      User 엔티티에서만 Blog 엔티티 참조 → 1:1 단방향

      JoinColumn으로 User에서 Blog의 id를 FK로 매핑

      [양방향]

      @Entity
      @Table(name="blog")
      public class Blog {
      	@Id
      	@GeneratedValue(strategy=GenerationType.IDENTITY)
      	@Column(name="no")
      	private Integer no;
      	
      	@Column(name="name")
      	private String name;
      	
      	@OneToOne(mappedBy="blog")
      	private User user;	
      }

      Blog 엔티티에서도 User 참조

      주인은 User 엔티티이므로 Blog 엔티티의 User필드에 mappedBy 속성으로 “blog” 지정

      테이블 연관 관계

      image

  • ManyToMany 데이터 모델링에서 M:N 관계를 해결 할 때는 두 테이블의 FK를 복합키로 하여 PK를 갖는 테이블을 아래와 같이 따로 생성함 JPA에서는 어노테이션을 달아주면 알아서 중간 테이블 만들어줌 image [단방향]
    @Entity
    @Table(name="user")
    public class User {
    	@Id
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	@Column(name="no")
    	private Integer no;
    	
    	@Column(name="id")
    	private String id;
    
    	@ManyToMany
    	@JoinTable(name="user_product",
    				joinColumns = @JoinColumn(name = "user_no"),
    				inverseJoinColumns = @JoinColumn(name = "product_no"))
    	private List<Product> product = new ArrayList<Product>();
    }
    
    @Entity
    @Table(name="product")
    public class Product {
    	@Id
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	@Column(name="id")
    	private Integer id;
    	
    	@Column(name="name")
    	private String name;
    }
    @JoinTable 어노테이션은 M:N 관계에서 생성할 테이블 명을 정의 name 속성으로 새로 생성할 테이블의 이름을 정의 joinColumns 속성으로 join을 수행할 자신의 칼럼 이름을 작성 inverseJoinColumns 속성으로 join을 수행할 다른 칼럼 이름을 작성 → FK로 product_no 와 user_no를 가진 user_product 라는 중간 테이블이 생성됨 [양방향]
    @Entity
    @Table(name="product")
    public class Product {
    	@Id
    	@GeneratedValue(strategy=GenerationType.IDENTITY)
    	@Column(name="id")
    	private Integer id;
    
    	@Column(name="name")
    	private String name;
    
    	@ManyToMany(mappedBy="products")
    	private List<User> users = new ArrayList<User>();
    
    }
    중간테이블을 따로 정의하기 만약 user와 product 사이에 cart라는테이블 만든다고 하면
    @Entity
    @Table(name="cart")
    @IdClass(CartId.class)
    public class Cart {
    	@Id
    	@ManyToOne
    	@JoinColumn(name="user_no", nullable=false)
    	private User user;
    	
    	@Id
    	@ManyToOne
    	@JoinColumn(name="product_id", nullable=false)
    	private Product item;
    	
    	@Column(name="count", nullable=false)
    	private Integer count;
    }
    
    public class CartId implements Serializable{
    	private Integer user;
    	private Integer product;
    	
    	// getter setter 생략
    }
    이런식으로 Cart 엔티티를 정의하고, User와 Product 엔티티의 PK를 복합키로 정의하기 위해 CartId 라는 클래스를 따로 정의하고 그리고 User와 Product 엔티티의 관계 엔티티인 Cart 엔티티를 정의할 때 @IdClass 어노테이션을 작성
profile
DB를 사랑하는 백엔드 개발자입니다. 열심히 공부하고 열심히 기록합니다.
post-custom-banner

0개의 댓글