[Spring] 엔티티와 테이블 간의 매핑

Minit88·2023년 4월 24일
0

Spring

목록 보기
12/16
post-thumbnail

Lab_01 : 엔티티와 테이블 간의 매핑

@Entity
@Table
public class Member {
    @Id
    private Long memberId;
}

[코드] 엔티티와 테이블 간의 매핑

@Entity 애너테이션 설명
애트리뷰트

  • name :
    • 엔티티 이름을 설정할 수 있다.
    • name 애트리뷰트를 설정하지 않으면 기본값으로 클래스 이름을 엔티티 이름으로 사용한다.
@Entity(name="USERS")

@Table 애너테이션 설명
애트리뷰트

  • name
    • 테이블 이름을 설정할 수 있다.
    • name 애트리뷰트를 설정하지 않으면 기본값으로 클래스 이름을 테이블 이름으로 사용한다.
    • @Table 애너테이션은 옵션이며, 추가하지 않을 경우 클래스 이름을 테이블 이름으로 사용한다.
    • 주로 테이블 이름이 클래스 이름과 달라야 할 경우에 추가한다.

기본키 매핑

JPA에서는 기본적으로 @Id 애너테이션을 축자한 필드가 기본 키 열이 되는데, JPA에서는 일한 기본키를 어떤 방식으로 생성해 줄지에 대한 다양한 전략을 지원한다.
기본키 직접 할당

  • 애플리케이션 코드 상에서 기본키를 직접 할당해주는 방식이다.
    기본키 자동 생성
  • IDENTITY
    • 기본키 생성을 데이터베이스에 위임하는 전략이다.
    • 데이터베이스에서 기본키를 생성해 주는 대표적인 방식은 MySQL의 AUTO_INCREMENT 기능을 통해 자동 증가 숫자를 기본키로 사용하는 방식이 있다.
  • SEQUENCE
    • 데이터베이스에서 제공하는 시퀀스를 사용해서 기본키를 생성하는 전략이다.
  • TABLE
    • 별도의 키 생성 테이블을 사용하는 전략이다.

기본키 직접 할당 전략

@NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id   // (1)
    private Long memberId;

    public Member(Long memberId) {
        this.memberId = memberId;
    }
}

기본키 직접 할당 전략은 @Id 애너테이션만 추가하면 된다.

@Configuration
public class JpaIdDirectMappingConfig {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaSingleMappingRunner(EntityManagerFactory emFactory){
        this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();

        return args -> {
            tx.begin();
            em.persist(new Member(1L));  // (1)
            tx.commit();
            Member member = em.find(Member.class, 1L);

            System.out.println("# memberId: " + member.getMemberId());
        };
    }
}

[코드] 기본키 직접 할당 예

  • (1) 과 같이 기본키를 직접 할당해서 엔티티를 저장한다.
  • (1) 에서 기본키 없이 엔티티를 저장하면 에러 메시지가 출력된다.

IDENTITY 전략

@NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) // (1)
    private Long memberId;

    public Member(Long memberId) {
        this.memberId = memberId;
    }
}

[코드] IDENTITY 기본키 생성 전략 설정

  • IDENTITY 기본키 생성 전략을 설정하려면 위와 같이 @GenerationValue 애너테이션의 strategy 애트리뷰트의 값을 GenerationType.IDENTITY로 지정해 주면 된다.
  • IDENTITY 전략은 데이터베이스에서 기본키를 대신 생성해 준다.

INDENTITY가 실제로 기본키를 자동 생성해주는지에 대해 알아보면

@Configuration
public class JpaIdIdentityMappingConfig {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaSingleMappingRunner(EntityManagerFactory emFactory){
        this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();

				return args -> {
            tx.begin();
            em.persist(new Member());
            tx.commit();
            Member member = em.find(Member.class, 1L);

            System.out.println("# memberId: " + member.getMemberId());
        };
    }
}

[코드] IDENTITY 기본키 생성 전략을 통한 엔티티 저장

  • Member 엔티티에 IDENTITY 전략이 적용되었기 떄문에 Member 엔티티에 별도의 기본키 값을 할당하지 않았다.

SEQUENCE 전략

NoArgsConstructor
@Getter
@Entity
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE)  // (1)
    private Long memberId;

    public Member(Long memberId) {
        this.memberId = memberId;
    }
}

[코드] SEQUENCE 기본키 생성 전략 설정

  • SEQUENCE 전략을 사용하기 위해서는 @GeneratedValue(strategy = GenerationType.SEQUENCE)를 지정하면 된다.
  • SEQUENCE 전략은 데이터베이스 시퀀스를 이용한다.
@Configuration
public class JpaIdIdSequenceMappingConfig {
    private EntityManager em;
    private EntityTransaction tx;

