[1] 스프링 부트와 JPA

hyyyynjn·2021년 2월 25일
0
post-thumbnail

JPA

  • JPA란 자바 표준 ORM이다.
  • Spring 과 JPA를 대부분 표준으로 사용하고 있다.
  • 관계형 데이터베이스와 객체 지향 프로그래밍 언어는 패러다임이 서로 다르기 때문에 이를 중간에서 일치시켜 주는 기술이다.
    • 개발자는 객체지향적으로 프로그래밍을 하고
    • JPA는 이를 관계형 데이터베이스에 맞게 SQL을 대신 생성하여 실행한다.
      • SQL에 종속적인 개발을 할 필요가 없게 된다.

Spring Data JPA

  • JPA는 인터페이스로서의 자바 표준 명세서이다.
  • JPA를 사용하기 위해서 구현체(Hibernate, Eclipse Link 등)가 필요하다.
  • 이러한 구현체들을 좀 더 쉽게 사용하기위해 추상화 시킨 Spring Data JPA 모듈을 사용하여 JPA 기술을 다룬다.
    • JPA(인터페이스) <- Hibernate(구현체) <- Spring Data JPA(모듈)
  • hibernate를 쓰는 것과 Spring Data JPA를 쓰는 것에는 차이가 없다. 그럼에도 한 단계 더 감싼 Spring Data JPA를 쓰는 이유는 다음과 같다.
    1. 구현체 교체의 용이성 : Hibernate 외에 른 구현체로 쉽게 교체하기 위함
    2. 저장소 교체의 용이성 : 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함

프로젝트에 Spring Data JPA 적용하기

build.gradle에 의존성 등록하기

dependencies {
    // spring data JPA
    compile('org.springframework.boot:spring-boot-starter-data-jpa')
    compile('com.h2database:h2')
}
  • spring-boot-starter-data-jpa

    • 스프링 부트용 Spring Data JPA 추상화 라이브러리이다.
  • h2

    • 인메모리 관계형 데이터베이스이다.
    • 별도의 설치가 필요없고 프로젝트 의존성만으로 관리가 가능하다.
    • 메모리에서 실행되므로 어플리케이션 재시작시에 초기화 된다.
    • 테스트 코드에서 @SpringBootTest 어노테이션을 작성하면 자동으로 H2 데이터베이스를 실행해준다
      • 기존의 application.yaml 파일에 mysql과 같은 다른 데이터베이스에 대한 configuration이 적혀있다면 테스트 코드 실행시 오류가 날 가능성이 크다.

Entity 클래스와 Entity Repository

  • domain 패키지에 posts패키지와 Posts 클래스와 PostsRepository를 만든다.
    • 도메인 : 게시글, 댓글, 회원, 정산, 결제 등의 소프트웨어에 대한 요구사항 혹은 문제영역이다.
@NoArgsConstructor
@Getter
@Entity
public class Posts extends BaseTimeEntity {

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

    @Column(length = 50, 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;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }
}
  • 어노테이션 설명
    • @NoArgsConstructor
      • 기본 생성자 자동 추가하는 롬복 어노테이션
      • Posts() {} 와 같은 의미이다.
    • @Getter
      • 클래스 내 모든 필드의 Getter 메소드를 자동 생성해주는 롬복 어노테이션
    • @Entity
      • JPA 어노테이션
    • @Id
      • 해당 테이블의 primary key 필드를 나타낸다.
    • @GeneratedValue
      • PK의 생성규칙을 나타낸다.
        스프링 부트 2.0에서는 IDENTITY 옵션을 추가해야지만 auto_increment가 된다.
      • 왠만하면 PK는 Long 타입의 auto_increment를 추천한다.
    • @Column
      • 해당 어노테이션을 굳이 선언하지 않아도 Entity 클래스의 모든 필드는 칼럼이 된다.
        기본값 이외에 추가로 변경이 필요한 옵션이 있을 경우 사용한다.
    • @Builder
      • 해당 클래스의 빌더 패턴 클래스를 생성하는 어노테이션이다.
        단, 생성자에 포함된 필드만 빌더에 포함한다.

Entity 클래스

  • JPA의 어노테이션으로 엔티티 클래스를 설정할 수 있다.
  • Posts 클래스는 실제 데이터베이스의 테이블과 매칭될 클래스이다.
    이러한 클래스가 엔티티 클래스이다.
  • 기본적으로 JPA는 엔티티 클래스의 카멜케이스 이름(SalesManager.java)을 ->
    언더스코어 네이밍(sales_manager table)으로 매칭한다.
  • 엔티티 클래스에 대하여 데이터베이스에 접근하게 해주는 JpaRepository 인터페이스를 생성해야한다.
    • 엔티티 클래스에서는 Setter 메소드를 만들지 않는다.
      • 만약, 해당 필드 값의 변경이 필요하다면 그 목적과 의도를 나타낼 수 있는 메소드를 추가해야한다.
      • setter 없이 생성자를 통해 최종값을 채운뒤 데이터베이스에 삽입한다.

Entity Repository

/**
 * */
public interface PostsRepository extends JpaRepository<Posts, Long> {
}
  • JpaRepository<Entity 클래스, PK 타입>을 상속하면 기본적인 CRUD 메소드가 자동생성된다.
  • @Repository 어노테이션을 추가할 필요가 없다.
  • 또한 Entity 클래스와 Entity Repository는 함께 위치해있어야 한다.

Test code

  • test 디렉토리에 domain.posts 패키지를 생성하고 PostsRepositoryTest 클래스를 만든다.
@RunWith(SpringRunner.class)
// @SpringBootTest 어노테이션을 사용할 경우 자동으로 H2 데이터베이스를 실행해준다.
// 만약 yaml 파일에 다른 DB에 관한 configuration 이 적혀있다면 오류가 발생할 가능성이 매우 크다.
@SpringBootTest
public class PostsRepositoryTest {

    @Autowired
    private PostsRepository postsRepository;

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

    @Test
    public void 게시글저장_불러오기() {
        String title = "테스트 게시글";
        String content = "테스트 본문";


        postsRepository.save(Posts.builder()
                .title(title)
                .content(content)
                .author("guswns3371@gmail.com")
                .build()
        );

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

        Posts posts = postsList.get(0);
        assertThat(posts.getTitle()).isEqualTo(title);
        assertThat(posts.getContent()).isEqualTo(content);
    }

    @Test
    public void BaseTimeEntity_등록() {
        LocalDateTime now = LocalDateTime.of(2021,2,25,0,0,0);
        postsRepository.save(Posts.builder()
                .title("title")
                .content("content")
                .author("author")
                .build());

        List<Posts> postsList = postsRepository.findAll();
        Posts posts = postsList.get(0);

        System.out.println(">>>>>> createdDate="+posts.getCreatedData()+", modifiedDate="+posts.getModifiedData());

        assertThat(posts.getCreatedData()).isAfter(now);
        assertThat(posts.getModifiedData()).isAfter(now);
    }
}
  • Test 코드 관련 어노테이션
  • @After
    • JUnit 단위 테스트가 끝날 떄 마다 수행되는 메소드를 지정한다.
    • 배포 전 전체 테스트를 수행할 경우에 테스트간 데이터 침범을 막는 용도로 사용한다.
  • JpaRepostiory 의 기본 CRUD 메소드
  • postsRepository.save
    • 테이블 posts에 insert/update 쿼리를 실행한다.
      • id값이 있다면 -> update가 실행되고
      • id값이 없다면 -> insert가 실행된다.
  • postsRepository.findAll()
    • 테이블 posts에 있는 모든 데이터를 조회해오는 메소드이다.

0개의 댓글