객체와 테이블 매핑
엔티티 매핑 소개
- 객체와 테이블 매핑
- @Entity, @Table(name = "테이블 명")
- 필드와 컬럼 매핑
- @Column(name = "컬럼 명")
- 기본 키 매핑
- @Id
- 연관관계 매핑
- @ManyToOne, @OneToMany, @ManyToMany, @OneToOne, @JoinColumn
@Entity
- @Entity가 붙은 클래스는 JPA가 관리하는 엔티티
- JPA를 사용해서 테이블과 매핑할 클래스는 @Entity 필수
- 주의
- 기본 생성자 필수 (파라미터가 없는 public 또는 protected 생성자)
- final 클래스, enum, interface, inner 클래스 사용 X
- 저장할 필드에 final 사용 X
@Table
- @Table은 엔티티와 매핑할 테이블 지정
데이터베이스 스키마 자동 생성
데이터베이스 스키마 자동 생성
- DDL을 애플리케이션 실행 시점에 DB 자동 생성
- 테이블 중심 -> 객체 중심
- 데이터베이스 방언을 활용해서 데이터베이스에 맞는 적절한 DDL 생성
- 이렇게 생성된 DDL은 개발 장비에서만 사용
- 생성된 DDL은 운영서버에서는 사용하지 않거나, 적절히 다듬은 후 사용
데이터베이스 스키마 자동 생성 - 속성
hibernate.hbm2ddl.auto
데이터베이스 스키마 자동 생성 - 주의
- 운영 장비에는 절대 create, create-drop, update 사용하면 안된다.
- 개발 초기 단계는 create 또는 update
- 테스트 서버는 update 또는 validate
- 스테이징과 운영 서버는 validate 또는 none
운영 같은 경우 몇천만건 있는 상태에서 alter를 잘못치면 시스템이 중단상태가 될 수 있다. 애플리케이션 로딩시점에 시스템에서 자동으로 alter 친다는 것은 매우 위험하다.
DDL 생성 기능
- 제약조건 추가 : 회원 이름은 필수, 10자 초과X
- @Column(nullable = false, length = 10)
- 유니크 제약조건 추가
- @Table(uniqueConstraints = {@uniqueConstraint( name = "NAME_AGE_UNIQUE",
columnNames = {"NAME", "AGE"})})
- @Table(uniqueConstraints = {@uniqueConstraint( name = "NAME_AGE_UNIQUE",
- DDL 생성 기능은 DDL을 자동 생성할 때만 사용되고 JPA의 실행 로직에는 영향을 주지 않는다.
필드와 컬럼 매핑
요구사항 추가
- 회원은 일반회원과 관리자로 구분해야 한다.
- 회원가입일과 수정일이 있어야 한다.
- 회원을 설명할 수 있는 필드가 있어야 한다. 이 필드는 길이 제한이 없다.
Member 클래스 수정
@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 // 데이터베이스에 varchar를 능가하는 큰 문자열을 넣을 때 Lob 사용
private String description;
public Member(){}
}
테이블이 create된 출력
- description이 clob인 이유는 String으로 선언했기 때문
- enum은 varchar로 매핑
매핑 어노테이션 정리
@Column
@Enumerated
자바 enum 타입을 매핑할 때 사용
주의! ORDINAL 사용 X (순서로 하게되면 1,2,3 이런식으로 저장되는데 문제는 Enum의 데이터를 추가할 경우 기존의 데이터가 바뀌지 않아 문제가 생긴다.)
@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
- 주로 메모리상에서만 임시로 어떤 값을 보관하고 싶을 때 사용
기본 키 매핑
기본 키 매핑 방법
- 직접 할당 : @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에서 식별자를 조회
- JDBC 드라이브에 insert 쿼리를 날릴 때 return을 받는 것이 내부적으로 되어있기 때문에 select 쿼리문을 날리지 않고 insert 만으로 기본 키를 넣을 수 있음
- persist 시점에 insert SQL을 실행하기 때문에 버퍼링을 이용한 성능 최적화를 할 수 없다.
Sequence 전략 - 특징
- 데이터베이스 시퀀스는 유일한 값을 순서대로 생성하는 특별한 데이터베이스 오브젝트 (예 : 오라클 시퀀스)
- 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용
- em.persist() 일 때 DB에서 Sequence만 갖고와서 영속성 컨텍스트에 저장하면서, insert 쿼리문도 함께 SQL저장소에 저장한 뒤, 트랜잭션 commit 시점에 insert 쿼리문을 DB에 전송
- 따라서, 버퍼링 기능을 사용 가능
- 하지만, persist에서 SEQ 값을 갖고 오기위해 네트워크를 이용하는 것 때문에 성능에 문제가 생길 수 있습니다. SEQ값을 갖고오는 것보다 Insert 쿼리문을 날리는게 성능에 더 좋아진다고 생각할 것 입니다.
- 이를 해결하기 위해, allocationSize를 이용하면됩니다. allocationSize에 할당한 수만큼 데이터가 모일 때 SEQ 값을 증가시키는 아래와 같은 쿼리문을 보냅니다.
Sequence 전략 - 매핑
Sequence - @SequenceGenerator
Table 전략
- 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
- 장점 : 모든 데이터베이스에 적용 가능
- 단점 : 성능
- 최적화가 되어있지 않기 때문에 성능이 떨어진다.
Table 전략 - 매핑
알아서 테이블과 컬럼이 생성되고, select와 update문을 날리면서 동작
테이블을 하나 만들어서 관리하는게 있어보이지만, 성능상 좋지 않기 때문에 잘 사용하지는 않는다고 합니다.
권장하는 식별자 전략
- 기본 키 제약 조건 : null 아님, 변하면 안된다.
- 미래까지 이 조건을 만족하는 자연키는 찾기 어렵다. 대리키(대체키)를 사용하자.
- 예를들어, 주민등록번호도 기본키로 적절하지 않다.
- 권장 : Long형 + 대체키 + 키 생성전략 사용
실전 예제 1 - 요구 사항 분석과 기본 매핑
요구사항 분석
- 회원은 상품을 주문할 수 있다.
- 주문 시 여러 종류의 상품을 선택할 수 있다.
기능 목록
- 회원 기능
- 회원 등록
- 회원 조회
- 상품 기능
- 상품 등록
- 상품 수정
- 상품 조회
- 주문 기능
- 상품 주문
- 주문 내역 조회
- 주문 취소
도메인 모델 분석
- 회원과 주문의 관계 : 회원은 여러 번 주문할 수 있다. (일대다)
- 주문과 상품의 관계 : 주문할 때 여러 상품을 선택할 수 있다. 반대로 같은 상품도 여러 번 주문될 수 있다. 주문상품이라는 모델을 만들어서 다대다 관계를 일다대, 다대일 관계로 풀어냄
테이블 설계
엔티티 설계와 매핑
엔티티 설계를 보며, 프로젝트에 Entity로 추가합니다. (Order 클래스에 status는 enum 타입입니다.)
참고
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
자바 11이상 버전을 이용하면 위의 라이브러리를 pom.xml에 추가해주어야합니다. java 11부터는 Java ee 모듈이 제거되어 jaxb 라이브러리를 꼭 추가해줘야 된다고 합니다.
엔티티를 추가한 뒤, 코드를 돌렸을 때 create table 로그가 출력되고 콘솔에 테이블이 만들어지면 잘 작동된 것 입니다.
근데 자세히보면 Member와 Order의 관계를 만들어주는 Order엔티티에 memberId의 자료형은 Long입니다. 조금 더 객체지향적으로 설계하기 위해선 자료형을 Member로 선언해야 할 것입니다. 그렇다면 객체지향적으로 설계된 엔티티와 그렇지 않은 엔티티의 차이점은 어떨까요?
자료형을 Long, 클래스로 하는 것의 차이점은 아래의 예시를 들어 확인해보도록 하겠습니다.
자료형을 Member로 하면 order.getMember()를 통해 한번의 작업으로 원하는 Member 엔티티를 얻어올 수 있습니다. 하지만, 객체지향적으로 설계하지 않은 Long의 자료형은 order.getMemberId()를 통해 memberid를 받아오고 해당 memberid를 통해 다시 또 엔티티를 찾아주는 작업을 해야합니다. 즉, Long 자료형의 경우 두번의 작업을 해야합니다.
위의 같은경우는 Order -> Member 한번의 이동의 차이점을 본 것인데요. 이러한 엔티티간의 이동이 많은 경우 객체지향적으로 설계한 것이 더 편리하다는 것을 느낄 수 있을 것 입니다.
데이터 중심 설계의 문제점
- 현재 방식은 객체 설계를 테이블 설계에 맞춘 방식
- 테이블의 외래키를 객체에 그대로 가져옴
- 객체 그래프 탐색이 불가능
- 참조가 없으므로 UML도 잘못됨
출처
'spring > 인프런 강의 정리' 카테고리의 다른 글
[JPA 기본편] 6. 다양한 연관관계 매핑 (0) | 2022.03.09 |
---|---|
[JPA 기본편] 5. 연관관계 매핑 기초 (0) | 2022.03.08 |
[JPA 기본편] 3. 영속성 관리 - 내부 동작 방법 (0) | 2022.03.05 |
[JPA 기본편] 2. JPA 시작하기 (0) | 2022.02.21 |
[JPA 기본편] 1. JPA 소개 (0) | 2022.02.19 |