[47일차] JPA, 엔티티 매핑

유태형·2022년 7월 5일
0

코드스테이츠

목록 보기
47/77

오늘의 목표

  1. JPA(Java Persistence API)
  2. 엔티티 매핑



내용

JPA(Java Persistence API)

JPA(Java Persistence API)는 JAVA에서 사용하는 ORM기술의 표준 사양 입니다.

JPA표준 사양을 구현한 구현체로는 Hibernate ORM, EclipseLink, DataNucleus등이 있습니다. 보통 JAP의 API에 구현체 자신만의 API를 추가적으로 구현되어 있습니다.

Persistence는 영속성, 지속성이라는 뜻으 가지고 있습니다. 즉, 무언가를 금방 사리지지 않게 하고 오래 지속되가한다Persistence의 목적입니다.



영속성 컨텍스트(Persistence Context)

JPA에서는 테이블과 캐핑되는 엔티티 객체 정보를 영속성 컨텍스트(Persistence Context)에 보관해서 애플리케이션 실행중에 장기간 보관할 수있도록 합니다.

영속성 컨텍스트는 1차 캐시쓰기 지연 SQL 저장소로 구성되어 있습니다.

ORM을 이용하여 JPA API를 호출하여 엔티티 객체를 저장하면 1차 캐시에 저장되고 쓰기 지연 SQL 저장소에 SQL문이 따로 저장되어 관리되게 됩니다.



Spring Data JPA 환경 설정

Spring Data JPA를 사용하기 위해서는 build.gradle에 먼저 외부 의존 라이브러리를 추가해야 합니다.

dependencies{
	implementation 'org.springframework.boot:spring-boot-starter-data-jdbc'
    ...
}

이전에 Spring Data JDBC와 마찬가지로 인메모리 DBh2를 이용하여 서버 실행마다 손쉽게 데이터베이스를 세팅할 수 있도록 application.propertiesapplication.yml로 확장자를 변경합니다.

spring:
	h2:
    	consle:
        	enabled: true
            path: /h2
    datasource:
    	url: jdbc:h2:mem:test
    jpa:
    	hibernate:
        	ddl-auto: create
        show-sql: true
  • ddl-auto: create : 스키마를 직접 지정할 필요 없이 JPA가 자동으로 데이터베이스에 테이블을 생성해 줍니다.
  • show-sql: true : SQL쿼리를 실행창에 출력합니다.
@Configuration
public class JpaBasicConfig{
	private EntityManager em;
    private EntityTransaction tx;
    
    @Bean
    public CommandLineRunner testJpaBasicRunner(EntityManagerFactory emFactory){
    	this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();
        
        return args -> {
        	//실행 코드
        };
    }
}
  • @Configuration : Spring에서 설정 정보 클래스로 등록합니다. 설정정보 클래스 내의 @Bean 애너테이션이 붙은 메서드를 실행하여 스프링 컨테이너에 빈 객체를 추가하고, 빈 객체간 의존 주입하는등 빈 객체를 관리 할 수 있도록 정보를 제공합니다.
  • EntityManager : 영속성 컨텍스트를 내장하고 있어 엔티티 클래스들을 영속성 컨텍스트에 저장하여 엔티티 클래스들을 관리하는 클래스입니다.
  • EntityManagerFactory : 엔티티 메니저를 여러 스레드가 동시에 접근하지 못하게 스레드마다 엔티티르 생성하여 전달할 수 있도록 엔티티를 만드는 역할을 수행합니다.
  • CommandLineRunner : 서버 구동 시점에 초기화작업으로 어떤일을 할것인지 정의하는 인터페이스 입니다.

@Configuration 애너테이션을 이요하여 설정 정보 클래스를 만들고 서버 구동시 초기화 작업으로 수행할 일들을 지정함으로써 엔티티와 테이블 매핑을 확인할 수 있습니다.

@Getter
@Setter
@NoArgsConstructor
@Entity
public class 엔티티{
	@Id
    @GeneratedValue(GerationType.IDENTITY)
    private long 엔티티Id;
    
    privaet String email;
}
  • @Entity : 해당 클래스가 엔티티 클래스임을 알리는 에너테이션입니다.
  • @Id : 테이블의 기본키에 속하는 필드와 매칭됩니다.
  • @GeneratedValue(GerationType.IDENTITY) : SQL의 AUTO_INCREMENT와 동일합니다. 값 생성을 데이터베이스에 위임합니다.

