JPA/JPA Basic

[JPA] 상속관계 매핑 - JOIN전략, SINGLE_TABLE전략

IT록흐 2023. 6. 7. 17:00
반응형

 

객체지향과 관계형DB의 대표적인 패러다임 불일치가 '상속'이다. 

 

객체지향은 '상속'이 있지만 관계형DB는 '상속' 개념이 없다.

그럼 관계형DB에서 상속을 어떻게 구현할 것인가?

 

1.  JOIN 전략 

2. SINGLE_TABLE 전략

 

 

JOIN 전략

 

JOIN전략은 '외래키'를 이용한 전략이다. 외래키를 이용하면 상속과 비슷한 형태로 구현할 수 있다. 

 

 

 

 

 

학교구성원에는 학생(Student)과 교수(Professor)가 있다. 공통필드는 MEMBER 테이블에 외래키로 JOIN하여 가져온다. 개발자는 JPA에게 구현하고 싶은 전략을 어노테이션으로 알려주면, JPA는 자동으로 전략에 따라 테이블을 구현한다. 

 

MEMBER 엔티티 ( 부모 엔티티 )

@Entity
@Inheritance(strategy = InheritanceType.JOINED) //상속관계 구현 전략 명시
@Data
public class Member {

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

    private String name;
    private String address;
    private String phNum;
    
}

 

@Inheritance 어노테이션의 strategy 속성에 구현전략을 명시하면 Member 엔티티를 상속하는 자식 엔티티들은 JOIN 전략에 맞게 테이블이 구성된다. 

 

STUDENT 엔티티 ( 자식 엔티티 )

@Entity
@Data
public class Student extends Member {

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

    private int Grade;
    private String Major;

}

 

PROFESSOR 엔티티 ( 자식 엔티티 )

@Entity
@Data
public class Professor extends Member{

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

    private String course;
    private int officeNum;

}

 

자식엔티티는 별다른 어노테이션 없이 그저 extends만 추가하면 된다. JPA는 Member를 상속하였음을 읽고 Member 엔티티 상속구현전략에 따라 테이블을 구성한다. 

 

Main클래스 

public class Main {

    public static void main(String[] args) {
        EntityManagerFactory entityManagerFactory = Persistence.createEntityManagerFactory("h2");
        EntityManager entityManager = entityManagerFactory.createEntityManager();
        EntityTransaction tx = entityManager.getTransaction();

        tx.begin();

        try {
            //Professor 객체 생성
            Professor professor = new Professor();
            professor.setName("김이나");
            professor.setCourse("알고리즘");
            professor.setAddress("증설동");
            professor.setOfficeNum(101);
            professor.setPhNum("010-3232-3333");

            //Student 객체 생성
            Student student = new Student();
            student.setName("이지훈");
            student.setAddress("이촌동");
            student.setGrade(3);
            student.setMajor("컴퓨터공학과");
            student.setPhNum("010-1111-2222");
            
            //영속화
            entityManager.persist(professor);
            entityManager.persist(student);
            
            tx.commit();
        }catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        }finally {
            entityManager.close();
        }
    }
}

 

 

Student 엔티티와 Professor 엔티티를 생성하고 영속화하였다. Member 엔티티를 생성하지 않았지만 자동으로 테이블이 구성된다. 그리고 INSERT문이 수행된다.  자식 엔티티 하나 생성하고 영속화하면 INSERT문은 2개가 생성된다.

 

1) 부모테이블 INSERT문

2) 자식테이블 INSERT문

 

 

DB 콘솔에서 생성된 테이블을 보면 다음과 같다.

 

- 부모테이블

 

MEMBER 테이블

 

- 자식테이블

 

PROFESSOR 테이블

 

STUDENT 테이블

 

 

엔티티에 외래키 설정이 없었지만 자동으로 JPA가 넣어주었다. @Inheritance 어노테이션 하나로 상속관계 테이블이 구현되었다. 

 

 

@DiscriminatorColumn

 

MEMBER 테이블은 여러 자식 엔티티의 레코드로 가득해진다. 그러므로 레코드를 자식별로 구분해야 한다. 구분자 역할을 하는 컬럼을 만들려면 @DiscriminatorColumn 어노테이션을 엔티티 선언부에 표시하면 된다. 그러면 JPA가 어노테이션을 읽고 자동으로 구분자 컬럼을 생성한다. 자식엔티티는 @DiscriminatorValue 어노테이션으로 각자의 구분타입명을 설정할 수 있다.

 

