[JPA] 4. 엔티티 매핑

최진민·2021년 6월 4일
0

JPA

목록 보기
4/11
post-thumbnail

객체와 테이블 매핑

  • @Entity

    • @Entity가 붙은 클래스는 JPA가 관리
    • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
    • 💋주의
      • 1) 기본 생성자 필수(파라미터가 없는 public or protected 생성자)
      • 2) final 클래스, enum, interface, inner 클래스 x
      • 3) 저장할 필드값에 final x
    • 속성 : name
      • JPA가 사용할 이름
      • 기본값은 클래스 이름과 똑같음
  • @Table

    • @Table : 엔티티와 매핑할 DB의 테이블 지정


DB 스키마 자동 생성

  • DDL(Data Definition Language)을 애플리케이션 실행 시점에 자동 생성

  • 테이블 중싱 → 객체 중심

  • ✨DB 방언을 활용해서 각 DB에 맞는 적절한 DDL 생성 → 생성된 DDL은 개발에서만 사용(운영 x)

  • 속성

    <property name="hibernate.hbm2ddl.auto" value="create" />

    • 속성 값을 create로 변경하고 애플리케이션을 실행했을 때

      print:
      Hibernate: 
          
          drop table Member if exists
      
      Hibernate: 
          
          create table Member (
             id bigint not null,
              name varchar(255),
              primary key (id)
          )
      • 테이블이 있는지 확인하고 다시 만든다.
  • 💥주의

    • 운영 장비에는 절대 create, create-drop, update를 사용하면 안된다.
    • 개발 초기 : create or update
    • 테스트 서버 : update or validate
    • 스테이징, 운영 서버 : validate or none
  • DDL 생성 기능

    • 제약조건 추가

      • ex) 회원 이름이 비어지면 안되고 크기는 10, 테이블의 name 이름을 "USERNAME"

        @Column(name = "USERNAME", nullable = false, length = 10)
        private String name;
    • DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA 실행 로직에는 영향 X


필드와 컬럼 매핑

  • 요구사항 추가

    • 1) 회원은 일반, 관리자로 구분

    • 2) 회원 가입일과 수정일

    • 3) 회원 설명 필드 (길이 제한 X)

      package hellojpa;
      
      import javax.persistence.*;
      import java.util.Date;
      
      @Entity
      public class Member {
      
          @Id
          private Long id;
      
          @Column(name = "name")
          private String username;
      
          private Integer age;
      
          @Enumerated(EnumType.STRING)
          private RoleType roleType;
      
          @Temporal(TemporalType.TIMESTAMP)
          private Date createdDate;
      
          @Temporal(TemporalType.TIMESTAMP)
          private Date lastModifiedDate;
      
          @Lob
          private String description;
      
          @Transient
          private int temp;
          
          protected Member(){
      
          }
      }
  • 정리

    • @Column : 컬럼 매핑

    • @Enumerated : enum 타입 매핑

      • 💥주의 : ORDINAL 사용 X → Why? 순서는 바뀔수가 있기 때문에.
    • @Temporal : 날짜 타입 매핑

      • DATE : 날짜
      • TIME : 시간
      • TIMESTAMP : 날짜, 시간
      • ✨중요 : LocalDate or LocalDateTime 타입이면 해당 애노테이션(@Temporal)이 필요 없다 ^^
    • @Lob : BLOB, CLOB 매핑 (큰 자료)

      • 속성 x
      • 매핑 타입 String = CLOB, 그 외 BLOB
        • CLOB : String, char[], java.sql.CLOB
        • BLOB : byte[], java.sql. BLOB
    • @Transient : 특정 필드 매핑 X, 메모리상에서 임시로 어떤 값을 보관하고 싶을 때 사용


