본문 바로가기

JPA

@MappedSuperclass

이번 시간에는 @MappedSuperclass라는 어노테이션에 대해서 알아보겠다!!
별 거 없다!! 그런데 유용하다!! 편하게 보자~~


우리가 관리하고 있는 어플리케이션에서 테이블에 데이터를 누가 언제 등록했고 수정했는지 관리해야 한다고 해보자!
그런데 이 관리를 특정 테이블이 아닌 거의 모든 테이블에서 해야 한다!
그러면 우리는 각 Entity마다 컬럼을 지정해야 한다..

Student.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
public class Student {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int age;

    @ManyToOne
    @JoinColumn(name = "CLUB_ID", insertable = false, updatable = false)
    private Club club;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    Locker locker;

    @OneToMany(mappedBy = "student")
    private List<StudentSubject> studentSubjects = new ArrayList<>();

    private String creator;

    private LocalDateTime createdAt;

    private String modifier;

    private LocalDateTime lastModifiedAt;

    void addStudentSubject(StudentSubject studentSubject) {
        studentSubject.setStudent(this);
        studentSubjects.add(studentSubject);
    }
}

Club.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
public class Club {

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

    private String name;

    private String description;

    @OneToMany
    @JoinColumn(name = "CLUB_ID")
    private List<Student> students = new ArrayList<>();

    private String creator;

    private LocalDateTime createdAt;

    private String modifier;

    private LocalDateTime lastModifiedAt;
}

지금 Club과 Student Entity에 공통적으로 creator, createdAt, modifier, lastModifiedAt까지 4개의 속성이 겹친다.
이 속성들은 누가 언제 데이터를 생성했고, 수정했는지 관리하는 역할을 한다.
(지금은 Entity 2개라 적어보이지만, 나중에 수십 개의 테이블을 이렇게 관리해야 한다면 수많은 중복이 생길 것이다.)
이럴 때 중복을 제거하기 위해 사용하는 어노테이션이 `@MappedSuperclass`이다.


@MappedSuperclass

package hellojpa;

import lombok.Data;
import org.hibernate.annotations.CreationTimestamp;

import javax.persistence.Column;
import javax.persistence.MappedSuperclass;
import java.time.LocalDateTime;

@Data
@MappedSuperclass
public abstract class BaseEntity {

    @Column(nullable = false)
    private String creator;

    @CreationTimestamp
    private LocalDateTime createdAt;

    private String modifier;

    private LocalDateTime lastModifiedAt;
}

사용방법은 간단하다. 공통 속성들을 하나의 클래스에 모아서 정의해놓는다!
그리고 공통속성들을 모아놓은 class에 @MappedSuperclass 어노테이션을 달아주면 완성이다.
또한 @MappedSuperclass의 class에는 Entity의 필드에 사용하는 어노테이션들을 그대로 사용할 수 있다.
이제 다른 Entity들이 상속받기만 하면 정의된 공통속성들을 가질 수 있다.

지금 보면 기반 클래스가 추상클래스로 선언되어 있다.
기반 클래스는 공통속성을 관리하기 위한 역할만을 하고,
Entity는 아니기때문에 단독으로 사용하지 못하도록 추상클래스로 만드는 것이다.
하지만 추상 클래스가 아니어도 동작하는데에는 지장이 없다.

Student.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
public class Student extends BaseEntity {

    @Id @GeneratedValue
    private Long id;

    private String name;

    private int age;

    @ManyToOne
    @JoinColumn(name = "CLUB_ID", insertable = false, updatable = false)
    private Club club;

    @OneToOne
    @JoinColumn(name = "LOCKER_ID")
    Locker locker;

    @OneToMany(mappedBy = "student")
    private List<StudentSubject> studentSubjects = new ArrayList<>();

    void addStudentSubject(StudentSubject studentSubject) {
        studentSubject.setStudent(this);
        studentSubjects.add(studentSubject);
    }
}

Club.java

package hellojpa;

import lombok.Data;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

@Entity
@Data
public class Club extends BaseEntity {

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

    private String name;

    private String description;

    @OneToMany
    @JoinColumn(name = "CLUB_ID")
    private List<Student> students = new ArrayList<>();

}

Student와 Club class가 @MappedSuperclass 어노테이션을 가진 BaseEntity를 상속받았다.
4개의 공통 속성(creator, createdAt, modifier, lastModifiedAt)도 상속되었고,
이제는 각 Entity마다 공통 속성들을 선언해주지 않아도 된다.

JpaMain.java

package hellojpa;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class JpaMain {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");

        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();

        tx.begin();

        try {
            // programming club 생성
            Club club = new Club();
            club.setName("programming");
            club.setDescription("즐거운 프로그래밍?");
            club.setCreator("code-mania");
            em.persist(club);

            // code-mania student 생성
            Student student = new Student();
            student.setName("code-mania");
            student.setClub(club);
            student.setCreator("code-mania");
            em.persist(student);

            tx.commit();
        } catch (Exception e) {
            tx.rollback();
            e.printStackTrace();
        } finally {
            em.close();
        }

        em.close();
        emf.close();
    }
}

JpaMain.java 실행 후 DB

데이터가 DB에 매우 잘 들어가고 있다.
(CREATEDDATE같은 경우 setter를 통해 지정한 것이 아니라 @CreationTimestamp에 의해 알아서 값이 세팅되었다.)

github 전체코드(branch: MappedSuperclass)

참고강의: 배달의 민족 개발팀장 김영한 강사님의 JPA 강의

'JPA' 카테고리의 다른 글

영속성 전이(CASCADE)와 고아객체  (0) 2021.08.24
단방향 연관관계  (0) 2021.03.09
기본키 매핑  (0) 2021.02.15
DB 스키마 자동 생성  (0) 2021.02.14
객체와 Table 매핑  (0) 2021.02.14