자바 ORM 표준 JPA 프로그래밍 | 김영한 | 에이콘출판- 교보ebook

스프링 데이터 예제 프로젝트로 배우는 전자정부 표준 데이터베이스 프레임, <b>★ 이 책에서 다루는 내용 ★</b> ■ JPA 기초 이론과 핵심 원리 ■ JPA로 도메인 모델을 설계하는 과정을 예제 중심

ebook-product.kyobobook.co.kr

소스

https://github.com/rkwhr0010/jpa

 

상속 관계 매핑

RDB 상속이라는 개념이 없다.

모델링 관점에서 슈퍼타입 서브타입 관계가 있다.

 

ORM에서 상속 관계 매핑은 객체의 상속 구조와 DB 슈퍼타입 서브타입 관계를 매핑하는 것을 의미한다.

 

가지 전략

  • 각각의 테이블로 변환
    부모
    테이블, 자식 테이블 외래 키로 조인
    JPA
    에서 조인 전략이라 한다.
  • 통합 테이블로 변환
    하나의
    테이블로 객체 상속 관계 모든 필드를 컬럼으로 만든다.
    JPA
    단일 테이블 전략
  • 서브타입 테이블로 변환
    부모
    타입 테이블은 안만들고 대신, 부모 타입 속성을 서브 타입 마다 가지고 있음
    구현
    클래스 마다 테이블 전략

 

 

 

 

조인 전략

부모 테이블, 자식 테이블을 만들고 사이에 외래 키로 조인한다.

자식 테이블은 부모의 개인 키를 받아 외래 + 기본 키로 사용하는 식별관계가 된다.

객체에선 타입이라는 개념이 존재해 부모 타입, 서브 타입을 구별가능하지만, 테이블엔 그런게 없다. 따라서 타입 구분 컬럼 필요하다.

 

 

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@DiscriminatorColumn(name = "DTYPE") // 기본 값이 DTYPE이다.
@Data
public abstract class Item {
        @Id
        @GeneratedValue
        @Column(name = "item_id")
        private Long id;
        
        private String name;
        private int price;
}
@Entity
@DiscriminatorValue("A")
@Data
public class Album extends Item {


        private String artist;
}
@Entity
@DiscriminatorValue("M")
@Data
public class Movie extends Item {
        private String director;
        private String actor;
}
@Entity
@DiscriminatorValue("B")
@PrimaryKeyJoinColumn(name = "book_id")
@Data
public class Book extends Item{
        private String author;
        private String isbn;
}

 

상속 매핑은 부모 클래스에 해야 한다. @Inheritance 어노테이션으로 설정한다.

전략은 가지

 

@DiscriminatorColumn 어노테이션은 구분 컬럼을 설정한다.

값으로 자식 타입 테이블을 식별한다.

@DiscriminatorValue 어노테이션으로 자식 엔티티가 어떤 구분 값을 가질 설정한다.

@PrimaryKeyJoinColumn 어노테이션으로 자식 테이블의 기본 컬럼 이름을 변경할 있다.

 

 

장점

  • 테이블 정규화
  • 외래 참조 무결성 제약 조건 활용
  • 저장공간 효율적

단점

  • 조회 조인 사용으로 성능 저하 가능성
  • 데이터 저장 INSERT 2

특징

JPA 표준 명세엔 구분 컬럼이 필수지만, 하이버네이트 포함 몇몇 구현체는 구분 컬럼 없이도 동작한다.

 

단점으로 소개된 결과는 테이블이 쪼개진(정규화된) 결과로 파생되는 증상들이다.

 

 



        private static void delete() {
                logic(em -> {
                        Book book = em.find(Book.class, 1L);
                        em.remove(book);
                });
        }


        private static void findAndUpdate() {
                logic(em -> {
                        Book book = em.find(Book.class, 1L);
                        book.setIsbn("2222222");
                        book.setAuthor("현진건");
                        book.setName("운수좋은날");
                });
        }


        private static void save() {
                logic(em -> {
                        Book book = new Book();
                        book.setAuthor("윤흥길");
                        book.setIsbn("111111");
                        book.setPrice(5000);
                        book.setName("아홉켤레의구두로남은사내");
                        
                        em.persist(book);
                });
        }

 

 

 

 

 

