[JPA] JPA 연관관계 매핑하기

 

 

JPA를 이용하여 기본 객체와 관계형 데이터베이스를 엔티티로 매핑시켰다면 각 엔티티간의 연관관계를 매핑 시켜야 한다.

 

2021.08.31 - [Spring-Boot/JPA] - [JPA] JPA 엔티티 매핑과(Entity Mapping) 엔티티 설계

 

[JPA] JPA 엔티티 매핑과(Entity Mapping) 엔티티 설계

[JPA] JPA 엔티티 매핑(Entity Mapping) 엔티티 설계 JPA에서 가장 기본적이지만 중요한 두 가지 개념을 뽑아보자면 영속성 컨텍스트 객체와 관계형 데이터베이스(RDB) 매핑 이 두가지가 아닐까 한다

dbsyys.tistory.com

 

객체와 테이블을 매핑시켜 엔티티로 사용할 수 있지만 객체와 테이블이 연관관계를 맺는 형식에 차이가 있기때문에 우리는 둘 중에 어느 쪽으로 모델링을 해야한다.

 

객체와 테이블의 연관관계 형식을 보자면

 

 

(객체 연관관계)

 

 

(테이블 연관관계)

 

차례로 객체 연관관계와 테이블 연관관계를 다이어그램으로 나타냈다. 회원과 주문은 1:N 관계를 가지고 있다. 회원의 아이디 값과 주문의 아이디 값으로 서로 연관관계가 정의 되어있다. 

 

언뜻보면 맞는 관계인것 같지만 위의 다이어그램에 객체 연관관계에는 문제가 있다. 테이블은 연관관계를 지정할 때 Join을 이용하여 연관관계를 지정한다. 회원 테이블의 외래키(ORDER_ID)로 주문 테이블의 기본키(ORDER_ID)를 가질 수 있는 것이다. 이렇게 되면 테이블간의 연관관계가 성립된다.

 

하지만 객체 연관관계에서는 Join을 이용 할 수 없다. 회원의 주문아이디(orderId) 값과 주문의 아이디(id) 값은 따로 존재한다.

 

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    private Long id; // 회원 id

    private String Mname;

    private Long orderId; // 주문 id

}

 

이렇게 되면 회원 객체의 값이 변경 되었을 때 주문 객체의 값 또한 변경되어야 하고 그 반대의 경우도 마찬가지이다. 이건 객체지향적이라고 할 수 없다. 하지만 객체는 참조를 이용하여 연관관계를 지정할 수 있다.

 

 

(올바른 객체 연관관계)

 

회원 객체는 주문객체의 아이디를 따로 생성하는 것이 아니라 주문 객체를 하나의 필드로 참조하는 것이다. 이렇게 하면 객체 지향적으로 모델링을 할 수 있다.

 

하지만 테이블과 객체의 연관관계 매핑 방식의 큰 차이가 있으므로 JPA를 이용하여 객체 연관관계와 테이블 연관관계의 연관관계를 매핑할 것이다. 


 

연관관계 매핑에 있어서 연관관계는 3가지의 큰 특징을 가진다.

 

  • 방향(Direction) : 단방향, 양방향
  • 다중성(Multiplicity) : 다대일(N:1), 일대다(1:N), 일대일(1:1), 다대다(N:N)
  • 연관관계의 주인(Owner) : 객체의 양방향 연관관계 시 연관관계 주인이 필요

 

위에서 계속 언급했듯 객체와 테이블은 관계를 맺는차이가 있다. 객체의 연관관계 매핑에 방향성이 있는 이유는 

- 객체 연관관계

  • 회원 -> 주문 연관관계 (단방향)
  • 주문 -> 회원 연관관계 (단방향)

- 테이블 연관관계

  • 회원 <-> 주문 연관관계 (양방향)

