엔티티 매핑

born_a·2022년 8월 28일
1

객체와 테이블 매핑

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

@Table(name="MBR")처럼 테이블 명을 바꾸는건 insert 쿼리나, update쿼리는 런타임에 영향을 주나, unique 제약 조건을 넣는건 실행 자체엔 영향 안주고, DDL 생성만 한다.

필드와 컬럼 매핑

@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;

    public Member() {
    }


}

@Column(name = "name") // db컬럼명은 name 이야.
private String username;
객체는 username이라고 쓰고싶은데, db에는 name이라고 적고 싶으면 이렇게 적어주면 됨.

db에는 enum 이 없음.
따라서 @Enumerated 어노테이션을 써주면 됨.

varchar를 넘어서는 큰 타입을 쓰고 싶으면 @Lob 해주면 됨.
@Lob 인데 String이면 clob으로 생성됨

이건 db랑 관계없이 메모리에만 계산하고 싶어 -> @Transient

@Column

@Column(name = "name",nullable = false,columnDefinition = "varchar(100) default 'EMPTY'")
private String username; 하면

반영이 되는것을 확인가능

Enumerated

Ordinal은 사용하면 안된다.
왜?

try {
            Member member = new Member();
            member.setId(1L);
            member.setUsername("A");
            member.setRoleType(RoleType.USER);

            em.persist(member);

            tx.commit();
        } 

이렇게 데이터를 넣었을 때,

public enum RoleType {
GUEST, USER, ADMIN;
}
게스트를 추가하면

순서가 바뀐다.

따라서 필수로 String으로 써야한다!!

@Enumerated(EnumType.STRING)
private RoleType roleType;

ROLETYPE이 GUEST로 들어간 것을 볼 수 있다.

@Temporal

최근은 Temporal 안씀.
private LocalDate testLocalDate;//연월
private LocalDateTime testLocalDateTime;//연월일
요렇게 쓴다

기본키 매핑

Auto는 데이터베이스 방언에 맞춰서 셋중 하나가 선택되는것.

Identity 전략

Member.java

@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private String id;

JpaMain.java

try {
            Member member = new Member();
            member.setUsername("C");

            em.persist(member);

            tx.commit();
        }

id가 자동으로 생성됨.

Identity전략은 id에 값을 넣지 않고 db에 인서트 해야한다.
null로 insert쿼리가 날라오면 디비에서 그때 값을 세팅해준다.

Member.java

@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

JpaMain.java

    
try {
            Member member = new Member();
            member.setUsername("C");
            System.out.println("==================");
            em.persist(member);
            System.out.println("==================");

            tx.commit();
        }


persist하는 시점에 쿼리가 날라간다.
insert쿼리를 날려봐야 그때서야 pk값을 알 수 있다.

Sequence 전략

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
시퀀스 오브젝트를 만들어냄.
데이터베이스에 있는 시퀀스 오브젝트를 통해 시퀀스를 가져와서 값을 세팅하는 것.
숫자니까 Long으로 해주어야한다!(Integer는 10몇억 이상은 안된다)

@SequenceGenerator

@Entity
@SequenceGenerator(name = "member_seq_generator", sequenceName = "member_seq")
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "member_seq_generator")
    private Long id;

meber_seq라는 시퀀스 오브젝트를 만들어냈다.

try {
            Member member = new Member();
            member.setUsername("C");
            System.out.println("==================");
            em.persist(member);
            System.out.println("member.id = " + member.getId());
            System.out.println("==================");

            tx.commit();
        }

em.persist를 할 때, 영속성 컨텍스트에 넣어야한다. 그럴려면 항상 pk가 있어야 한다.
그럴려면 먼저 시퀀스를 가지고 와야한다. 시퀀스에서 내 pk를 가지고 와야한다. @GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "MEMBER_SEQ_GENERATOR") 너 시퀀스 전략이네, 하고 jpa가 여기서 내가 값 가져올게 하는 방식으로 매커니즘이 동작한다.
그래서 call next value for MEMBER_SEQ (MEMBER_SEQ)의 다음 값 내놔 라는 쿼리를 날린다.

        정리 : 영속성 컨텐스트 넣을려하는데 sequence 전략이네?
        db한테 가서 얻어와야 겠네하고 db에서 값을 얻어와서 
        db에 이 id값을 넣어준다.
        그 다음 영속성 컨텍스트에 저장함. 아직 db에 insert쿼리는 안날라감. 
        왜냐면 pk값만 딱 얻고 버퍼링도 해야하므로 영속성컨텍스트에 쌓여있다가 
        실제로 커밋하는 시점에 insert쿼리가 호출이 된다. 시퀀스 방식은 버퍼링(모아놨다가 한번에 보냄) 이 가능.

allocationSize

Member.java

@SequenceGenerator(
        name = "MEMBER_SEQ_GENERATOR",
        sequenceName = "MEMBER_SEQ", //매핑할 데이터베이스 시퀀스 이름
        initialValue = 1, allocationSize = 50)

JpaMain.java

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); // "

            System.out.println("member.id = " + member1.getId());
            System.out.println("member.id = " + member2.getId());
            System.out.println("member.id = " + member3.getId());
            System.out.println("==================");

            tx.commit();
        }

allocationSize:db에 50개를 db에 미리 올려두고 메모리에서 그 개수만큼 쓰는 방식.
51이면 1 부터 50개까지 쓸 수 있구나 한다.
101이면 51부터 100까지 쓸 수 있구나 함.

call next value for MEMBER_SEQ가 두번 호출 된다. 왜 두번 호출될까?
처음 호출하면 DB SEQ는 1이 되고, 두번째 호출하면 51이 된다
처음 호출해봤더니 1이 되어서 뭔가 문제가 있나보다 해서 한번더 호출된거다.

DB SEQ = 1 | 애플리케이션이 쓰는 것: 1
DB SEQ = 51 | 2
DB SEq = 51 | 3

start with 1 increment by 1: 1부터 시작해서 1씩 증가

시퀀스는 db가 관리하기 때문에

MEMBER_SEQ를 확인할 수 있다.

TABLE 전략

@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;

자연키 : 비즈니스 적으로 의미있는 키 ex) 주민등록번호
대리키 : 비즈니스적으로 관계없는 키
비즈니스를 키로 끌고 오지 말자.

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

@Entity
public class Item {
    @Id @GeneratedValue
    @Column(name = "ITEM_ID")
    private Long Id;

    private String name;
    private int Price;
    private int stockQuality;

Member.java

package jpabook.jpashop.domain;

import javax.persistence.*;

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

Order.java

@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;

OrderItem.java

@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;

OrderStatus.java

public enum OrderStatus {
    ORDER, CANCLE;
}

Id를 db가 만들어주는 값을 쓴다고 가정하면 @GeneratedValue까지 해줘야한다.
getter,setter의 경우 다 만들어준 필요는 없지만 getter는 만들어주는게 좋다. setter는 좀 고민해봐야.
가급적이면 생성자에서 세팅하도록 해야한다.

Enum타입은 @Enumerated()를 해줘야한다.

localhost/~/test->localhost/~/jpashop
새로운 db쓰는것.

<property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/~/jpashop"/>

객체는 참조로 쭉쭉 이어져야 함.

0개의 댓글

관련 채용 정보