단일 테이블 전략



@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE") // 기본 값이 DTYPE이다.
@Data
public abstract class Item {
        @Id
        @GeneratedValue
        @Column(name = "item_id")
        private Long id;
        
        private String name;
        private int price;
}

 

 

 

장점

  • 조인이 없어 조회가 편리하고, 빠르다

단점

  • 자식 엔티티가 매핑한 컬럼은 무조건 null 허용해야 한다.
    논리적으로 자식 엔티티들로 행에 모두 데이터를 채울 없음
  • 단일 테이블이 커질 있다.

 

특징

몇몇 구현체는 구분 컬럼이 없어도 동작 가능하다고 헀지만, 전략은 구분 컬럼이 필수다. @DiscriminatorColumn 필수, 자식 엔티티에 @DiscriminatorValue 없으면, 기본 값으로 자식 엔티티 이름을 사용한다.

 

 

구현 클래스마다 테이블 전략

 

@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
@Data
public abstract class Item {
        @Id
        @GeneratedValue
        @Column(name = "item_id")
        private Long id;
        
        private String name;
        private int price;
}


@Entity
@Data
public class Album extends Item {


        private String artist;
}
@Entity
@Data
public class Book extends Item{
        private String author;
        private String isbn;
}

@Entity
@Data
public class Movie extends Item {
        private String director;
        private String actor;
}

 

 

일반적으로 추천하지 않는 전략, 사용하지

 

장점

  • 서브 타입 구분이 명확
  • not null 제약 조건 가능

단점

  • 여러 자식 테이블을 함께 조회 느리다. UNION 으로 행을 붙여야 한다.
    자식 테이블 통합 쿼리가 어렵다.
    일반 조인을 사용하면 컬럼으로 붙인다.

특징

  • 구분 컬럼이 없다.

 

 

@MappedSuperclass

부모 클래스 속성을 사용하되, 부모 엔티티를 테이블에 매핑하고 싶지 않을 사용한다.

상속 관계를 표현하는 목적이 아닌 단순히 공통의 매핑 정보만 제공할 목적으로 사용한다.

 

 

@MappedSuperclass
@Data
public abstract class BaseEntity {
        @Id
        @GeneratedValue
        private Long id;
        private LocalDateTime reg_date; // 등록일자
        private String reg_id; //등록자 ID
        private LocalDateTime mod_date; // 수정일자
        private String mod_id; // 수정자 ID
}

@Entity
@Data
public class Board extends BaseEntity {
        private String name;
        private String contents;
}



@Entity
@AttributeOverrides({
        @AttributeOverride(name = "id", column = @Column(name = "member_id"))
})
@Data
public class Member extends BaseEntity{
        private String name;
}

 

 

@AttributeOverride 어노테이션을 통해 상속받은 매핑 정보를 재정의할 있다.

 

@MappedSuperclass로 지정한 클래스는 엔티티가 아니므로 영속성 컨텍스트에서 사용할 없다.

보통 인스턴스를 생성하지 못하도록 추상 클래스로 선언한다.

 

@Entity 원래 @Entity 클래스만 상속 받을 있지만, @MappedSuperclass도 상속 받을 있다.

 

복합 키와 식별 관계 매핑

DB 테이블 사이 외래 관계

  • 식별 관계
    외래
    키를 자신의 기본 키로 사용
    이때
    복합 키로 사용되어도 식별 관계이다.
  • 비식별 관계
    외래
    키로만 사용
    null 허용 여부에 따라 추가 분류

 

보통 비식별 관계를 사용하고, 필요한 곳만 식별 관계를 사용한다.

 

복합키 : 비식별 관계 매핑

방법 가지

  • @IdClass
    DB
    가까운 방법
  • @EmbeddedId
    객체지향에 가까운 방법

 

 

@IdClass

 

 



@Entity
@IdClass(MasterId.class)
@Data
public class Master {
        @Id
        @GeneratedValue
        @Column(name = "master_id1")
        private Long id1;
        
        @Id
        @GeneratedValue
        @Column(name = "master_id2")
        private Long id2;
        
        private String name;
}