기본 키 매핑

  • 매핑 방법

    • 직접 할당 : @Id

    • 자동 생성(할당) : @GeneratedValue

      @Id
      @GeneratedValue(strategy = GenerationType.AUTO)
      private String id;
  • @GeneratedValue의 전략

    • ⭕AUTO : (defalut) 방언에 따라 자동 지정

    • ⭕IDENTITY

      • 기본 키 생성을 DB에 위임 (DB가 값을 생성)

      • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용 (ex) MySQL의 AUTO_INCREMENT)

      • 예제)

        • Member

          package hellojpa;
          
          import javax.persistence.*;
          
          @Entity
          public class Member {
          
              @Id
              @GeneratedValue(strategy = GenerationType.IDENTITY)
              private String id;
          
              @Column(name = "name", nullable = false)
              private String username;
          
              public String getId() {
                  return id;
              }
          
              public void setId(String id) {
                  this.id = id;
              }
          
              public String getUsername() {
                  return username;
              }
          
              public void setUsername(String username) {
                  this.username = username;
              }
          }
        • JpaMaintry

          try {
          
                  Member member = new Member();
                  member.setUsername("A");
          
                  em.persist(member);
          
                  tx.commit();
              }
          • 기본 키인 id를 생성하는 코드가 없다.
        • DB 결과

          • 자동으로 1이 등록되며, 데이터를 삽입할 때마다 순차적으로 2, 3, 4...의 값이 생긴다.
      • 🧨제약 사항

        • IDENTITY 전략은 DB에 넣기 전까지는 기본 키값을 모른다. (=DB를 통해서만 PK를 확인할 수 있다.)

        • 원래는, 1)em.persist(object)를 통해서 영속성으로 만들고 2)tx.commit()으로 쿼리를 날리지만 IDENTITY 전략은 1)을 통해서 쿼리를 먼저 날린다.

          try {
          
              Member member = new Member();
              member.setUsername("A");
          
              System.out.println("======================");
              em.persist(member);
              System.out.println("member.id = " + member.getId());
              System.out.println("======================");
          
              tx.commit();
          }
          print:
          ======================
          Hibernate: 
              /* insert hellojpa.Member
                  */ insert 
                  into
                      Member
                      (id, name) 
                  values
                      (null, ?)
          member.id = 1
          ======================
          //tx.commit() 시점이 아니라 em.persist(member) 시점에 쿼리를 보내고
          //id(pk)를 받아올 수 있음을 보여준다.
    • ⭕SEQUENCE (Type은 Long 추천)

      • 유일한 값을 순서대로 생성하는 특별한 DB 오브젝트에서 값 생성

      • 주로 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용

      • 전략 - 매핑 (오브젝트(시퀀스) 생성)

        package hellojpa;
        
        import javax.persistence.*;
        
        @Entity
        @SequenceGenerator(
                name = "MEMBER_SEQ_GENERATOR",
                sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
                initialValue = 1, allocationSize = 1)
        
        public class Member {
        
            @Id
            @GeneratedValue(
                    strategy = GenerationType.SEQUENCE,
                    generator = "MEMBER_SEQ_GENERATOR")
            private Long id;
        
            ...
        }
        try {
        
                Member member = new Member();
                member.setUsername("A");
        
                System.out.println("======================");
                em.persist(member);
                System.out.println("member.id = " + member.getId());
                System.out.println("======================");
                
                tx.commit();
            }
        • 실행 시, DB 모습

        • IDENTIY 전략.persist()시, 쿼리가 날라가는 반면에 SEQUENCE 전략.persist()시, SEQUENCE 전략인걸 알고 DB에서 값을 얻어와서 멤버(member.getId())에서 값을 얻어올 수 있다. 그 뒤 .commit()시에 쿼리를 날린다.

          • SEQUENCE : 버퍼링(모았다가 한 번에 쓰기 가능)

          • IDENTITY : X

            print:
            ======================
            Hibernate: 
                call next value for MEMBER_SEQ
            member.id = 1
            ======================
            Hibernate: 
                /* insert hellojpa.Member
                    */ insert 
                    into
                        Member
                        (name, id) 
                    values
                        (?, ?)
      • 속성

        • allocationSize = 50 일 때!!

          try {
          
                  Member member1 = new Member();
                  member1.setUsername("A");
          
                  Member member2 = new Member();
                  member2.setUsername("B");
          
                  Member member3 = new Member();
                  member3.setUsername("C");
          
                  System.out.println("======================");
          
                  em.persist(member1); // 1, 51
                  em.persist(member2); // memory
                  em.persist(member3); // memory
          
                  System.out.println("member1.id = " + member1.getId());
                  System.out.println("member2.id = " + member2.getId());
                  System.out.println("member3.id = " + member3.getId());
          
                  System.out.println("======================");
          
                  tx.commit();
              }
          print:
          ======================
          Hibernate: 
              call next value for MEMBER_SEQ
          Hibernate: 
              call next value for MEMBER_SEQ
          member1.id = 1
          member2.id = 2 (memory)
          member3.id = 3 (memory)
          ======================
          // 밑에는 쿼리를 보낸다.
          // 두 번 호출된 이유는 51번까지 미리 확보하기위해서.
    • ⭕TABLE (실무에서 잘 사용하지는 않는다.)

      • 키 생성 전용 테이블을 하나 만들어 SEQUENCE 흉내

      • 장점 : 모든 DB 적용 가능

      • 단점 : 성능 이슈

      • 전략 - 매핑 (Table 생성)

        • MY_SEQUENCES

          create table MY_SEQUENCES (
           sequence_name varchar(255) not null,
           next_val bigint,
           primary key ( sequence_name )
          )
        • Member

          @Entity
          @TableGenerator(
           name = "MEMBER_SEQ_GENERATOR",
           table = "MY_SEQUENCES",
           pkColumnValue = “MEMBER_SEQ", allocationSize = 1)
          
          public class Member {
           @Id
           @GeneratedValue(strategy = GenerationType.TABLE,
           generator = "MEMBER_SEQ_GENERATOR")
           private Long id;
          }
        • 속성

  • 권장하는 식별자 전략

    • 기본키 제약 조건 : null x, 유일, 변하지 않음
    • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. → 대체키 사용 ex) 주민번호? 기본키 x
    • 🔔권장 : Long + 대체키 + 키 생성전략 사용

