SMALL

 

JPA의 연관관계 매핑의 다중성 중에 하나인 One To One Mapping에 관해서 알아보고 예시를 보도록 하자.

언뜻 생각하면 일대일 관계에는 방향성과 관계의 주인이 존재하지 않을 것 같지만,

OneToOne 매핑에서도 방향성과 연관관계의 주인이 존재한다.

 

예시로 학생과 수업 Entity를 살펴보자.

학생은 한 개의 수업만 수강할 수 있고, 수업은 한 명의 학생만 수강할 수 있다.

 

이 관계에서 주인을 학생으로 설정해 보자.

( 연관 관계의 주인은 foreign key를 가지고 있다 )

@Getter
@NoArgsConstructor
@Entity
public class Students {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column
    private String name;
    
    @OneToOne
    private Courses course;

    @Builder
    public Students(String name) {
        this.name = name;
    }

    public void update(String name, Courses course) {
    	this.name = name;
    	this.course = course;
    }
}
@Getter
@NoArgsConstructor
@Entity
public class Courses {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column
    private String name;

}

 

 

주인 테이블에 외래키를 가지고 있는 형태

일대일 관계에서 주인을 설정하는 방법으로는 @OneToOne 어노테이션을 붙여주는 것이다. ( Students )

 

방향 관계

이 상태에서 양방향으로 방향을 지정하고 싶은 경우에는

양방향에 해당하는 Entity에 똑같이 @OneToOne 어노테이션을 붙이고

mappedBy라는 설정을 추가하면 된다. ( Courses )

@Getter
@NoArgsConstructor
@Entity
public class Courses {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column
    private String name;
    
    @OneToOne(mappedBy = "course") // 양방향
    private Students student;
}

 

테스트 코드

위의 관계를 확인하는 테스트 코드를 보자.

    @Test
    public void 코스와학생매핑_oneToone() {
    	String studentName = "jun";
    	String courseName = "meth";
    	
    	Courses course = Courses.builder().name(courseName).build();
    	Students student = Students.builder().name(studentName).build();
    	
    	coursesRepository.save(course);
    	student.update(studentName, course);
    	studentsRepository.save(student);
    	
    	Students ss = studentsRepository.findById(student.getId()).get();
    	assertThat(ss.getCourse().getName().equals(courseName));
    	
    }
LIST
SMALL

JPA를 이용한다면 일반적인 테이블 간의 관계를 이용하는 것처럼, 

Entity 사이의 관계를 통해 데이터를 관리할 수 있다.

 

하지만, JPA를 사용하여 연관관계를 맺기 위해서는 참조 변수를 이용하기 때문에

테이블의 연관과 Entity의 연관이 정확하게 일치하지 않는다.

 

이러한 문제를 해결하면서 연관관계를 매핑하는 방법을 알아야 한다.

 

먼저, 연관관계 매핑에 사용되는 용어들을 정리해 보자.

방향 (Direction)

방향 관계: 두 Entity가 관계를 맺을 때, 한쪽의 Entity만 참조하고 있는 것을 의미한다
방향 관계: 두 Entity가 관계를 맺을 때, 양쪽이 서로 참조하고 있는 것을 의미한다

 

다중성 (Multiplicity)

Many To One: 다대일 (N : 1)
One To Many: 일대다 (1 : N)
One To One: 일대일 (1 : 1)
Many To Many: 다대다 (N : N)

 

위의 관계 중에 일대다(1 : N)와 다대일(N : 1)은 같은 것이라고 생각할 수 있다.

예를 들어, 유저가 게시판에 등록하는 글의 관계를 보면 

유저는 여러개의 글을 등록할 수 있어 : 유저와 게시판의 관계는 일대다(1 : N)

게시판에는 여러 유저가 등록한 글이 존재하므로 : 게시판과 유저의 관계는 다대일(N : 1)

 

어떻게 생각하면 같은 것인데, 중요하게 중점을 두어야 하는 것은

"어떤 Entity를 중심으로 상대 Entity를 바라보느냐" 이다.

 

연관 관계의 주인 (Owner)

Entity 사이의 관계에서의 주인을 찾는 방법은 

연관관계를 갖는 두 테이블에 대해서 외래 키(Foreign Key)를 갖는 테이블이 연관관계의 주인이 된다.

연관관계의 주인만이 외래 키(Foreign Key)를 관리할 수 있고, 반면 주인이 아닌 Entity는 읽기만 할 수 있다.

 

( 결국 Entity의 관계를 생각할 때는 이미 설계된 테이블의 내역을 기반으로 관계를 그리는 게 더 이해하기 쉬운 거 같다)