@EqualsAndHashCode
@AllArgsConstructor
@NoArgsConstructor
public class MasterId implements Serializable{
        private Long id1;
        private Long id2;
}


@Entity
@Data
public class Detail {
        
        @Id
        @GeneratedValue
        @Column(name = "detail_id")
        private Long id;
        
        @ManyToOne
        @JoinColumns({
                @JoinColumn(name = "fk_master_id1", referencedColumnName = "master_id1"),
                @JoinColumn(name = "fk_master_id2", referencedColumnName = "master_id2")
        })
        private Master master;
}

@IdClass 식별자 클래스 조건

  • 속성명이 같아야 한다.
  • Serializable 구현
  • equals, hashCode 재정의
  • 기본 생성자 필수
  • public 클래스

 



@Entity
@IdClass(Master.MasterId.class)
@Data
public class Master {
        @Id
        @GeneratedValue
        @Column(name = "master_id1")
        private Long id1;
        
        @Id
        @GeneratedValue
        @Column(name = "master_id2")
        private Long id2;
        
        private String name;
        
        @EqualsAndHashCode
        @AllArgsConstructor
        @NoArgsConstructor
        public static class MasterId implements Serializable{
                private Long id1;
                private Long id2;
        }


}

참고로 내장 클래스로 사용해도 된다.

 



        private static void find() {
                logic(em -> {
                        Master.MasterId masterId = new Master.MasterId(1L, 2L);
                        em.find(Master.class, masterId);
                        em.find(Detail.class, 1L);
                });
        }




        private static void save() {
                logic(em -> {
                        Master master = new Master();
                        master.setName("마스터");
                        
                        em.persist(master);
                        
                        Detail detail = new Detail();
                        detail.setMaster(master);
                        
                        em.persist(detail);
                        
                });
        }

 

 

 

@EmbeddedId

객체지향적 방법



@Entity
@Data
public class Master {
        @EmbeddedId
        private Master.MasterId id;
        
        private String name;
        
        @EqualsAndHashCode
        @AllArgsConstructor
        @NoArgsConstructor
        @Embeddable
        public static class MasterId implements Serializable{
                @Column(name = "master_id1")
                private Long id1;
                @Column(name = "master_id2")
                private Long id2;
        }
}


@Entity
@Data
public class Detail {
        
        @Id
        @GeneratedValue
        @Column(name = "detail_id")
        private Long id;
        
        @ManyToOne
        @JoinColumns({
                @JoinColumn(name = "fk_master_id1", referencedColumnName = "master_id1"),
                @JoinColumn(name = "fk_master_id2", referencedColumnName = "master_id2")
        })
        private Master master;
}


        private static void find() {
                logic(em -> {
                        Master.MasterId masterId = new Master.MasterId(1L, 2L);
                        em.find(Master.class, masterId);
                        em.find(Detail.class, 1L);
                });
        }




        private static void save() {
                logic(em -> {
                        Master master = new Master();
                        master.setId(new Master.MasterId(1L, 2L));
                        master.setName("마스터");
                        
                        em.persist(master);
                        
                        Detail detail = new Detail();
                        detail.setMaster(master);
                        
                        em.persist(detail);
                        
                });
        }

@GeneratedValue 사용이 불가능해서 제거 직접 키를 할당했다.

 

DB 전혀 바뀌는 없다. 단지 애플리케이션에서 매핑을 어떻게 하냐만 다를

 

 

 

식별자 클래스에 @Embeddable 애노테이션을 붙인다.

식별자 클래스에 직접 기본 키를 매핑한다.

식별자 클래스를 직접 필드로 넣고 @EmbeddedId 애노테이션을 붙인다.

 

식별자 클래스 조건

  • @Embeddable 붙임
  • Serializable 구현
  • equals, hashCode 재정의
  • 기본 생성자 필수
  • public 클래스일

 

영속성 컨텍스트는 식별자를 기반으로 엔티티를 관리한다.

식별자를 비교할 equals, hashCode 사용하므로

식별자 클래스는 반드시 재정의해야 한다.

 

@IdClass vs @EmbeddedId

장단점이 존재하므로, 중요한 것은 하나를 선택하면 일관성 있게 사용해야 한다.

 

 

 

복합 식별 관계 매핑

 

@IdClass 식별 관계

 