예제 1) 요구사항 분석과 기본 매핑

  • 새 프로젝트 생성 후, persistence.xmldependency들 설정새 프로젝트 생성 후, persistence.xml과 dependency들 설정

  • 데이터베이스 파일 생성 방법

    • jdbc:h2:~/jpashop (최초 한번)
    • ~/jpashop.mv.db 파일 생성 확인
    • 이후부터는 jdbc:h2:tcp://localhost/~/jpashop 접속
    • ~ : 해당 경로는 홈 디렉토리(C\Users{username})
    • . : h2 폴더 (H2\bin)
  • 요구사항

    • 회원은 상품 주문
    • 주문 시 여러 종류의 상품 선택 가능
  • 기능

    • 회원
      • 등록
      • 조회
    • 상품
      • 등록
      • 수정
      • 조회
    • 주문
      • 상품 주문
      • 내역 조회
      • 취소
  • 도메인 모델 분석

    • 회원 - 주문 : 회원은 여러번 주문 가능
    • 주문 - 주문 상품 : 주문시, 여러 상품 선택 가능
    • 주문 상품 - 상품 : 같은 상품은 여러번 주문될 수 있다
  • 테이블 & 엔티티 설계

    • 테이블

    • 엔티티

  • 멤버

    @Entity
    public class Member {
    
        @Id
        @GeneratedValue
        @Column(name = "MEMBER_ID")
        private Long id;
    
        private String name;
        private String city;
        private String street;
        private String zipcode;
    }
  • 주문

    @Entity
    @Table(name = "ORDERS")
    public class Order {
    
        @Id
        @GeneratedValue
        @Column(name = "ORDER_ID")
        private Long id;
    
        @Column(name = "MEMBER_ID")
        private Long memberId;
    
        private LocalDateTime orderDate;
    
        @Enumerated(EnumType.STRING)
        private OrderStatus status;
    }
  • 주문상품

    @Entity
    public class OrderItem {
    
        @Id
        @GeneratedValue
        @Column(name = "ORDER_ITEM_ID")
        private Long id;
    
        @Column(name = "ORDER_ID")
        private Long orderId;
    
        @Column(name = "ITEM_ID")
        private Long itemId;
    
        private int orderPrice;
        private int count;
    }
  • 상품

    @Entity
    public class Item {
    
        @Id
        @GeneratedValue
        @Column(name = "ITEM_ID")
        private Long id;
    
        private String name;
        private int price;
        private int stockQuantity;
    }
  • 데이터 중심 설계의 문제점

    • 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
    • 테이블의 외래키를 객체에 그대로 가져옴 (ex) 주문의 memberId
    • 객체 그래프 탐색 불가
    • 참조가 끊겨 UML도 잘못
profile
열심히 해보자9999

0개의 댓글