ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 엔티티 매핑
    JPA 2022. 7. 26. 11:44
    더보기

    해당 내용은

    자바 ORM 표준 JPA 프로그래밍 - 기본편 (https://www.inflearn.com/course/ORM-JPA-Basic/dashboard)

    강의를 듣고 정리한 포스트입니다.

     JPA 중요한 2가지

    1. 영속성 컨텍스트, JPA 동작 방식 매커니즘
    2. 실제 설계적측면 , 객체와RDB 어떻게 매칭해서 쓸건지

     

    객체와 테이블매핑

     

     

    엔티티 매핑 소개

    • 객체와 테이블 매핑: @Entity, @Table

    • 필드와 컬럼 매핑: @Column

    • 기본 키 매핑: @Id

    • 연관관계 매핑: @ManyToOne,@JoinColumn

    ------------------------------------------------------------------------------------------------

    객체와 테이블 매핑

    ------------------------------------------------------------------------------------------------

    @Entity

    • @Entity가 붙은 클래스는 JPA가 관리, 엔티티라 한다.

    • JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수

    • 주의

    • 기본 생성자 필수(파라미터가 없는 public 또는 protected 생성자)

    • final 클래스, enum, interface, inner 클래스 사용X

    • 저장할 필드에 final 사용 X

    ------------------------------------------------------------------------------------------------

    @Entity 속성 정리

    • 속성: name

    • JPA에서 사용할 엔티티 이름을 지정한다.

    • 기본값: 클래스 이름을 그대로 사용(예: Member)

    • 같은 클래스 이름이 없으면 가급적 기본값을 사용한다

     

    ----------------------------------------------------------------------------------------------------

    @Table

    • @Table은 엔티티와 매핑할 테이블 지정

     

    --------------------------------------------------------------------------------------------------------------------------------------

     

    데이터베이스 스키마 자동 생성

     

    데이터베이스 스키마 자동 생성

    • DDL을 애플리케이션 실행 시점에 자동 생성

    • 테이블 중심 -> 객체 중심

    • 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한

    DDL 생성

    • 이렇게 생성된 DDL은 개발 장비에서만 사용

    • 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬

    은 후 사용

     

    • JPA 애플리케이션이 기동될때 맴핑된 객체에 맞게 DataBase 생성해주기도 한다.
    • 그러나 운영에서는 쓰지않는다.  해당 기능으로 생성 적절히 다듬거나 해서 따로 적용할
    • 개발에서만 DDL 사용

    --------------------------------------------------------------------------------------------------------------------------------------

     

    데이터베이스 스키마 자동 생성 - 속성

    hibernate.hbm2ddl.auto

    ---------------------------------------------------------------------------------------------------------------

     

    데이터베이스 스키마 자동 생성 - 실습

    • 스키마 자동 생성하기 설정

    • 스키마 자동생성하기 실행, 옵션별 확인

    • 데이터베이스 방언 별로 달라지는 것 확인(varchar)

     

    ---------------------------------------------------------------------------------------------------------------

     

    데이터베이스 스키마 자동 생성 - 주의

    • 운영 장비에는 절대 create, create-drop, update 사용하면

    안된다.

    • 개발 초기 단계는 create 또는 update

    • 테스트 서버는 update 또는 validate

    • 스테이징과 운영 서버는 validate 또는 none

     

    실제 데이터가 많은 DB alter 시스템이 중단상태가 된다.

     

    운영서버 반영시에도

    해당 스크립트를 고민하고 다듬어서 반영한다

     

    ---------------------------------------------------------------------------------------------------------------

    DDL 생성 기능

     

    • 제약조건 추가: 회원 이름은 필수, 10자 초과X

    @Column(nullable = false, length = 10)

    • 유니크 제약조건 추가

     @Table(uniqueConstraints = {@UniqueConstraint( name = "NAME_AGE_UNIQUE",
    columnNames = {"NAME", "AGE"} )})

    • DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.

     

    ==========================================================================================

    필드와 컬럼 매핑

    요구사항 추가

    1. 회원은 일반 회원과 관리자로 구분해야 한다.

    2. 회원 가입일과 수정일이 있어야 한다.

    3. 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.

    jdbc:h2:tcp://localhost/D:/Study/spring_jpa_basic/h2_db_home

     

    RoleType
    USER, ADMIN

     

     

        //PK 설정
        @Id
        private Long id;
        // 만약에 변수값이 username  인데 컬럼값은 name 경우 다음같이 @Column 써준다.
        @Column(name = "name")
        private String name;
       //Integer 맞는 숫자타입이 설정된다.
        private Integer age;
       //EnumType 쓰는 경우 ,
        @Enumerated(EnumType.STRING)
        private RoleType rolType;
        //날짜 타입을 쓰는 경우 , DATE, TIME , TIMESTMAP ,  DB에는 날짜, 시간, 날짜 시간 따로
        @Temporal(TemporalType.TIMESTAMP)
        private Date createDate;


        @Temporal(TemporalType.TIMESTAMP)
        private Date lastModifiedDate;
      // CLaB, Blob
        @Lob
        private String description;

     

    nibernate.hbm2ddl.auto 설정값을 true 설정하면 다음과 같이 나온다.

        create table Member (
           id bigint not null,
            age integer,
            createDate timestamp,
            description clob,
            lastModifiedDate timestamp,
            name varchar(255),
            rolType varchar(255),
            primary key (id)
        )

     

    매핑 어노테이션 정리

    hibernate.hbm2ddl.auto

     

    @Transient 특정 필드를 컬럼에 매핑하지 않음(매핑 무시)

    DB 저장하지 않고 메모리에만 캐시 데이터를 가지는 경우

     

     

     

    @Column

     

    만약에 해당 컬럼을 등록만 실시하고 , 수정은 허용하지 않는 경우는 다음과 같이 설정한다.

    기본 , insertable updatable  true 이다.(둘다허용함 , 제한을 두고싶은 경우만 false 설정한다.)

     

    notnull  = false 하는 경우 null 허용하지 않는다.

    해당 컬럼에 notnull 제약이 걸린다.

    유니크 제약 걸기 해당값에 유니크 제약을 걸어줌

    근데 잘안 쓴다고 한다

    왜냐 유니크 이름이 UK_랜덤 이런식으로 지어지기 때문이다.

    운영에서 사용할 수가 없다.

    Exception 나면 테이블의 유니크제약때문에 에러났네 이런식으로 찾아야되는데

    랜덤 이름인 경우 해당 Exception 찾기 힘들기 때문에

    걸때 @Table 유니크 제약을 있다. 거기서 유니크 명을 설정할 잇다.

     

    length 설정가능 VARCHAR (10);

     

    실제 자신이 컬럼 설정을 지정할 있다.

    위는 varchar(100) default 값을 EMPTY 준다는

     

    Precision, scale(DDL)

    큰숫자나 소수점 관련할때 쓰면

     

    @Enumerated

    자바 enum 타입을 매핑할 때 사용

    주의! ORDINAL 사용X

     

     

     Ordinal 사용   이런식으로 순서에따라 값이 들어감

     

    근데 쓰면 안되는 이유

    만약에 요구사항때문에 추가됨

    순서가 바뀌는 순간 안맞아버림

    위험함 해결할 없는 버그가 되어버림

    필수로 Enum.STRING으로 해야

     

    몇자 아끼려다 사고내기보다 그냥 STRING 쓰기

     

     @Temporal

    날짜 타입(java.util.Date, java.util.Calendar)을 매핑할 때 사용

    참고: LocalDate, LocalDateTime을 사용할 때는 생략 가능(최신 하이버네이트 지원)

     

    @Lob

    데이터베이스 BLOB, CLOB 타입과 매핑

    • @Lob에는 지정할 수 있는 속성이 없다.

    • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑

    • CLOB: String, char[], java.sql.CLOB

    • BLOB: byte[], java.sql. BLOB

     

    @Transient

    • 필드 매핑X

    • 데이터베이스에 저장X, 조회X

    • 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용

    • @Transient

    private Integer temp;

     

    기본 키 매핑==================================

     

     

    기본 키 매핑 어노테이션

    • @Id

    • @GeneratedValue

     

     

    기본 키 매핑 방법

    • 직접 할당: @Id만 사용  (뭔가 임시적 아이디로 할때)

    • 자동 생성(@GeneratedValue)

    • IDENTITY: 데이터베이스에 위임, MYSQL

    • SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE

    • @SequenceGenerator 필요

    • TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용

    • @TableGenerator 필요

    • AUTO: 방언에 따라 자동 지정, 기본값

     

    -----------------------------------------------------------------------------------------------------------

     

    IDENTITY 전략 - 특징

    • 기본 키 생성을 데이터베이스에 위임

    • 주로 MySQL, PostgreSQL, SQL Server, DB2에서 사용

    (예: MySQL의 AUTO_ INCREMENT)

    • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL 실행

    AUTO_ INCREMENT는 데이터베이스에 INSERT SQL을 실행

    한 이후에 ID 값을 알 수 있음

    • IDENTITY 전략은 em.persist() 시점에 즉시 INSERT SQL 실행

    하고 DB에서 식별자를 조회

     

    추가 설명

    • 데이터 베이스에 insert 해봐야 있다, ID값이 뭔지 DB 값이 들어가봐야 알수 있다.

    왜냐하면 기본키 생성 전략을 DB에게 위임하기 때문이다.

    • 그런데 영속성 컨텍스트에 등록하려면 무조건 PK값이 있어야한다.
    • 그러므로 제약사항이 생긴다

    id 생성전략이 identity 경우 , entityManger em.pertist(Member member) 호출시

    1차캐시에 키와 값으로 등록되는 아닌 바로 insert문이 날아가서 등록된 JPA 내부적

    값을 select (실제 select 문을 받지는 않음 jdbc 키값 리턴받는 것을 한다.)해서 키값을 가져   1차캐시에 등록된다.

     

     

    IDENTITY 전략 - 매핑

     

    SEQUENCE 전략 - 특징

    • 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한

    데이터베이스 오브젝트(예: 오라클 시퀀스)

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

     

    추가 설명

    • 먼저 sequence 전략일 경우 ,JPA에서 DB 다음시퀸스 값이 무엇인지 알아낸

    1차캐시에 등록 한다.

    • 그러면 쿼리를 2 날리게되는데 성능이슈 있지 않냐?

    JPA AllocationSize 이란게 있다 .

    기본값 , 50개를 땡겨놈 메모리에서 1 사용 하다가 쓰면 다시 50개씩 땡김

    여러 WAS 있어도 동시성 이슈 없이 해결 가능하다.

    • 근데 서버를 내리는순간 날아간다. (구멍이 생길 있음)

     

    SEQUENCE 전략 - 매핑

     

     

    Generage 아이디에서 Long 쓰는 이유!

    int 에서 long 된다고 애플리케이션 전체에 영향을 주지 않는다.
    integer에서 Long 으로 타입을 변경하는데 훨씬 힘듬




    강의에서 엔티티 클래스를 정의할 때,
    Long 타입을 id로 사용하는 이유가 궁금합니다.
    primitive 타입인 long 타입도 있는데, Long타입과의 차이점? 사용시의 차이점..?을 알고 싶습니다.
    항상 친절한 답변 감사드립니다 (_ _)
    좋은 질문입니다. null을 사용할 수 있기 때문에 사용하고 있습니다.




    프리미티브 타입은 기본값이 0인데 그럼 실제로 id 값이 0인건지, 값이 없는건지 사실 구분하기 어렵습니다. id가 0일 수도 있는거니까요. 그런데 Wrapper 타입인 Long이나 Integer를 쓰면 id가 없는 경우엔 확실히 null이고, 그 자체로 id가 없다는걸 보장할 수 있죠.


    https://docs.jboss.org/hibernate/orm/5.3/userguide/html_single/Hibernate_User_Guide.html#entity-pojo-identifier


    자세한 이유는 나와있지 않긴하지만 Hibernate JPA 공식 문서에서도 Wrapper 타입을 권장하고 있네요.


    We recommend that you declare consistently-named identifier attributes on persistent classes and that you use a nullable (i.e., non-primitive) type.

     

    해보면 나오는 쿼리

       
        drop table if exists Member CASCADE
    Hibernate:
       
        drop sequence if exists MEMBER_SEQ
    Hibernate: create sequence MEMBER_SEQ start with 1 increment by 1
    Hibernate:
       
        create table Member (
           id bigint not null,
            age integer,
            createDate timestamp,
            description clob,
            lastModifiedDate timestamp,
            name varchar(255),
            rolType varchar(255),
            primary key (id)
        )

     

    SEQUENCE - @SequenceGenerator

    • 주의: allocationSize 기본값 = 50

     

    SEQUENCE 전략과 최적화

    • 실습으로 소개

     

     

    TABLE 전략

    • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉

    내내는 전략

    • 장점: 모든 데이터베이스에 적용 가능

    • 단점: 성능

     

    TABLE 전략 - 매핑

    @TableGenerator - 속성

    쓰지는 않는다.

     

    권장하는 식별자 전략

    기본 키 제약 조건: null 아님, 유일, 변하면 안된다.

    • 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대

    체키)를 사용하자.

    • 예를 들어 주민등록번호도 기본 키로 적절하기 않다.

    • 권장: Long형 + 대체키 + 키 생성전략 사용

     

    실제 사례)

    예전에는 주민등록번호를 가지고 있어도 됐는데

    정책 변경으로 주민등록 번호를 가지면 안되게

    포털에서는 주민번호를 PK

    해당 PK를 바꾸는 것 뿐만 아니라 PK 참조하는

    모든 테이블의 값을 변경해야된다.

    어마어마한 비용 발생

     

    • 권장: Long형 + 대체키 + 키 생성전략 사용

    Long형을 쓰는이유

    1. 10억이 넘어도 동작해야되기 때문에
    2. primitive 값이면 0 될경우 값이 없는건지 실제 0 건지 구분할 없으므로

    명시적으로 null 나타낼 있는 wrapper 클래스를 쓰는 좋기 때문에

     

    때에따라 UUID 랜덤값을 조합한 키생성전략을 사용을 권장

     

    실전 예제 - 1. 요구사항 분석과 기본 매핑

     

    요구사항 분석

    • 회원은 상품을 주문할 수 있다.

    • 주문 시 여러 종류의 상품을 선택할 수 있다.

    기능 목록

    • 회원 기능

    • 회원등록

    • 회원조회

    • 상품 기능

    • 상품등록

    • 상품수정

    • 상품조회

    • 주문 기능

    • 상품주문

    • 주문내역조회

    • 주문취소

     

     

    도메인 모델 분석

    • 회원과 주문의 관계: 회원은 여러 번 주문할 수 있다. (일대다)

    • 주문과 상품의 관계: 주문할 때 여러 상품을 선택할 수 있다. 반

    대로 같은 상품도 여러 번 주문될 수 있다. 주문상품 이라는 모델

    을 만들어서 다대다 관계를 일다대, 다대일 관계로 풀어냄

     

    테이블 설계

     

    엔티티 설계와 매핑

     

    데이터 중심 설계의 문제점

    • 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식

    • 테이블의 외래키를 객체에 그대로 가져옴

    • 객체 그래프 탐색이 불가능

    • 참조가 없으므로 UML도 잘못됨

     

    해당 예제로 새로 다시 프로젝트를 만든다.

    프로젝트부터 다시 만들기

     

    기본 프로젝트 오브젝트 세팅

    package jpabook.jpashop.domain;


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


    @Entity
    public class Item {
        @Id
        @GeneratedValue
        @Column(name = "ITEM_ID")
        private Long id;
        private String name;
        private int price;
        private int stockQuantity;


    //
    }
    @Entity
    public class Member {
        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name="MEMBER_ID")
        private Long id;
        @Column(length = 10)
        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;
        //이렇게하면 db컬럼명도 orderDate로 생성됨
        //그러나 DBA가 원하는건 order_date , ORDER_DATE 이다.
        //스프링 부트를 쓰면 camel을 undersocore로 바꾸는게 기본이다.
        @Enumerated(EnumType.STRING)
        private OrderStatus orderStatus;
    @Entity
    public class OrderItem {
        @Id
        @GeneratedValue
        @Column(name = "ORDER_ITEM_ID")
        private Long id;
        @Column(name = "OREDER_ID")
        private Long orderId;
        @Column(name="ITEM_ID")
        private Long itemId;
        private int orderPrice;
        private int count;
    public enum OrderStatus {
        ODER,CANCEL
    }

     

    여튼만듬

     

    객체의 제약조건을 명시적으로 해놓는것이 좋다.

        @Column(length = 10)
        private String name;

    이렇게 해놓으면 DB 안까보고도 해당 제약조건이 10인지 알수 있다 .

    인덱스도같은것도 알수 있다 .

     

     

     

    @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;
        //이렇게하면 db컬럼명도 orderDate로 생성됨
        //그러나 DBA가 원하는건 order_date , ORDER_DATE 이다.
        //스프링 부트를 쓰면 camel을 undersocore로 바꾸는게 기본이다.
        @Enumerated(EnumType.STRING)
        private OrderStatus orderStatus;

     

    뭔가 이상하다 .

     

    근데 주문정보 밖에 모름

    주문한 멤버를 찾고싶다.

     

    그러면이렇게됨

    Order order = em.find(Order.class,1L);
    Long memberId = order.getMemberId();
    Member member = em.find(Member.class,memberId);

    뭔가 이상하다 .

     

    계속 타고 타고 가서 값을 가져와야된다.

     

    객체지향적이지 않다.

     

    객체지향적인건

    Order Member 객체를 가지고 있어서 바로 값을 가져올수 있어야됨

    이런 식이 되어야한다.

    Order order = em.find(Order.class,1L);
    order.getMember().getName();

     

     

    객체는 참조로 쭉쭉 찾아가야한다.

    끊겨 버림

     

    이런방식의 설계는 객체지향스럽지 않다.

    관계형 DB 맞춤 설계

     

    데이터 중심 설계의 문제점

    • 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식

    • 테이블의 외래키를 객체에 그대로 가져옴

    • 객체 그래프 탐색이 불가능

    • 참조가 없으므로 UML도 잘못됨

     

     

    'JPA' 카테고리의 다른 글

    프록시와 연관관계 관리  (0) 2022.08.06
    고급 매핑  (0) 2022.07.30
    다양한 연관관계 매핑  (0) 2022.07.29
    연관관계 매핑 기초  (0) 2022.07.27
    영속성 관리 - 내부 동작 방식  (0) 2022.07.26
Designed by Tistory.