[자바 ORM 표준 JPA 프로그래밍 - 기본편] 04. 엔티티 매핑

Turtle·2024년 6월 18일
0
post-thumbnail

🙄엔티티 매핑 소개

  • 객체와 테이블 매핑 : @Entity, @Table
  • 필드와 컬럼 매핑 : @Column
  • 기본 키 매핑 : @Id
  • 연관관계 매핑 : @ManyToOne, @JoinColumn

🙄@Entity

  • @Entity가 붙은 클래스는 JPA가 관리
  • JPA를 사용해서 테이블과 매핑할 클래스에는 @Entity 어노테이션이 필수
  • 주의사항
    • 기본 생성자 필수(파라미터가 없는 public 혹은 protected 생성자)
    • final 클래스, enum, interface, inner 클래스 사용X
    • 저장할 필드에 final 사용X

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

아래 코드는 애플리케이션 실행 시점에 데이터베이스 테이블을 자동으로 생성한다.

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

아래 코드에서 속성을 true로 설정하면 콘솔에 실행되는 테이블 생성 DDL을 출력할 수 있다.

<property name="hibernate.show_sql" value="true"/>
  • ✔️hibernate.hbm2ddl.auto 속성
    • create : 기존 테이블을 삭제하고 생성한다.(DROP + CREATE)
    • create-drop : create 속성에 추가로 애플리케이션을 종료할 때 생성한 DDL을 제거한다.
    • update : 데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 변경 사항만 수정한다.
    • validate : 데이터베이스 테이블과 엔티티 매핑 정보를 비교해서 차이가 있으면 경고를 남기고 애플리케이션을 실행하지 않는다.
    • none : 자동 생성 기능을 사용하지 않으려면 hibernate.hbm2ddl.auto 속성 자체를 삭제하거나 유효하지 않은 옵션 값
  • ✔️주의
    • 운영 장비에는 절대 create, create-drop, update를 사용하면 안 된다.
    • 개발 초기 단계는 create 또는 update
    • 테스트 서버는 update 또는 validate
    • 스테이징과 운영 서버는 validate 또는 none

🙄필드와 컬럼 매핑

  • ✔️@Column
    • name : 필드와 매핑할 테이블의 컬럼 이름
    • nullable : null값의 허용 여부 → false로 허용하면 not null 제약조건이 붙는다.
    • unique : 유니크 제약 조건
  • ✔️@Enumerated
    • EnumType.ORDINAL : enum 순서를 데이터베이스에 저장
    • EnumType.STRING : enum 이름을 데이터베이스에 저장
    • ORDINAL은 사용하지말고 STRING을 사용해야 안전하다.
  • ✔️@Temporal
    • 날짜 타입 매핑시 사용
    • Date, Time, DateTime
    • 최신 버전의 경우 LocalDate나 LocalDateTime을 사용
  • ✔️@Lob
    • 데이터베이스 BLOB, CLOB 타입과 매핑
    • 지정할 수 있는 별도의 속성 존재X
    • 매핑하는 필드 타입이 문자면 CLOB 매핑, 나머지는 BLOB 매핑
  • ✔️@Transient
    • 데이터베이스에 매핑X
@Entity
public class Member {
	@Id
	private Long id;

	@Column(name = "name")
	private String name;

	@Enumerated(value = EnumType.STRING)
	private RoleType roleType;

	@Temporal(value = TemporalType.TIMESTAMP)
	private Date createdAt;

	@Temporal(value = TemporalType.TIMESTAMP)
	private Date lastModifiedDate;

	@Lob
	private String description;

	public Member() {

	}
    
    // Getter/Setter ...
}

🙄기본 키 매핑

@Entity
public class Member {
    @Id
    private Long id;

    @Column(name = "name", nullable = false)
    private String username;

    protected Member() {

    }
    
    // ....
}
  • ✔️@기본 키 매핑 방법
    • 직접 할당 : @Id만 사용
    • 자동 생성 : @GeneratedValue 사용
      • IDENTITY
      • SEQUENCE
      • TABLE
      • AUTO
  • ✔️@GeneratedValue(strategy = GenerationType.IDENTITY)
    • 기본 키 생성을 데이터베이스에 위임하는 전략
    • MySQL, PostgreSQL, SQL Server, DB2에서 사용한다.
      • Ex. MySQL의 AUTO_INCREMENT
    • JPA는 보통 트랜잭션 커밋 시점에 INSERT SQL을 실행
    • AUTO_INCREMENT는 데이터베이스에 INSERT SQL을 실행한 이후에 ID값을 알 수 있음
    • MySQL의 AUTO_INCREMENT 기능은 데이터베이스가 기본 키를 자동으로 생성
    • 데이터베이스가 순서대로 값을 채워준다.
    • IDENTITY 전략은 em.persist() 메서드 호출 시점에 즉시 INSERT SQL 실행하고 DB에서 식별자를 조회할 수 있다.