@Entity
@Data
public class A {
        @Id
        @Column(name = "a_id")
        private String id;
        private String name;;
}
@Entity
@IdClass(B.BId.class)
@Data
public class B {
        
        @Id
        @ManyToOne
        @JoinColumn(name = "a_id") // 외래 키 식별
        private A a;
        
        @Id
        @Column(name = "b_id")
        private String id;
        private String name;;
        
        @NoArgsConstructor
        @EqualsAndHashCode
        @Data
        public static class BId implements Serializable {
                private String a;  //public A a;
                private String id; //private String id;
        }
}
@Entity
@IdClass(C.CId.class)
@Data
public class C {
        
        @Id
        @ManyToOne
        @JoinColumns({
                @JoinColumn(name = "a_id"),
                @JoinColumn(name = "b_id")
        })
        private B b;
        
        @Id
        @Column(name = "c_id")
        private String id;
        
        private String name;
        
        @NoArgsConstructor
        @EqualsAndHashCode
        @Data
        public static class CId implements Serializable{
                private B.BId b;  //private B b;
                private String id; // private String id;
        }
}

 

 



        public static void main(String[] args) {
                save();
                find();
                emf.close();
        }




        private static void find() {
                logic(em -> {
                        A a = em.find(A.class, "A_ID");
                        
                        BId bId = new B.BId();
                        bId.setA("A_ID");
                        bId.setId("B_ID");
                        
                        B b = em.find(B.class, bId);
                        
                        CId cId = new C.CId();
                        cId.setB(bId);
                        cId.setId("C_ID");
                        
                        C c = em.find(C.class, cId);
                });
        }




        private static void save() {
                logic(em -> {
                        A a = new A();
                        a.setId("A_ID");
                        a.setName("A");
                        em.persist(a);
                        
                        B b = new B();
                        b.setA(a);
                        b.setId("B_ID");
                        b.setName("B");
                        em.persist(b);
                        
                        C c = new C();
                        c.setB(b);
                        c.setId("C_ID");
                        c.setName("C");
                        em.persist(c);
                });
        }

 

 

 

 

@EmbeddedId 식별 관계

@MapsId 사용이 필요

 



@Entity
@Data
public class A {
        @Id
        @Column(name = "a_id")
        private String id;
        private String name;;
}


@Entity
@Data
public class B {
        @MapsId("AId") //private String AId;
        @ManyToOne
        @JoinColumn(name = "a_id")
        private A a;
        
        @EmbeddedId
        private B.BId id;
        private String name;;
        
        @NoArgsConstructor
        @EqualsAndHashCode
        @Data
        @Embeddable
        public static class BId implements Serializable {
                private String AId; //@MapsId("AId")
                @Column(name = "b_id")
                private String id;
        }
}


@Entity
@Data
public class C {
        
        @MapsId("BId") //private B.BId BId;
        @ManyToOne
        @JoinColumns({
                @JoinColumn(name = "a_id"),
                @JoinColumn(name = "b_id")
        })
        private B b;
        @EmbeddedId
        private C.CId id;
        private String name;
        
        @NoArgsConstructor
        @EqualsAndHashCode
        @Data
        @Embeddable
        public static class CId implements Serializable{
                private B.BId BId; //@MapsId("BId")
                @Column(name = "c_id")
                private String id;
        }


}


        public static void main(String[] args) {
                save();
                find();
                emf.close();
        }




        private static void find() {
                logic(em -> {
                        A a = em.find(A.class, "A_ID");
                        
                        BId bId = new B.BId();
                        bId.setAId("A_ID");
                        bId.setId("B_ID");
                        
                        B b = em.find(B.class, bId);
                        
                        CId cId = new C.CId();
                        cId.setBId(bId);
                        cId.setId("C_ID");
                        
                        C c = em.find(C.class, cId);
                });
        }




        private static void save() {
                logic(em -> {
                        A a = new A();
                        a.setId("A_ID");
                        a.setName("A");
                        em.persist(a);
                        
                        B b = new B();
                        b.setA(a);
                        BId bId = new B.BId();
                        bId.setAId("A_ID");
                        bId.setId("B_ID");
                        b.setId(bId);
                        b.setName("B");
                        em.persist(b);
                        
                        C c = new C();
                        c.setB(b);
                        CId cId = new C.CId();
                        cId.setBId(bId);
                        cId.setId("C_ID");
                        c.setId(cId);
                        c.setName("C");
                        em.persist(c);
                });
        }

 