의 차이가 있기 때문이다. 객체의 양방향 관계는 객체의 서로 다른 단방향 관계가 각각 있는 것이다. 객체를 단방향 연관관계만 지정해도 연관관계 매핑은 완료된 것이나 양방향 매핑을 하게되면 연관관계의 주인으로 지정하지 않은 반대 방향으로 조회가 가능하다(객체 그래프 탐색, *역 방향은 조회만 가능하다)

 

아래는 양방향 연관관계 매핑을 한것이다.

 

 

(객체의 양방향 연관관계)

 

 

@Entity
public class Member {

    @Id
    @GeneratedValue
    @Column(name = "member_id")
    private Long id;

    private String Mname;

    @OneToMany(mappedBy = "member")
    private List<Order> orders = new ArrayList<Order>();

}
@Entity
@Table(name = "orders")
public class Order {

    @Id
    @GeneratedValue
    @Column(name = "order_id")
    private Long id;

    private String OName;

    @ManyToOne
    @JoinColumn(name = "member_id")
    private Member member;   

}

 

@ManyToOne

  • 다대일(N:1) 관계를 매핑할 때 사용한다. (@JoinColumn과 함께 사용)

@OneToMany

  • 일대다(1:N) 관계를 매핑할 때 사용한다. mappedBy 라는 속성을 통해 다(N)객체와 연관관계를 매핑한다.

@JoinColumn

  • @ManyToOne과 함께 사용하며 name 속성을 사용하여 일(1)객체와 연관관계를 매핑한다.

 

객체의 연관관계를 양방향으로 설정했다면 연관관계의 주인(Owner)을 정해야한다. 연관관계 주인은 객체의 두 관계 중 하나를 연관관계의 주인으로 지정하고 연관관계의 주인만이 외래 키를 관리(등록, 수정) 할 수 있다. 주인이 아닌 역방향은 조회(읽기)만 가능하다. 실제로 사용하다보면 역방향으로 탐색할 일이 많기 때문에 양방향 매핑을 사용한다.

 

위에서 본 속성들 중에서 주인은 mappedBy 속성을 사용하지 않고 주인이 아닌 속성에 mappedBy 속성을 지정한다. 연관관계의 주인은 외래 키(FK)의 위치를 기준으로 정해야 한다. 양방향 연관관계에서는 양 쪽에 값을 설정하고 연관관계에 관련된 편의 메소드를 생성하는 것을 권장한다. 또한 양방향 매핑 시에 무한루프에 빠질 수 있다는것을 주의하자.

 

 

[JPA] JPA 엔티티 매핑(Entity Mapping) 엔티티 설계

 

JPA에서 가장 기본적이지만 중요한 두 가지 개념을 뽑아보자면

 

  • 영속성 컨텍스트
  • 객체와 관계형 데이터베이스(RDB) 매핑

 

이 두가지가 아닐까 한다.

 

 

영속성 컨텍스트에 관련된 내용은 아래 링크에 정리해보았다.

 

2021.08.19 - [Spring-Boot/JPA] - [JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context)

 

[JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context)

[JPA] 영속성(Persistence) 관리와 영속성 컨텍스트(Persistence Context) 지난번에는 JPA와 관련하여 기본적인 세팅과 EntitiyManagerFactory, EntitiyManager, EntitiyTransaction의 특징과 어떤 역할을 하..

dbsyys.tistory.com


 

이번엔 객체와 테이블 매핑을 할 때 사용하게되는 속성들을 정리해보겠다.

 

먼저 엔티티 매핑이란 객체와 테이블 사이의 관계와 속성을 지정할 수 있도록 매핑하는 것이다.

 

관련된 어노테이션을 사용해서 테이블과 클래스나 필드와 속성 등을 매핑할 수 있다.

 

@Entity
@Table(name = "TBMember")
public class Member {

    @Id
    @GeneratedValue
    private Long id;
    
    @Column(name = "name")
    private String username; 
 	
    @Enumerated(EnumType.STRING) 
    private RoleType roleType; 
 	