    @Bean
    public CommandLineRunner testJpaSingleMappingRunner(EntityManagerFactory emFactory){
        this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();

        return args -> {
            tx.begin();
            em.persist(new Member());  // (1)
            Member member = em.find(Member.class, 1L);
            System.out.println("# memberId: " + member.getMemberId());
            tx.commit();

        };
    }
}

[코드] SEQUENCE 기본키 생성 전략을 통한 엔티티 저장

  • Member 엔티티 객체를 생성하면서 별도의 기본키 값을 전달하지 않았다.
  • SEQUENCE 전략을 사용하도록 지정하여, 엔티티가 영속성 컨텍스트에 저장되기 전에 데이터베이스가 시퀀스에서 기본키에 해당하는 값을 제공한다.

IDENTITY vs SEQUENCE

IDENTITY

  • 먼저 Entity를 DB에 저장
  • 식별자를 조회해 Entity의 식별자로 할당하는 전략
    SEQUENCE
  • em.persist() 호출 전에 먼저 DB Sequence를 먼저 조회한다.
  • 조회한 식별자를 Entity에 할당한 후 Entity를 영속상태로 저장한다.
  • Transaction을 Commit하여 Flush가 발생할 때 해당 Entity를 저장한다.

AUTO 전략

  • @Id 필드에 @GenenratedValue(strategy = GenerationType.AUTO)를 지정하면 JPA가 데이터베이스의 Dialect에 따라서 적절한 전략을 자동으로 선택한다.

    Dialect는 표준 SQL 등이 아닌 특정 데이터베이스에 특화된 고유한 기능을 의미한다. 만일 JPA가 지원하는 표준 문법이 아닌 특정 데이터베이스에 특화된 기능을 사용할 경우 Dialect가 처리해준다.

필드(멤버 변수)와 열 간의 매핑

@Getter
@Setter
@NoArgsConstructor
@Entity(name="Member")
public class Member {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //기본 키를 직접 할당하는 대신 데이터베이스가 생성해주는 값을 사용하려면 추가
    private Long memberId;
    
    //unique= 해당 열에 중복된 값 허용 x
    @Column(nullable = false,updatable=false,unique=true)
    private String email;

    public Member(String email){
        this.email = email;
    }

}

[코드] email필드에 열 매핑 애너테이션 추가

@Column 애너테이션은 필드와 열을 매핑해 주는 애너테이션이다. 만약 @Column 애너테이션이 없고, 필드만 정의되어 있다면, JPA는 기본적으로 이 필드가 테이블의 열과 매핑되는 필드라고 간주하게 된다. 또한, @Column 애너테이션에 사용되는 애트리뷰트의 값은 디폴트 값이 모두 지정된다.

애트리뷰트

  • nullable
    • 열에 null값을 허용할지 여부를 지정한다.
    • 디폴트 값은 true이다
    • email 주소는 일반적으로 회원 정보에서 ID로 많이 사용되어, 필수 항목이기에 nullable 값을 false로 지정
  • update
    • 열 데이터를 수정할 수 있는지 여부를 지정
    • 디폴트 값은 true
    • 아이디 값으로 사용할거라 수정이 불가능 하도록 updatable 값을 false로 지정
  • unique
    • 하나의 열에 unique 유니크 제약 조건을 설정
    • 디폴트 값은 false
    • email의 경우 고유한 값이어야 하므로 unnique 값을 true로 지정

email 필드 null,update,unique 테스트

null테스트

    private void testEmailNotNull(){
        tx.begin();
        em.persist(new Member());
        tx.commit();
    }

[코드] null 테스트를 위한 메서드

  • email 필드가 null이 허용되지 않기에 객체를 생성할 때 파라미터에 빈 값을 넣고 null을 테스트해본다.

  • 출력 값은 위 그림과 같다.
  • email은 null을 허용하지 않는데, 출력 결과 값에도 동일하게 null이 허용이 되지 않음을 나타내고 있다.

update

    private void testEmailUpdatable(){
        tx.begin();
        em.persist(new Member("hgd@gmail.com"));
        Member member = em.find(Member.class,1L);
        member.setEmail("hgd@gmail.co.kr");
        tx.commit();
    }

[코드] update 테스트를 위한 메서드

  • email 필드가 update가 허용되지 않기에 set을 이용한 update를 추가했다.

  • 출력 결과는 위 그림과 같다.
  • Insert가 동작했지만, update는 발생하지 않았음을 알 수 있다.
  • 따라서 updatable=false 설정이 정상적으로 동작했다.
    Unique
    private void testEmailUnique(){
        tx.begin();
        em.persist(new Member("hgd@gmail.com"));
        em.persist(new Member("hgd@gmail.com"));
        tx.commit();
        Member member = em.find(Member.class,2L);

    }

[코드] unique를 확인해보기 위한 메서드

  • 동일한 email값을 persist 하여 unique 가 실행되는지 확인

  • 위 그림에서 에러가 발생한 것을 보면, 유니크가 정상적으로 동작함을 알 수 있다.
profile
" To be BE "

0개의 댓글