728x90
반응형
SMALL
반응형
SMALL

2024.10.30 업데이트


 

이건 Part 8에서 알아본 상속 관계 매핑이 아니다! (느낌은 비슷하다, 왜냐하면 클래스에서는 extends로 사용되기 때문에 헷갈리긴 하지만 상속관계 매핑은 @Inheritance 애노테이션을 사용할때가 상속 관계 매핑이다.) 어떤 거냐면 테이블들이 공통으로 사용하는 필드들을 편하게 가져다 쓰기 위해 한 클래스에서 그 필드들을 선언하고 필요한 엔티티가 가져다가 사용하는 것을 말한다. 

 

자주 사용되며 아주 대표적인 예시가 createdDate, createdBy, lastModifiedBy, lastModifiedDate같은 필드를 다룰 때이다. 바로 코드를 보자. Part 8에서 사용했던 Item, Movie, Album, Book 테이블을 활용해서 적용해보자. 

 

BaseEntity

package org.example.entity.inheritance;

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

@MappedSuperclass
public abstract class BaseEntity {
    private String createdBy;
    private LocalDateTime createdDate;
    private String lastModifiedBy;
    private LocalDateTime lastModifiedDate;

    public String getCreatedBy() {
        return createdBy;
    }

    public void setCreatedBy(String createdBy) {
        this.createdBy = createdBy;
    }

    public LocalDateTime getCreatedDate() {
        return createdDate;
    }

    public void setCreatedDate(LocalDateTime createdDate) {
        this.createdDate = createdDate;
    }

    public String getLastModifiedBy() {
        return lastModifiedBy;
    }

    public void setLastModifiedBy(String lastModifiedBy) {
        this.lastModifiedBy = lastModifiedBy;
    }

    public LocalDateTime getLastModifiedDate() {
        return lastModifiedDate;
    }

    public void setLastModifiedDate(LocalDateTime lastModifiedDate) {
        this.lastModifiedDate = lastModifiedDate;
    }
}
  • BaseEntity라는 추상클래스를 만들고 공통적으로 사용될 데이터를 모두 작성한 후 이 클래스에 @MappedSuperclass 어노테이션을 추가해준다. 그럼 JPA는 아 이 클래스가 테이블로 만들어지는 클래스가 아니고 메타데이타성 클래스구나를 인지한다.
  • 직접 객체를 만들어 사용할 일도 없고, 엔티티도 아니기 때문에 추상 클래스로 만들기를 권장한다.

 

Item

package org.example.entity.inheritance;

import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item extends BaseEntity {

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

    private String name;
    private int price;

    public Item() {}

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public Long getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }
}
  • 이제 Album, Book, Movie가 상속받는 Item 클래스는 BaseEntity를 부모로 선언한다. 그렇게 되면 나머지 Album, Book, Movie도 모두 가져다가 사용할 수 있게 된다.

 

Main

package org.example;

import org.example.entity.inheritance.Item;
import org.example.entity.inheritance.Movie;

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

public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello");
        EntityManager em = emf.createEntityManager();
        EntityTransaction tx = em.getTransaction();
        
        tx.begin();
        try {
            Movie movie = new Movie("반지의 제왕", 50000, "감독", "배우");
            movie.setCreatedBy("반지의제왕 감독");
            em.persist(movie);

            em.flush();
            em.clear();

            Item item = em.find(Item.class, movie.getId());
            
            System.out.println("Movie item = " + item.getName());
            System.out.println("Movie item createdBy = " + item.getCreatedBy());

            tx.commit();
        } catch (Exception e) {
            System.out.println(e.getMessage());
            tx.rollback();
        } finally {
            em.close();
            emf.close();
        }
    }
}
  • 위 코드처럼 Movie 객체 하나를 추가해서 createdBy 값을 추가하고 DB에 저장한 뒤 DB로부터 데이터를 받아와보자.

실행 결과

Hibernate: 
    call next value for hibernate_sequence
Hibernate: 
    insert 
    into
        Item
        (createdBy, createdDate, lastModifiedBy, lastModifiedDate, name, price, DTYPE, ITEM_ID) 
    values
        (?, ?, ?, ?, ?, ?, 'Movie', ?)
Hibernate: 
    insert 
    into
        Movie
        (actor, director, ITEM_ID) 
    values
        (?, ?, ?)