    @Temporal(TemporalType.TIMESTAMP) 
    private Date createdDate; 
 	
    @Lob 
    private String description;

}

@Entity

  • 해당 어노테이션이 붙은 클래스는 JPA에 관리되어지며 엔티티라고 한다. JPA를 사용하기 위해선 필수적으로 사용해야한다. (*기본 생성자 필수)

@Table

  • 엔티티와 매핑할 테이블을 지정한다. name 속성을 통해 매핑할 테이블 이름을 지정 할 수있다. ex) 엔티티 이름을 Order이라고 한다면 Oracle에선 order by 예약어와 중복되어 테이블 명으로 설정 할 수 없기때문에 변경 해야함.

@Id

  • 해당 필드를 기본 키로 매핑한다. (직접 할당 하는 방식)

@GeneratedValue

  • 해당 필드를 기본 키로 매핑한다. (자동 생성 하는 방식, @Id 와 같이 사용)
  • strategy 속성을 이용하여 생성되는 타입을 정할 수 있다. default 값은 Auto이며 sql 방언에 따라 자동 지정된다.

@Column

  • 필드와 컬럼의 매핑 속성을 지정한다. 컬럼 이름이나 변경 여부, null 여부, unique, 길이 등의 제약조건을 설정할 수 있다.

@Enumerated

  • 자바의 enum 타입을 매핑할 때 사용한다.
  • 속성타입으로 ORDINAL과 STRING이 있는데 꼭 STRING을 사용하길 권장한다. ORDINAL은 enum 순서를 DB에 저장하고 STRING은 이름으로 값을 저장하는데 enum 타입이 변경 되었을 시 ORDINAL은 순서로 저장하기 때문에 데이터가 꼬일 수 있다.

@Temporal

  • 날짜 타입을 매핑 할 때 사용한다. 속성 타입으로 DATE(날짜), TIME(시간), TIMESTAMP(날짜와 시간) 등 이 있다.
  • Date 객체를 쓴 이유는 하이버네이트가 최신버전이라면 LocalDate나 LocalDateTime 객체를 사용 시 해당 어노테이션을 생략 할 수있다.

@Lob

  • 데이터베이스의 BLOB, CLOB 타입과 매핑할 때 사용한다. (속성은 없으며 문자타입은 CLOB이고  나머지는 BLOB이다.)
  • CLOB: String, char[] / BLOB: byte[]

 

JPA는 데이터베이스의 스키마(DDL)를 애플리케이션 실행 시점에 자동으로 생성할 수 있다. 기존의 테이블 중심의 설계에서 객체 중심의 설계로 바꾸는 것이다. 각 데이터베이스의 특징이 있는 방언별로 적절한 DDL을 생성한다. 하지만 이렇게 생성한 DDL은 개발 서버에서만 사용하거나 초기 세팅 시에만 사용할 것을 권장한다. 운영서버에서는 적절히 수정한 후에 사용해야 한다.

 

데이터베이스 스키마를 자동생성은 설정파일인 프로퍼티(application.properties)나 야믈(application.yml)에서 설정 하면 된다.

 

* application.properties

spring.jpa.hibernate.ddl-auto=create

 

* application.yml

jpa:
  hibernate:
    ddl-auto: create

 

해당 설정파일 create부분에 들어갈 속성들로 5가지가 있다.

 

  • create : 기존 테이블 삭제 후 다시 생성(DROP 후 CREATE)
  • create-drop : create와 같으나 종료 시점에 모든 테이블 DROP
  • update : 변경분만 반영
  • validate : 엔티티와 테이블이 정상 매핑되었는지만 확인
  • none : 사용 안함

 

위에서 언급했듯이 운영서버에는 create, create-drop, update는 사용하지 않을것을 권장한다.

 

- 개발 초기 : create 또는 update

- 테스트 서버 : update 또는 validate

- 스테이징 서버나 운영 서버 : validate 또는 none

 

+ Recent posts