@EmbeddedId @IdClass 다르게 @Id 대신 @MapsId 사용한다.

@MapsId 외래 키와 매핑한 연관관계를 기본 키에도 매핑하겠다는

 

 

@IdClass, @EmbeddedId 엔티티에서 매핑 방식이기에 DB 테이블 생성, 조회 쿼리는 똑같다.

 

비식별 관계 구현

 

 



@Entity
@Data
public class A {
        @Id
        @GeneratedValue
        @Column(name = "a_id")
        private Long id;
        private String name;;
}


@Entity
@Data
public class B {
        @Id
        @GeneratedValue
        @Column(name = "b_id")
        private Long id;
        
        @ManyToOne
        @JoinColumn(name = "a_id")
        private A a;
        
        private String name;;
}


@Entity
@Data
public class C {
        @Id
        @GeneratedValue
        @Column(name = "c_id")
        private Long id;
        
        @ManyToOne
        @JoinColumn(name = "b_id")
        private B b;
        private String name;
}

 

 

비식별 관계에서 복합 키를 만들 필요가 없어 식별 관계 사용 복합 키를 사용한 것과 비교해 쉽다.

 

일대일 식별 관계

일대일 관계는 자식 테이블이 부모 테이블 기본 키를 외래 + 기본 키로 사용한다.

따라서 부모 테이블 기본 키가 복합 키가 아니면, 자식 테이블도 복합 키로 구성하지 않아도 된다.



@Entity
@Data
public class Board {
        @Id
        @GeneratedValue
        @Column(name = "board_id")
        private Long id;
        
        private String title;
        
        @OneToOne(mappedBy = "board")
        private BoardDetail boardDetail;
}


@Entity
@Data
public class BoardDetail {
        @Id
        private Long id;
        
        @MapsId
        @OneToOne
        @JoinColumn(name = "board_id")
        private Board board;
        
        private String content;
}

 

 

식별 컬럼이 하나면 값을 생략해도 된다.

 

 

식별, 비식별 관계의 장단점

DB 관점에서 비식별 관계를 선호하는 이유

부모 테이블의 기본 키를 자식 테이블로 전파한다.

특성 때문에 부모-자식 관계가 반복될 경우 가장 자식 테이블의 기본 키는 상위 모든 부모 테이블의 기본 키를 가지게 된다.

기본 키가 많은 수록 인덱스 부하, 조인 복잡하다.

 

기본 전파 특성 때문에 복합 키를 기본 키로 많들어야 하는 경우가 많다

 

식별 관계에서 기본 키로 자연 키를 사용할 경우가 많다. 경우 시간이 지나 비즈니스 요구사항이 바뀌면, 자식 모두에게 영향을 미쳐 변경이 힘들어 진다.

, 테이블 구조가 유연하지 못하다.

 

객체 관계 매핑 관점에서 비식별 관계를 선호하는 이유

일대일 관계를 제외하고, 식별 관계는 복합 키를 기본 키로 사용해야 한다.

, 기본 설정이 복잡하다.

 

생성 또한, 비식별 관계는 대리 자동 생성을 주로 사용하는 , 식별 관계는 복합 키가 많아 불가능하다.

 

식별 관계 장점으로 자식 테이블이 기본 키가 거대해 특수한 상황에서 조인없이 기본 인덱스로만 조회할 있다.

 

가급적 필수적 비식별 관계를 사용하고 기본 키는 Long 대리 키를 사용하자

필수적 비식별 관계여야 외부 조인을 사용하지 않는다.

 

 

조인 테이블

DB 테이블 연관관계 설계 방법 가지

  • 조인 컬럼 (외래 사용)
  • 조인 테이블 (테이블 사용)
    연관관계를 별도 테이블로 관리
    조인
    연관관계 테이블까지 조인해야 한다.
    다대다 관계를 일대다 - 다대일 관계로 풀기 위한 용도로 주로 사용

 

 

일대일 조인 테이블

 

