당우 일기장

JPA 상속 기법: 다중테이블 전략으로 회원 도메인 설계하기 본문

JPA

JPA 상속 기법: 다중테이블 전략으로 회원 도메인 설계하기

당우 2025. 4. 8. 17:26
JPA의 상속 매핑 전략 중
다중테이블 전략(TABLE_PER_CLASS)
를 활용해 회원 도메인을 설계한 과제 내용을 요약해 소개합니다.
학생(Student), 강사(Instructor), 관리자(Admin)로 구분되는 회원 데이터를 각각 독립적인 테이블로 관리하며
코드와 함께 살펴보겠습니다.

 

 

과제 개요

회원 도메인 설계

  • 부모 클래스: User
    • 공통 속성: userId, username, email, passwordHash, createdAt
    • 공통 메서드: login(), updateProfile()
  • 자식 클래스:
    • Student: 특화 속성 studentId (학번)
    • Instructor: 특화 속성 majorSubject (전공 과목)
    • Admin: 특화 속성 position (직책), department (부서)
  • 상속 전략: TABLE_PER_CLASS
    • 각 역할별 독립 테이블(students, instructors, admins) 생성
    • MySQL 환경 가정, ID는 GenerationType.TABLE로 관리 (수정 시 IDENTITY 권장)

 

 

코드 설명

1. 부모 클래스: User

  • 역할: 모든 회원의 공통 속성과 메서드를 정의.
  • 상속 전략: @Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)로 다중테이블 전략 지정.
  • ID 생성: @GeneratedValue(strategy = GenerationType.TABLE)로 자동 생성 (MySQL에서는 IDENTITY로 변경 가능).

2. 자식 클래스: Student, Instructor, Admin

  • 공통점: User를 상속받아 공통 속성을 물려받음.
  • 특화 속성: 각 클래스마다 독자적인 속성을 추가.
  • 테이블 분리: @Table로 각기 다른 테이블 이름 지정.

3. 실행 클래스: Application

  • 기능: 엔티티 생성, 저장, 조회를 테스트.
  • 수정 포인트: 초기 코드에서 ID를 수동 설정해 detached entity 에러 발생 → ID 수동 설정 제거 후 자동 생성으로 변경.

4. 다중테이블 전략의 특징

  • 장점: 조인 없이 빠른 조회, 독립적인 테이블 관리.
  • 단점: 공통 속성 중복 저장, 다형성 쿼리 시 UNION 비용 발생.

 

1. 부모 클래스: User
 
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE) // MySQL에서는 IDENTITY 추천
    private int id;
    
    private String userId;
    private String username;
    private String email;
    private String passwordHash;
    private LocalDateTime createdAt;

    public User() {}
    
    public User(String username, String userId, String email, String passwordHash, LocalDateTime createdAt) {
        this.username = username;
        this.userId = userId;
        this.email = email;
        this.passwordHash = passwordHash;
        this.createdAt = createdAt;
    }

    public void login() {
        System.out.println(username + " 로그인 시도");
    }

    public void updateProfile() {
        System.out.println(username + "의 프로필 업데이트");
    }

    // Getter, Setter, toString 생략
}
2. 자식 클래스: Student
@Entity
@Table(name = "students")
public class Student extends User {
    private int studentId;

    public Student() {}

    public Student(String username, String userId, String email, String passwordHash, LocalDateTime createdAt, int studentId) {
        super(username, userId, email, passwordHash, createdAt);
        this.studentId = studentId;
    }

    // Getter, Setter, toString 생략
}​
3. 자식 클래스: Instructor
@Entity
@Table(name = "instructors")
public class Instructor extends User {
    private String majorSubject;

    public Instructor() {}

    public Instructor(String username, String userId, String email, String passwordHash, LocalDateTime createdAt, String majorSubject) {
        super(username, userId, email, passwordHash, createdAt);
        this.majorSubject = majorSubject;
    }

    // Getter, Setter, toString 생략
}​
4. 자식 클래스: Admin
@Entity
@Table(name = "admins")
public class Admin extends User {
    private String position;
    private String department;

    public Admin() {}

    public Admin(String username, String userId, String email, String passwordHash, LocalDateTime createdAt, String position, String department) {
        super(username, userId, email, passwordHash, createdAt);
        this.position = position;
        this.department = department;
    }

    // Getter, Setter, toString 생략
}​
5. 실행 클래스: Application
public class Application {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-lecture");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        tx.begin();

        try {
            LocalDateTime now = LocalDateTime.now();
            
            // 엔티티 생성 및 저장
            Student student = new Student("김학생", "student01", "student@example.com", "pass123", now, 20240001);
            em.persist(student);
            
            Instructor instructor = new Instructor("이강사", "instructor01", "instructor@example.com", "pass456", now, "컴퓨터 과학");
            em.persist(instructor);
            
            Admin admin = new Admin("박관리", "admin01", "admin@example.com", "pass789", now, "시스템 관리자", "IT 부서");
            em.persist(admin);
            
            em.flush();
            em.clear();
            
            // 조회 및 메서드 호출
            System.out.println("==== 사용자 정보 조회 ====");
            Student foundStudent = em.find(Student.class, student.getId());
            System.out.println("조회된 학생: " + foundStudent);
            foundStudent.login();
            
            // 나머지 조회 코드 생략
            tx.commit();
        } catch (Exception e) {
            e.printStackTrace();
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}​
JPA의 TABLE_PER_CLASS 전략을 적용하며 상속 구조를 설계해보았습니다.

 

 

'JPA' 카테고리의 다른 글

JPA 기본 뼈대  (0) 2025.04.08
플러시  (0) 2025.04.08
영속성 컨텍스트  (0) 2025.04.08
JPA상속기법(inheritance)싱글테이블,조인테이블,다중테이블  (3) 2025.04.08
JPA 시작하기  (0) 2025.04.07