IT
스프링부트 4
Dr. yul
2023. 2. 27. 18:09
김영한 님 강의 <자바 ORM 표준 JPA 프로그래밍> 중. (인프런)
- 필드와 컬럼 매핑
- 칼럼은 기본적으로 @Column인데,
- Enum을 넣고 싶으면 @Enumerated
- Date, 시간을 넣고 싶으면 @Temporal - type에 Date, Time, DateTime이 있다.
- 지금은 사실 필요 없다. LocalDate, LocalDateTime 사용할 때는 생략 가능. 최신 하이버네이트 쓸 때.
- 큰 데이터를 넣고 싶으면 @Lob - BLOB, CLOB 매핑
- 지정할 수 있는 속성이 없다. String이면 CLOB, 나머지는 BLOB으로 매핑된다.
- 매핑 안 하고 싶을 때 @Transient (DB에 가지 말고 memory에서만 계산해줘. 메모리에서만 쓸래. )
- 속성
- insertable, updatable - 등록, 변경 가능 여부. 기본은 TRUE
- nullable
- unique - 잘 안씀. 이름을 안 넣으면 그냥 랜덤으로 넣어줘 버림. 그래서 @Column(unique = true) 이렇게 안하고 엔티티 단위에서 @Table(uniqueConstraints = { @UniqueConstraint(columnNames = { "personNumber", "isActive" }) }) 해서 넣음.
- columnDefinition - 데이터베이스 컬럼 정보를 직접 줌.
- length - 문자 길이 제약조건, String 타입에만 사용한다.
- precision, scale - BicDecimal 타입에서 사용. 아주 큰 숫자나 소숫점 다룰 때 쓴다.
- Enum 쓸 때 주의사항: EnumType은 두 가지. ORDINAL과 STRING. default가 ORDINAL임. ORDINAL은 enum 순서를 데이터베이스에 저장, STRING은 이름을 데이터베이스에 저장. 이게 왜 위험하냐 - ENUM에 하나의 타입이 더 추가된다고 해보자. 원래 user, admin밖에 없었는데 앞에 guest를 추가함 - 그럼 원래 user가 0번째였는데 guest가 0번째가 되는 것. DB가 엉망이 되겠쥬...? 무조건 EnumType.STRING으로 해주자.
- 칼럼은 기본적으로 @Column인데,
- 기본 키 매핑
- @Id - 직접 할당시
- @GeneratedValue - 자동 생성시
- IDENTITY: 데이터베이스에 위임, MYSQL, PostgreSQL, SQL Server, DB2에서 사용.
- 난 모르겠고 DB야 너가 해줘.
- Null로 넣는다. id를 임의로 넣으면 안 됨. 그럼 얘를 DB에 insert할 때 PK 값이 생긴다. 즉, 커밋 칠때까지 PK값을 알 수 없다. 근데 영속성 컨텍스트에서 얘를 쓰려면 무조건 PK값이 있어야 함.. 그래서 울며 겨자먹기로 Identity 전략에서만 persist할 때 바로 DB에 insert 쿼리를 날려버린다.
- SEQUENCE: 데이터베이스 시퀀스 오브젝트 사용, ORACLE
- @SequenceGenerator 필요
- 시퀀스 오브젝트를 가져와서 값을 셋팅해주는 것. Call next value 해서 가져옴.
- SequenceGenerator 속성: name(MEM_SEQ_GENERATOR), sequenceName(MEM_SEQ), initialValue (DDL 생성 시에만 사용됨, 시퀀스 DDL을 생성할 때 처음 시작하는 수를 지정한다. 기본값 1 - 성능최적화를 위함), allocationSize (성능최적화를 위함)
- initialValue와 allocationSize - allocationSize의 디폴트 값은 50이다. persist를 할 때마다 call을 해야 하니까 어쨌뜬 통신을 해야 해서 성능문제 생길 수 있음. 그래서 미리 1~50을 한 번에 메모리에 꺼내놓고 1부터 쌓음. (메모리에서 키밸류를 가져온다.) 50까지 다 쌓으면 그 다음 51~100번 꺼내 놓음.
- TABLE: 키 생성용 테이블 사용, 모든 DB에서 사용
- @TableGenerator 필요
- 키 생성 전용 테이블을 하나 만들어서 DB 시퀀스를 흉내내는 전략 - 모든 DB에 적용 가능하나 성능이 떨어질 수 있다. 쓰는 건 SequenceGenerator랑 거의 비슷하게 쓴다. 대신 sequenceName대신 table = "" 해서 테이블 이름을 지정하고, pkColumnValue (시퀀스 컬럼명)를 지정해 준다. 중요한 건 initialValue와 allocationSize - 성능 최적화와 관련 있음.
- 실행을 하면 - table 안에 sequence_name, next_val 등의 column이 생성된다.
- 운영에서는 이 전략 쓰기가 부담스럽다.
- AUTO: 기본값은 이거임. 방언에 따라 위 셋 중 자동 지정, 기본값
- IDENTITY: 데이터베이스에 위임, MYSQL, PostgreSQL, SQL Server, DB2에서 사용.
- 권장하는 식별자 전략
- 기본 키 제약 조건: null 아님, 유일, 변하면 안된다. - 변하면 안된다는 게 어렵다!!!
- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. (자연키 = 주민등록번호 등) - 대리키 (대체키)를 사용하자. 비즈니스와 전혀 상관 없는 애를 사용한다. 에를 들어 주민번호를 사용하다가 정부가 갑자기 주민번호 저장해두면 안된다고 하면 ->.... 망..
- 권장: Long형 + 대체키(시퀀스, uuid 등) + 키 생성전략 사용 - 조합해서.
- 실전예제: 요구사항 분석과 기본 매핑 : 멤버 한 명이 주문을 넣을 수 있다. 주문 - 여러 상품 넣을 수 있다. 상품 - 여러 주문에 들어갈 수 있다. 이 때 매핑을 어떻게 할 것이냐?
- 다음과 같은 테이블 설계 가능
- 그럼 처음에는 이렇게 설계한다. (다음 사진)
- 얘의 문제는 뭘까 - 객체지향스럽지 않다. RDB에 맞춘 설계임.
- 들어가기 전에: Setter는 아무데나 남발하지 않는 것이 유지보수에 좋다.
- 문제는, 객체지향스럽지 않다는 것. 이렇게 하면 Order를 찾고 이 order를 넣은 member를 찾고 싶을 때, 이렇게 찾아야 함.
- 즉, 객체지향스럽지 않다. Order.getMember로 멤버를 찾아야 객체지향스럽다.
- 객체그래프 탐색 불가능. 참조가 없으므로 UML도 잘못됨. 참조가 다 끊긴다. Id만 가져와서.
- 연관관계 매핑 기초
- 목표
- 객체와 테이블 연관관계 차이를 이해 - 객체의 참조와 테이블의 외래 키를 매핑!!!
- 용어 이해 - 방향: 단방향/양방향, 다중성: 다대일/일대다/일대일/다대다, 연관관계의 주인 (이게 제일 어렵다): 객체 양방향 연관관계는 관리?이 필요함.
- 단방향 연관관계
- 객체지향적 모델링
- 목표
- 이렇게 그냥 Team을 박아버림. 그러면 member를 저장할 때 jpa가 알아서 FK를 찾아서 DB에는 테이블 연관관계로 저장해 준다.
- 양방향 연관관계
- 머선 뜻: 단방향 연관관계는 member.getTeam()만 할 수 있음. 양방향은 member.getTeam(), team.getMember() 둘다 가능하게 한 것.
- 테이블 연관관계는 단방향이나 양방향이나 똑같다. FK로 member에서 team을, team에서 member를 조회할 수 있다.
- 객체 연관관계만 달라짐. 요러케.
- 코딩은 이렇게 한다. @OneToMany annotation에 (mappedby = "team") 해줌.
- new ArrayList<>();로 초기화해주는 것은 관습임. 그래야 add할 때 null point가 안 뜨니까 .. 라는데 머선말이고.. 나중에 궁금하면 찾아보자.
- 양방향 매핑: 반대 방향으로도 객체 그래프 탐색 가능!!!
- 하지만 객체는 엥간하면 단방향이 좋다. 양방향으로 하면 신경쓸게 많다..
- 여기서 의문. mappedBy가 왜 있을까?
- 객체와 테이블간 연관관계 맺는 차이를 이해해야 이해할 수 있다!
- 객체의 양방향 연관관계 - 단방향 연관관계 2개: 회원 -> 팀 / 팀 -> 회원. 사실은 단방향 연관관계가 두 개인 건데 어거지로 양방향 연관관계라고 하는 거임. 참조값이 두 개.
- 테이블의 양방향 연관관계 - 양방향 연관관계 1개임. 회원 <-> 팀. FK 하나로 연관관계가 양쪽으로 다 됨. 양쪽으로 조인할 수 있다. 사실 방향이 없는 것임.
- 객체의 양방향 관계는 사실 양방향 관계가 아니라 서로 다른 단방향 관계 두 개다!
- 둘 중 하나로 외래 키를 관리해야 한다!
- 객체와 테이블간 연관관계 맺는 차이를 이해해야 이해할 수 있다!
- DB 입장에서는 사실 FK 뭘로 쓰든 상관 없다. 하지만 외래 키를 둘 중 어떤걸로 관리할지 정해야 한다. 이게 바로 연관관계의 주인이다.
- 연관관계의 주인
- 객체의 두 관계중 하나를 연관관계의 주인으로 지정
- 연관관계의 주인만이 외래 키를 관리(등록, 수정)
- 주인이 아닌 쪽은 읽기만 가능 - mappedBy = "team"이라는 거는 team이 주인이라는 것. member에서부터 team을 조회는 할 수 있지만 update하거나 값을 더 넣을 수는 없다. 넣어도 아무 일도 일어나지 않는다!
- 주인은 mappedBy 속성을 사용하지 않는다. (mappedBy: 내가 뭐에 의해 매핑이 되어버렸어. 이 의미부터가 주인이 아닌 너낌이자나)
- 주인이 아니면 mappedBy 속성 사용 - 이걸로 주인을 구분한다.
- 누구를 주인으로?
- 외래 키가 있는 곳을 주인으로 정해라. 여기서는 Member.team이 연관관계의 주인. 그럼 N쪽이 무조건 연관관계의 주인이 됨. 그래야 설계도 깔끔하고 성능도 좋음. 주인을 누구로 정하느냐는 비즈니스적인 논리와 전혀 관계가 없다.
- FK가 member에 있다. Member->team이 진짜 매핑. 연관관계의 주인. team->Member는 가짜 매핑. 조회만 가능.
- 양방향 연관관계 매핑 시 많이 하는 실수 (4분 23초까지만 봄 - 언젠가 마저 보자 양방향 연관관계 할 일 있으)
- 가장 많이 하는 실수: 연관관계의 주인에 값을 입력하지 않음. - 가짜매핑으로 update나 persist를 하는 경우.
- 그래서 요렇게 하면 member에 teamID가 제대로 안 들어간다. Null로 보여짐. 이런 실수 하지 말자!
- 연관관계의 주인에만 값을 넣고 주인이 아닌 것에는 값을 안 넣으면 잘 들어감. 반대로 하면 안 들어간다.