외래 컬럼 모두에 유니크 제약조건이 필요하다

 



@Entity
@Data
public class Parent {
        @Id
        @GeneratedValue
        @Column(name = "parent_id")
        private Long id;
        private String name;
        
        @OneToOne
        @JoinTable(name = "parent_child",
                joinColumns = @JoinColumn(name = "parent_id"),
                inverseJoinColumns = @JoinColumn(name = "child_id", unique = true)
        )
        private Child child;
}


@Entity
@Data
public class Child {
        @Id
        @GeneratedValue
        @Column(name = "child_id")
        private Long id;
        private String name;
        @OneToOne(mappedBy = "child")
        private Parent parent;
}

 

 

 

 

일대다 조인 테이블

쪽에 속한 CHILD_ID 대한 유니크 제약 조건이 필요하다.

 

 

@Entity
@Data
public class Parent {
        @Id
        @GeneratedValue
        @Column(name = "parent_id")
        private Long id;
        private String name;
        
        @OneToMany
        @JoinTable(name = "parent_child",
                joinColumns = @JoinColumn(name = "parent_id"),
                inverseJoinColumns = @JoinColumn(name = "child_id")
        )
        private List<Child> childs = new ArrayList<>();
}
@Entity
@Data
public class Child {
        @Id
        @GeneratedValue
        @Column(name = "child_id")
        private Long id;
        private String name;
}

 

다대일 조인 테이블



@Entity
@Data
public class Parent {
        @Id
        @GeneratedValue
        @Column(name = "parent_id")
        private Long id;
        private String name;
        
        @OneToMany(mappedBy = "parent")
        private List<Child> childs = new ArrayList<>();
}


@Entity
@Data
public class Child {
        @Id
        @GeneratedValue
        @Column(name = "child_id")
        private Long id;
        private String name;
        
        @ManyToOne(optional = false)
        @JoinTable(name = "parent_child",
                joinColumns = @JoinColumn(name = "child_id"),
                inverseJoinColumns = @JoinColumn(name = "parent_id")
        )
        private Parent parent;
}

 

 

대다대 조인 테이블

 



@Entity
@Data
public class Parent {
        @Id
        @GeneratedValue
        @Column(name = "parent_id")
        private Long id;
        private String name;
        
        @ManyToMany
        @JoinTable(name = "parent_child",
                uniqueConstraints = {@UniqueConstraint(columnNames = {"parent_id", "child_id"})},
                joinColumns = @JoinColumn(name = "parent_id"),
                inverseJoinColumns = @JoinColumn(name = "child_id")
        )
        private List<Child> childs = new ArrayList<>();
}


@Entity
@Data
public class Child {
        @Id
        @GeneratedValue
        @Column(name = "child_id")
        private Long id;
        private String name;
        
        @ManyToMany(mappedBy = "childs")
        private List<Parent> parent = new ArrayList<>();;
}

 

모든 @JoinTable 외래 키를 제외한 추가 컬럼이 필요 사용할 없다.

 

엔티티 하나에 여러 테이블 매핑

@SecondaryTable

엔티티에 여러 테이블을 매핑할 있다.



@Data
@Entity
@Table(name = "Board")
@SecondaryTable(name = "Board_Detail",
        pkJoinColumns = @PrimaryKeyJoinColumn(name = "board_detail_id"))
public class Board {
        @Id
        @GeneratedValue
        @Column(name = "board_id")
        private Long id;
        private String title;
        
        @Column(table = "Board_Detail")
        private String content;
}

 

 

 

@SecondaryTable 사용하지 않는 것이 좋다.

항상 테이블에서 조회하게 되며 최적화하기 힘들다.

각각 테이블에 매핑되었다면, 쪽만 조회 필요 조인으로 조회할 있다.

 

 

 

 

실전 예제

상속 관계 매핑 추가

  • @Inheritance, @DiscriminatorColumn, @DiscriminatorValue
  • @MappedSuperclass

 



@MappedSuperclass
@Data
public class BaseEntity {
        private LocalDateTime createdDate;
        private LocalDateTime lastModifiedDate;
}


@Entity
@Data
public class Member extends BaseEntity{
        @Id
        @GeneratedValue
        @Column(name = "member_id")
        private Long id;
        