@Entity@Id 에너테이션을 추가하면 JPA에서 해당 클래스를 엔티티 클래스로 인식합니다.

@Configuration
public class 설정정보클래스{
	private EntityManager em;
    private EntityTransaction tx;
    
    @Bean
    public CommaindLineRunner 러너(EntityManagerFactory emFactory){
    	this. em = emFactory.createEntityManager();
        this.tx = em.getTransaction();
        
        return args -> {
        	엔티티 엔티티 = new 엔티티(매개변수);
            
            tx.begin();
            
            em.persist(엔티티);
            엔티티 엔티티 = em.find(엔티티.class,@Id값);
            엔티티.set필드(새로운값);
           	em.remove(엔티티);
            
            tx.commit();
        };
    }
}
  • EntityTransaction : 영속성 컨테이너 내에서 트랜젝션을 관리하는 클래스입니다.
  • tx.begin(); : 트랜잭션 단위를 시작합니다.
  • tx.commit(); : 트랜잭션 단위르 종료하고 쓰기 지연 SQL 저장소에 있는 SQL문을 실행후 삭제합니다. 내부적으로 em.flush()메서드가 호출됩니다.
  • em.persist(엔티티) : 엔티티를 영속성 컨텍스트내의 1차 캐시에 저장하고 해당하는 SQL문을 쓰기 지연 SQL 저장소에 저장합니다. 쓰기 지연 SQL 저장소INSERT문이 저장됩니다.
  • em.find(엔티티.class,@Id값); : 엔티티 클래스에 해당하는 엔티티 객체들중 기본키 값(아이디 값)에 해당하는 엔티티 객체를 반환합니다. 쓰기 지연 SQL 저장소SELECT문이 저장됩니다.
  • 엔티티.set필드(새로운값); : 1차 캐시내에 있는 객체의 해당 필드를 수정합니다. setter만으로도 업데이트 로직이 완성됩니다. 쓰기 지연 SQL 저장소UPDATE문이 저장됩니다.
  • em.remove(엔티티); : 1차 캐시내에 존재하는 엔티티 객체 제거를 요청합니다. 쓰기 지연 SQL 저장소DELETE문이 저장됩니다.



엔티티 매핑

@Entity(name = "USERS")
@Table(name = "USERS")
public class 엔티티{
	@Id
    private Long 필드;
}
  • @Entity 해당 클래스가 엔티티 클래스임을 JPA에게 알리는 역할을 합니다.
    • name 속성은 1차 캐시에 해당 이름으로 엔티티 클래스를 저장할 것임을 명시합니다.
  • @Table(name = "USERS") 해당 엔티티 클래스와 매핑될 데이터베이스의 테이블 명을 명시합니다.
    • 에너테이션 자체를 생략하거나 name속성 생략시 엔티티 클래스와 동일한 테이블과 매핑됩니다.
  • @Id : 기본키와 매핑되는 필드를 지정합니다.

@Entity에너테이션과 @Id에너테이션은 엔티티 클래스를 지정하는데 필수적이며 생략시 에러를 발생시킵니다. 반면에 @Table에너테이션은 생략이 가능합니다.



기본키 매핑

JPA에서 @Id에너테이션으로 지정한 필드가 테이블에서 기본키 컬럼이 되는데 해당 키 값을 어떻게 지정할 것인가는 여러 가지 전략이 존재합니다.

전략설명
직접 할당엔티티 객체에 직접 값을 할당하는 방식입니다.
IDENTITY기본키 생성을 데이터베이스에 위임하고, AUTO_INCREMENT처럼 숫자를 +1씩 증가시키는 방식입니다.
SEQUENCE데이터베이스의 시퀀스를 사용해서 기본키를 생성합니다.
TABLE별도의 키 생성을 위한 테이블을 사용합니다.

직접 할당

@NoArgsConstructor
@Getter
@Entity
public class 엔티티{
	@Id
    private Long 필드;
    public 엔티티(Long 필드){
    	this.필드 = 필드;
    }
}

엔티티 클래스

@Configuration
public class 설정정보클래스{
	private EntityManager em;
    private EntityTransaction tx;
    
    @Bean
    public CommandLineRunner 러너(EntityManagerFactory emFactory){
    	this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();
        
        return args -> {
       		tx.begin();
            em.persist(new 엔티티(1L)); //직접 할당
            tx.commit();
        };
    }
}