@DiscriminatorColumn(name = "MEMBER_TYPE") // 부모엔티티에 사용
@DiscriminatorValue("PROFESSOR_TYPE") // 자식엔티티에 사용
@DiscriminatorValue("STUDENT_TYPE") // 자식엔티티에 사용

 

MEMBER 테이블

PROFESSOR 테이블

STUDENT 테이블

 

MEMBER 테이블에 구분컬럼이 추가되었고 자식 엔티티에서 설정한 구분타입명이 컬럼값으로 들어가 있다. 구분타입명이 있으면 시스템 통합에 유용해진다. 다른 시스템은 PROCESSOR테이블과 STUDENT테이블에 각각 접근하지 않아도 된다. MEMBER테이블에 접근하면 구분컬럼으로 원하는 테이블에 필요에 따라 접근할 수 있다. 

 

 

 

SINGLE_TABLE 전략

 

 

JOIN 전략은 객체지향과 비슷한 구조로 테이블을 구현하기에 시스템 통합에 유용하다. 그러나 데이터 조회시, JOIN이 자주 발생하기에 성능에 문제가 생길 수 있다. 그래서 부모와 자식을 하나로 합친 SINGLE_TABLE 전략이 있다.

 

MEMBER, STUDENT, PROFESSOR가 모두 하나의 테이블로 구현된다. SINGLE_TABLE 전략은 JPA가 상속을 구현하는 디폴트 전략이다. 그러므로 @Inheritance를 굳이 표시 안해도 JPA는 알아서 SINGLE_TABLE 전략으로 상속관계를 구현한다. 

 

MEMBER 엔티티 (부모클래스)

@Entity
@Data
public class Member {

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

    private String name;
    private String address;
    private String phNum;

}

 

MEMBER 테이블

 

테이블은 MEMBER 테이블 하나만 생성된다. 

 

특이점은 2가지가 있다. 

 

1. DTYPE 자동생성 : JOIN전략은 @DicriminatorColumn으로 표시를 해주어야 구분컬럼이 생성되었다. SINGE_TABLE전략은 디폴트로 구분컬럼이 생성된다. JOIN전략은 적어도 외래키로 자식엔티티를 구분할 수 있지만 SINGLE_TABLE 전략은 구분컬럼이 없으면 어떤 레코드가 어떤 자식엔티티의 레코드인지 구분할 수 없다. 

 

2. NULL값이 들어감 : 레코드는 하나의 자식 엔티티의 데이터가 들어간다. 반면 컬럼은 모든 자식의 필드가 통합되어 있다. 그래서 필요없는 컬럼에는 NULL값이 들어간다. 컬럼값에 NULL값이 들어가면 데이터가 무결하지 못하다. SINGLE_TABLE 전략의 단점이다. 

 

SINGLE_TABLE 전략은 테이블 하나의 모든 데이터가 들어가므로 JOIN 연산이 일어나지 않아 성능이 우수하다. 그러나 NULL값이 지나치게 들어가 데이터 무결성을 헤칠 수 있고 테이블의 크기가 방대해질 수 있다. 

 

실무에서 상속을 구현하는 2가지 전략을 알아보았다. 상황에 맞는 알맞는 전략을 구현하는 것이 옳다. 인프런 김영한 강사 말에 따르면, 기본으로 JOIN전략을 구사하되, 단순한 상속관계인 경우 SINGLE_TABLE 전략을 선택하는 것이 좋다 말한다. JOIN에 의한 성능저하는 제어 가능한 수준이고 JOIN전략이 시스템 통합에 좋기 때문이다. 

 

 


 

 

참고자료

 

자바 ORM 표준 JPA 프로그래밍 - 기본편 - 인프런 | 강의

JPA를 처음 접하거나, 실무에서 JPA를 사용하지만 기본 이론이 부족하신 분들이 JPA의 기본 이론을 탄탄하게 학습해서 초보자도 실무에서 자신있게 JPA를 사용할 수 있습니다., - 강의 소개 | 인프런

www.inflearn.com

 

반응형