Hibernate: 
    create table Member (
        id bigint generated by default as identity,
        name varchar(255) not null,
        primary key (id)
    )
  • ✔️@GeneratedValue(strategy = GenerationType.SEQUENCE)
    • 데이터베이스 시퀀스는 유일한 값을 생성하는 특별한 데이터베이스 오브젝트다.
    • SEQUENCE 전략은 이 시퀀스를 사용해서 기본 키를 생성한다.
    • 시퀀스를 지원하는 오라클, PostgreSQL, DB2, H2 데이터베이스에서 사용할 수 있다.
    • allocationSize의 기본 값은 50이다. 이 속성을 이용해 성능 최적화를 수행한다.
@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;
Hibernate: 
    create sequence MEMBER_SEQ start with 1 increment by 1
  • ✔️@GeneratedValue(strategy = GenerationType.TABLE)
    • 키 생성 전용 테이블을 하나 만들어서 데이터베이스 시퀀스를 흉내내는 전략
    • 모든 데이터에 적용 가능하나 성능의 단점이 존재
@Entity
@TableGenerator(
    name = "MEMBER_SEQ_GENERATOR",
    table = "MY_SEQUENCES",
    pkColumnValue = "MEMBER_SEQ", allocationSize = 1)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;
Hibernate: 
    create table hibernate_sequences (
        next_val bigint,
        sequence_name varchar(255) not null,
        primary key (sequence_name)
    )

✔️권장하는 식별자 전략

  • 기본 키 제약 조건 : null 아님, 유일성, 변하면 안 된다.
  • 권장 : Long형 + 대체키 + 키 생성전략 사용

🙄요구사항 분석과 기본 매핑

  • ✔️핵심 요구사항
    • 회원은 상품을 주문할 수 있다.
    • 주문 시 여러 종류의 상품을 선택할 수 있다.
  • ✔️기능 정리
    • 회원 기능
      • 회원 등록
      • 회원 조회
    • 상품 기능
      • 상품 등록
      • 상품 수정
      • 상품 조회
    • 주문 기능
      • 상품 주문
      • 주문 내역 조회
      • 주문 취소
  • ✔️도메인 모델 분석
    • 회원과 주문의 관계 : 회원은 여러 번 주문을 할 수 있다.(일대다)
    • 주문과 상품의 관계 : 주문 시 여러 종류의 상품을 선택할 수 있다.(다대다)
      • 하나의 주문에 여러 상품이 포함될 수 있으며 하나의 상품은 여러 주문에 포함될 수 있다.
      • 이와 같은 관계를 다대다 관계라고 한다. 하지만 실무에서 다대다는 거의 사용하지 않는다.
      • 주문상품이라는 연결 엔티티를 추가해서 다대다 관계를 일대다, 다대일 관계로 풀어낸다.
      • 하나의 주문에 여러 주문 상품이 있고 이 주문 상품 테이블에는 수량과 가격 정보가 들어있다.
      • 하나의 상품은 여러 주문 상품에 포함될 수 있다. 예를 들어 설명하자면 치약을 주문한다고 가정할 때, 이 치약이 클라이언트A에 의해서 주문이 될 수도 있고 클라이언트 B에 의해서도 주문이 될 수 있다는 것이다.
      • 주문상품에는 해당 상품 금액과 수량 정보가 포함되어 있다.

🙄실전 예제 작성 코드 - 점진적으로 발전시켜나가기

✔️Member 클래스

@Entity
@Table(name = "MEMBERS")
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "MEMBER_ID")
	private Long id;
	private String name;
	private String city;
	private String zipcode;

	// Getter/Setter ...
}

✔️Order 클래스

@Entity
@Table(name = "ORDERS")
public class Order {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	@Column(name = "ORDER_ID")
	private Long id;
	@Column(name = "MEMBER_ID")
	private Long memberId;
	private Date orderDate;
	private OrderStatus status;

	// Getter/Setter ...
}

✔️OrderItem 클래스

@Entity
public class OrderItem {
    @Id @GeneratedValue
    private Long id;
    @Column(name = "ORDER_ID")
    private Long orderId;
    @Column(name = "ITEM_ID")
    private Long itemId;
    private int orderPrice;
    private int count;
    
    // Getter/Setter ...
}

✔️Item 클래스

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

	// Getter/Setter ...
}

❓MEMBERS, ORDERS 지정 이유 → 예약어 때문에 오류가 발생하는 것을 방지하기 위해

0개의 댓글