설정정보 클래스

엔티티 객체를 생성하면서 인자로 필드의 값을 직접 넣어줌으로써 값을 할당하고 있습니다.

IDENTITY

@NoArgsConstructor
@Getter
@Entity
public class 엔티티{
	@Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long 필드;
    
    public 엔티티(Long 필드){
    	this.필드 = 필드;
    }
}

엔티티 클래스

@Configuration
public class 설정정보클래스{
	private EntityManager em;
    private EntityTransaction tx;
    
    @Bean
    public CommandLineRunner 러너(EntityManagerFactory emFactory){
    	this.em = emFactory.createEntityManager();
        this.tx = em.getTransaction();
        
        return args -> {
       		tx.begin();
            em.persist(new 엔티티()); //IDENTITY : 1 -> 2 -> 3 -> 4 -> ...
            tx.commit();
        };
    }
}

IDENTITY 전략이 적용되었기 때문에 별도의 기본 키 값을 할당 하지 않아도 자동으로 +1 씩해가며 할당됩니다.

Sequence

@NoArgsConstructor
@Getter
@Entity
public class 엔티티{
	@Id
    @GeneratedValue(strategy=GenerationType.SEQUENCE)
    private Long 필드;
    
    public 엔티티(Long 필드){
    	this.필드 = 필드;
    }
}

IDENTITY 전략과 마찬가지로 SEQUENCE저략도 엔티티 객체 생성시 별도의 기본 키 값을 할당하지 않아도 데이터베이스가 자동으로 값을 +1 씩 증가시켜가며 할당합니다.

차이점이라면 시퀀스에서 기본키를 제공을 하냐 테이블 자체 제공을 하냐 차이 입니다.

AUTO

@GeneratedValue(strategy=GenerationType.AUTO)

JPA가 데이터베이스의 Dialect에 따라 적절한 전략을 자동으로 선택합니다. 표준으로 정해진 것이 아니라 데이터베이스에 따라 다릅니다.



필드와 컬럼간 매핑

엔티티 클래스와 테이블을 매핑할 수 있었다면 클래스의 멤버변수, 테이블의 속성들을 서로 매핑할 수 있을 것입니다.

엔티티 클래스의 카멜 표기법 -> 테이블의 언더바 표기법으로 이름으로 매핑이 가능합니다.

SQL의 DDL에서 알 수 있듯이 테이블의 속성을 정의 할 때 여러 옵션들으 줄수 있습니다. NULL이 가능한지 수정 가능한지, 후보키인지 등 자세한 속성들을 정의할 수 있습니다.
Spring Data JPA는 애너테이션으로 다양한 옵션들을 제공합니다.

  • @Column : 필드멤버가 매핑되는 속성에 어떤 옵션을 줄지 에너테이션 내 에트리뷰트로 줄 수 있습니다.
  • @Transient : 테이블의 컬럼과 매핑되지 않는 필드를 나타냅니다.
  • @Enumerated : 필드로 열거형인 경우 값을 어떻게 저장할 지 선택 합니다.
@Column에트리뷰트
nullablefalse : 빈 값으로 입력이 불가능 합니다. true(디폴트) : 빈값으로 입력이 가능합니다.
updatablefalse : 사용 중 수정이 불가능 합니다. true(디폴트) : 사용 중 수정이 가능합니다.
uniquefalse(디폴트) : 후보키가 아닙니다. true : 후보키 입니다.
length(디폴트:255) 문자의 길이를 지정할 수 있습니다.
name매핑되는 테이블의 컬럼명을 필드명이 아닌 임의로 지정할 수 있습니다.
@Enumerated에트리뷰트
EnumType.ORDINALenum의 순서를 나타내는 숫자를 테이블에 저장합니다.
EnumType.STRINGenum의 이름을 테이블에 저장합니다.

enum에 새로운 상수가 추가된다면 새롭게 추가된 위치의 다음 상수들의 순서가 바뀌게 되므로 문제가 발생할 수 있습니다. 따라서 EnumType.ORDINAL 보단 EnumType.STRING의 권장됩니다.




후기

드디어 JPA를 처음 배웠습니다. 이전에 Spring Data JDBC를 미리 학습해서 그런지 새로운 에너테이션과 인터페이스는 낯설었지만 ORM과 매핑등은 어렵지 않게 이해가 되었습니다.




GitHub

private!

profile
오늘도 내일도 화이팅!

0개의 댓글