JPA

YoungG209·2021년 11월 9일
0

본 포스팅은 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 책을 보고 작성하였음

1. JPA

1.1 개요

1.1.1 JPA란?

  • 객체지형적으로 프로그래밍하고, JPA가 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행
  • SQL에 종속적인 개발을 하지 않아도 됨

Spring Data JPA

  • JPA는 인터페이스로서 자바 표준명세서임, 사용하기 위한 구현체가 필요
  • 대표적으로 Hibernate, Eclipse, Link 등이 있음
  • Spring에서 JPA를 사용할 때는 상기 구현체를 좀 더 쉽게 사용하고자 추상화 시킨 Spring Data JPA라는 모듈을 이용하여 다룸
  • 굳이 한 단계 더 삼싸놓은 Spring Data JPA가 등장한 이유
    • 구현체 교체의 용이성

      • Hibernate 외에 다른 구현체로 쉽게 교체 하기 위함
      • Hibernate -> Eclipse 언제든지 교체 가능
    • 저장소 교체의 용이성

      • 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함
      • MySQL -> MongoDB 언제든지 교체 가능

1.2 프로젝트 Spring Data JPA 세팅

1.2.1 build.gradle에 의존성 등록

implementation 'org.springframework.boot:spring-boot-starter-data-jpa' ⓐ
   runtimeOnly 'com.h2database:h2' ⓑ
  • ⓐ spring-boot-starter-data-jpa

    • 스프링 부트용 Spring Data Jpa 추상화 라이브러리
    • 스프링 부트 버전에 맟춰 자동으로 JPA 관련 라이브러리들의 버전을 관리해 줌
  • ⓑ h2

    • 인메모리 관계형 데이터베이스
    • 별도의 설치가 필요 없이 프로젝트 의존성만으로 관리할 수 있음
    • 메모리에서 실행되기 때문에 애플리케이션을 재시작할 때마다 초기화 된다는 점을이용해 테스트 용도로 많이 사용

2. JPA Domain

2.1 Domain 이란?

  • 게시글, 댓글, 회원, 정산, 결제 등 소프트웨어에 대한 요구사항 혹은 문제 영역이라고 생각하면 됨
  • MyBatis와 같은 쿼리 매퍼 때 사용한 DAO 패키지와는 다름

2.2 Domain 패키지

2.2.1 Entity 클래스

실제 DB의 테이블과 매칭될 클래스이며, 보통 Entity 클래스라고도 함

import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Getter ⓐ
@NoArgsConstructor ⓑ
@Entity ⓒ
public class Posts {

    @Id ⓓ
    @GeneratedValue(strategy = GenerationType.IDENTITY) ⓔ
    private long id;

    @Column(length = 500, nullable = false) ⓕ
    private String title;

    @Column(columnDefinition = "TEXT", nullable = false)
    private String content;

    private String author;

    @Builder ⓖ
    public Posts(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }
}
  • JPA의 어노테이션
    • ⓒ @Entity
      • 테이블과 링크될 클래스임을 나타냄
      • 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍(_)으로 테이블 이름을 매칭함
        ex) SalesManager.java - > sales_manager table
    • ⓓ @Id
      • 해당 테이블의 PK 필드를 나타냄
    • ⓔ @GeneratedValue
    • ⓕ @Column
      • 테이블 칼럼을 나타내며 굳이 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼이 됨
      • 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용
      • 문자열의 경우 VARCHAR(255)가 기본값, 사이즈를 500으로 늘리고 싶거나, 타입을 TEXT로 변경하고 싶을 등의 경우에 사용됨
  • 롬복의 어노테이션
    • 롬복은 코드를 단순화시켜 주지만 필수 어노테이션은 아님

    • 코틀린 등의 새 언어 전환으로 롬복이 더이상 필요 없을 경우 삭제 필요

    • ⓐ @Getter

      • 클래스 내 모든 필드의 Getter메소드를 자동 생성
    • ⓑ @NoArgsConstructor

      • 기본 생성자 자동추가
      • public Posts(){}와 같은 효과
    • ⓖ @Builder

      	- 해당 클래스의 빌더 패턴 클래스를 생성
      • 생성자 상단에 선언 시 생성자에 포함된 필드만 빌더에 포함

2.2.2 Entity 클래스 주의 사항

  • Entity 클래스는 절대 Setter 메소드를 만들지 않음, 대신 해당 필드의 값 변경이 필요하면 명확히 그 목적과 의도를 나타낼 수 있는 메소드를 추가해야만 함

    • 예시

      잘못된 사용 예
      public class Order {
          public void setStatus(boolean status) {
              this.status = status;
          }
      }
      
      public void 주문서비스의_취소이벤트() {
          order.setStatus(false);
      }
      올바른 사용 예
      public class Order {
          public void setStatus(boolean status) {
              this.status = status;
          }
      }
      
      public void 주문서비스의_취소이벤트() {
          order.setStatus(false);
      }
    • Setter가 없는 상황에서 값을 채워 DB에 삽입?

      • 생성자를 통해 최종값을 채운 후 DB에 삽입하며, 값변경이 필요한경우 해당 이벤트에 맞는 public 메소드를 호출하여 변경하는 것을 전제로 함
      • @Builder를 통해 제공되는 빌더 클래스를 사용
        -> 빌더를 사용하게 되면 어느 필드에 어떤 값인지 명확하게 인지 가능

2.2.3 Repository

import org.springframework.data.jpa.repository.JpaRepository;

public interface PostsRepository extends JpaRepository<Posts, Long> {

}
  • Repository란?
    • DB Layer 접근자
    • 인터페이스로 생성, JpaRepository<Entity 글래스, PK 타입>를 상속하면 기본적인 CRUD 메소드가 자동 생성

2.3 Domain 테스트

import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@SpringBootTest
public class PostsRepositoryTest {

    @Autowired
    PostsRepository postsRepository;

    @After ⓐ
    public void cleanup() {
        postsRepository.deleteAll();
    }

    @Test
    public void saveAndFindPosts() {
        //given
        String title = "테스트 게시글";
        String content = "테스트 본문";

        postsRepository.save(Posts.builder() ⓑ
                .title(title)
                .content(content)
                .author("sunsky300@naver.com")
                .build()
        );

        //when
        List<Posts> postsList = postsRepository.findAll(); ⓒ

        //then
        Posts posts =postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }
}
  • ⓐ @After

    • Junit에서 단위 테스트가 끝날 때마다 수행되는 메소드를 지정
    • 보통은 배포 전 전체 테스트를 수행할 때 테스트간 데이터 침범을 막기 위해 사용함
    • 여러 테스트가 동시에 수행되면 테스트용 데이터베이스인 H2에 데이터가 그대로 남아 있어 다음 테스트 실행 시 테스트가 실패할 수 있음
  • ⓑ postsRepository.save

    • 테이블 posts에 insert/update 쿼리를 실행함
    • id 값이 있다면 update, 없다면 insert 쿼리가 실행됨
  • ⓒ postsRepository.findAll

    • 테이블 posts에 있는 ㅗ든 데이터를 조회해오는 메소드
  • 별다른 설정없이 @SpringBootTest를 사용할 경우 H2 데이터베이스를 자동으로 실행

  • 실행된 쿼리 로그

    • 설정 위치 : src/main/resource -> application.poperties

    • 설정 코드 :

      spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect
      spring.jpa.properties.hibernate.dialect.storage_engine=innodb
      spring.datasource.hikari.jdbc-url=jdbc:h2:mem://localhost/~/testdb;MODE=MYSQL
      
profile
고우고우~

0개의 댓글