Hibernate: 
    select
        item0_.ITEM_ID as item_id2_3_0_,
        item0_.createdBy as createdb3_3_0_,
        item0_.createdDate as createdd4_3_0_,
        item0_.lastModifiedBy as lastmodi5_3_0_,
        item0_.lastModifiedDate as lastmodi6_3_0_,
        item0_.name as name7_3_0_,
        item0_.price as price8_3_0_,
        item0_1_.author as author1_1_0_,
        item0_1_.isbn as isbn2_1_0_,
        item0_2_.artist as artist1_0_0_,
        item0_3_.actor as actor1_6_0_,
        item0_3_.director as director2_6_0_,
        item0_.DTYPE as dtype1_3_0_ 
    from
        Item item0_ 
    left outer join
        Book item0_1_ 
            on item0_.ITEM_ID=item0_1_.ITEM_ID 
    left outer join
        Album item0_2_ 
            on item0_.ITEM_ID=item0_2_.ITEM_ID 
    left outer join
        Movie item0_3_ 
            on item0_.ITEM_ID=item0_3_.ITEM_ID 
    where
        item0_.ITEM_ID=?
Movie item = 반지의 제왕
Movie item createdBy = 반지의제왕 감독

 

참고로, 이후에 Spring Data JPA를 같이 사용할땐 이렇게 직접 값을 넣어주지 않아도 알아서 넣어주는 방법이 있다. 지금은 순수 JPA만 사용중이니까 이렇게 직접 createdBy 값을 넣어줬지만.

 

실전 예제

그럼 이제, 상속관계 매핑도 배웠고 @MappedSuperclass도 배웠으니 이걸 사용해서 테이블을 엔티티로 매핑해보는 예제를 다뤄보자.

 

도메인 모델

 

상세

 

이렇게 되어 있을 때, 테이블을 엔티티로 설계해보자.

 

BaseEntity

package cwchoiit.relationship;

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

@MappedSuperclass
public abstract class BaseEntity {

    private LocalDateTime createdDate;
    private LocalDateTime lastModifiedDate;

    private String createdBy;
    private String lastModifiedBy;
}

 

Item

package cwchoiit.relationship;

import javax.persistence.*;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn
public abstract class Item extends BaseEntity {

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

    private String name;
    private Integer price;
    private Integer stockQuantity;
}
  • 상속관계 매핑 전략을 JOINED로 설정했다. SINGLE_TABLE로 해도 무방하다.

Album

package cwchoiit.relationship;

import javax.persistence.Entity;

@Entity
public class Album extends Item {

    private String artist;
    private String etc;
}

 

Book

package cwchoiit.relationship;

import javax.persistence.Entity;

@Entity
public class Book extends Item {

    private String author;
    private String isbn;
}

 

Movie

package cwchoiit.relationship;

import javax.persistence.Entity;

@Entity
public class Movie extends Item {

    private String director;
    private String actor;
}

 

Member

package cwchoiit.relationship;

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

@Entity
public class Member extends BaseEntity {

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

    private String name;
    private String city;
    private String street;
    private String zipCode;

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

 

Orders

package cwchoiit.relationship;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
public class Orders extends BaseEntity {

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

    private LocalDateTime orderDate;
    private Status status;

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

    @OneToOne
    @JoinColumn(name = "DELIVERY_ID")
    private Delivery delivery;
}

 

Delivery

package cwchoiit.relationship;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Delivery extends BaseEntity {

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

    private String city;
    private String street;
    private String zipCode;
    private Status status;
}

 

OrderItem

package cwchoiit.relationship;

import javax.persistence.*;

@Entity
public class OrderItem extends BaseEntity {

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

    private Integer orderPrice;
    private Integer count;

    @ManyToOne
    @JoinColumn(name = "ITEM_ID")
    private Item item;

    @ManyToOne
    @JoinColumn(name = "ORDER_ID")
    private Orders orders;
}

 

Category

package cwchoiit.relationship;

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

@Entity
public class Category extends BaseEntity {

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

    private String name;

    @ManyToOne
    @JoinColumn(name = "PARENT_ID")
    private Category parent;

    @OneToMany(mappedBy = "parent")
    private List<Category> child = new ArrayList<>();
}

 

CategoryItem

package cwchoiit.relationship;

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
public class CategoryItem extends BaseEntity {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne
    @JoinColumn(name = "CATEGORY_ID")
    private Category category;

    @ManyToOne
    @JoinColumn(name = "ITEM_ID")
    private Item item;

    private LocalDateTime created;
}

 

이렇게 구성하면 된다!

728x90
반응형
LIST

+ Recent posts