        private String name;
        private String city;
        private String strret;
        private String zipcode;
        
        @OneToMany(mappedBy = "member")
        @ToString.Exclude
        private List<Order> orders = new ArrayList<>();
}


@Entity
@Data
public class Delivery extends BaseEntity{
        @Id
        @GeneratedValue
        @Column(name = "delivery_id")
        private Long id;
        
        @OneToOne(mappedBy = "delivery")
        private Order order;
        private String city;
        private String street;
        private String zipcode;
        @Enumerated(EnumType.STRING)
        private DeliveryStatus status;
        
}


@Entity
@Table(name = "orders")
@Data
public class Order extends BaseEntity{
        @Id
        @GeneratedValue
        @Column(name = "order_id")
        private Long id;
        
        @ManyToOne
        @JoinColumn(name = "member_id")
        private Member member;
        
        @OneToMany(mappedBy = "order")
        private List<OrderItem> orderItems = new ArrayList<>();
        
        @OneToOne
        @JoinColumn(name = "delivery_id")
        private Delivery delivery;
        
        private LocalDateTime orderDate;
        
        @Enumerated(EnumType.STRING)
        private OrderStatus status;
        // 연관관계 편의 메소드
        public void setMember(Member member) {
                if (this.member != null) {
                        this.member.getOrders().remove(this);
                }
                this.member = member;
                member.getOrders().add(this);
        }
        public void addOrderItem(OrderItem orderItem) {
                orderItems.add(orderItem);
                orderItem.setOrder(this);
        }
        
        public void setDelivery(Delivery delivery) {
                this.delivery = delivery;
                delivery.setOrder(this);
        }
}


@Entity
@Table(name = "order_item")
@Data
public class OrderItem extends BaseEntity {
        
        @Id
        @GeneratedValue
        @Column(name = "order_item_id")
        private Long id;
        
        @ManyToOne
        @JoinColumn(name = "item_id")
        private Item item;


        @ManyToOne
        @JoinColumn(name = "order_id")
        private Order order;
        
        private int orderPrice;
        private int count;
        
        public void setOrder(Order order) {
                if (this.order != null) {
                        this.order.getOrderItems().remove(this);
                }
                this.order = order;
                order.getOrderItems().add(this);
        }
}


@Entity
@Data
public class Category extends BaseEntity{
        @Id
        @GeneratedValue
        @Column(name = "category_id")
        private Long id;
        
        private String name;
        
        @ManyToMany
        @JoinTable(name = "category_item",
                        joinColumns = @JoinColumn(name = "category_id"),
                        inverseJoinColumns = @JoinColumn(name = "item_id"))
        private List<Item> items = new ArrayList<>();
        
        // 카테고리 계층 구조를 위한 필드들
        @ManyToOne
        @JoinColumn(name = "parent")
        private Category parent;
        
        @OneToMany(mappedBy = "parent")
        private List<Category> child = new ArrayList<>();
        
        // 연관관계 메소드
        public void addChildCategory(Category child) {
                this.child.add(child);
                child.setParent(this);
        }
        public void addItem(Item item) {
                items.add(item);
        }
}


@Data
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "DTYPE")
public abstract class Item extends BaseEntity{
        @Id
        @GeneratedValue
        @Column(name = "item_id")
        private Long id;
        
        private String name;
        private int price;
        private int stockQuantity;
        
        @ManyToMany(mappedBy = "items")
        private List<Category> categories = new ArrayList<>();
}


@Entity
@DiscriminatorValue("A")
@Data
public class Album extends Item{
        private String artist;
        private String etc;
}


@Entity
@DiscriminatorValue("M")
@Data
public class Movie extends Item{
        private String director;
        private String actor;
}


@Entity
@DiscriminatorValue("B")
@Data
public class Book extends Item{
        private String author;
        private String isbn;
}

'개발 > JPA' 카테고리의 다른 글

09 값 타입  (0) 2024.05.20
08 프록시와 연관관계 관리  (1) 2024.05.13
06 다양한 연관관계 매핑  (0) 2024.04.14
05 연관관계 매핑 기초  (1) 2024.03.22
04 엔티티 매핑  (1) 2024.02.26

+ Recent posts