( 그렇기 때문에 데이터베이스에 대한 지식과 학습이 필요하다고 느껴진다.. ㅎㅎ)

LIST
SMALL
  • Entity란
  • Dto란
  • Entity와 Dto를 분리해서 사용해야 하는 이유

JPA에 대해서 학습을 하면서 간단한 Controller을 작성하는 예제를 수행해 보았다. 

간단하게 JPA를 사용하는 로직에서는 굳이 Dto 클래스를 만들지는 않는다. 

JPA 예제 소스에 대한 여러 가지 글들을 찾아서 읽어보는데, Entity와 Dto는 구분해서 사용하는 게 좋다는 글을 보고 해당 내용을 정리해 보도록 하겠다. 

 

먼저 간단하게 Entity, Dto가 무엇인지 알아보자.

Entity

  • JPA에서는 엔티티는 테이블에 대응하는 하나의 클래스 ( 클래스 - 테이블 매핑 관계 )
  • DB에서 영속적으로 저장된 데이터를 자바 객체로 매핑하여 '인스턴스의 형태'로 존재하는 데이터
    ( 객체 - 테이블 로우 매핑 관계 )
  • @Entity, @Column, @Id 등을 이용

Dto 

  • 계층간 데이터 교환이나 View와 데이터를 교환할 때 사용하는 객체
  • DB로부터 데이터를 얻어 Service나  Controller 등으로 보낼 때 사용하는 객체
  • 로직을 갖고 있지 않은 데이터 객체이며, getter/setter 메서드만을 갖는다

위의 두 내용을 간단히 구분하면 Entity는 DB Layer와 데이터 교환을 위한 클래스이고, Dto는 View Layer와 데이터 교환을 위한 클래스이다.


그럼 이 두 가지 클래스를 구분하여서 사용해야하는 이유에 대해서 알아보자. 

  • View와 통신하는 Dto 클래스는 자주 변경이 된다 ( UI 요건에 따라서 )
    하지만, 테이블에 매핑되는 Entity는 그에 비해 변경도 적고, 영향범위도 매우 크다
  • 테이블에 매핑되는 정보가, 실제 View에서 원하는 정보와 다를 수 있다.
    ( 이러한 경우에는 변환하는 로직이 필요한데, 같이 쓰게 되면 해당 로직이 Entity에 들어가게 되어서 Entity가 지저분해진다 )
  • DB로부터 조회된 모든 Entity를 View로 넘기게 되면, 원하지 않는 정보까지 전달하게 되어 정보 노출에 대한 문제가 생길 수 있고, 이를 막기 위한 비즈니스 로직과는 상관없는 방어 로직들이 생기게 된다

 

LIST
SMALL

JPA ( Java Persistence API )

- 자바 ORM 기술에 대한 API 표준 명세

- 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스

 

ORM ( Object Relational Mapping )

- 객체와 관계형 데이터베이스를 매핑하는 기술

  ( DB Data --- Mapping --- Object ) 

- 비교되는 개념으로는 SQL Mapper가 있다

  ( SQL --- Mapping --- Object )

 

이 각각의 개념을 머리속에 생각해보면, 

자바에서 ORM기술을 사용하여 애플리케이션을 짜고 싶으면, 그의 표준인 JPA를 사용하여 작성해야 한다. 

( ORM을 사용하기 위해서는 표준인 JPA가 있지만, SQL Mapper를 사용하기 위해서는 표준이라고 딱히 정의된 부분은 없는 것 같다. 그냥 SQL를 통해 실행된 DB Data를 애플리케이션 레벨에 맞는 객체로 전환한다고 생각하면 될 것 같다.) 

 

위에서 말한것 처럼, JPA는 기술 명세이다.

어떻게 사용할지 가이드만 제공한 것이지, 실제 구현체가 아니다.

단순 인터페이스이다. 

 

그렇다면, 왜 JPA라는 표준까지 정해 놓으면서, ORM을 사용하려 하는 것인가

- DBMS에 대한 종속성을 줄일 수 있다.

- 객체 지향적인 코드로 더 직관적이고 비즈니스 로직에 더 집중할 수 있다.

 

더 나아가 JPA 기술 명세의 구현체인 ORM 프레임워크를 사용하면

개발자가 직접 query를 작성하지 않고, 메서드 호출로 원하는 데이터를 조회할 수 있고, 애플리케이션 레벨의 객체로 전환도 해주는 편리함을 누릴 수 있다. 

( ex : Hibernate ) 

 

LIST

+ Recent posts