본문 바로가기

WEB/Java

스프링 부트와 AWS로 혼자 구현하는 웹 서비스 - 03-1 JPA 적용

JPA 란

  • JPA는 인터페이스로서 자바 표준명세서이다
  • 따라서 인터페이스인 JPA를 사용하기 위해서는 구현체가 필요하다
  • 대표적인 구현체로는 Hibernate, Eclipse Link 등이 있는데, 이걸 직접 사용하진 않는다.
  • 아래의 이유로, 위 구현체를 좀 더 쉽게 추상화시킨 Spring Data JPA 라는 모듈을 사용함.
    • 구현체 교체의 용이성
    • 저장소 교체의 용이성
 

 

build.gradle 에 JPA 관련 의존성 추가

//jpa
implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('com.h2database:h2')
  • h2 는 인메모리 관계형 데이터베이스로, 별도로 설치가 필요하지 않다. 이 프로젝트에서는 테스트용, 로컬 환경에서의 구동용으로 사용할 예정.

 

Posts 엔티티 클래스 작성

package com.example.boilerplate.domain.posts;

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

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
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;
    }
}

 

@Entity
• 테이블과 링크될 클래스임을 나타냅니다.
• 기본값으로 클래스의 카멜케이스 이름을 언더스코어 네이밍(_)으로 테이블 이름을 매 칭합니다.
• ex) SalesManager.java > sales_manager table 

@Id
• 해당 테이블의 PK 필드를 나타냅니다.

@GeneratedValue
• PK의 생성 규칙을 나타냅니다.
• 스프링 부트 2.0 에서는 GenerationType.IDENTITY 옵션을 추가해야만 auto_ increment가 됩니다.

@Column
• 테이블의 칼럼을 나타내며 굳이 선언하지 않더라도 해당 클래스의 필드는 모두 칼럼 이 됩니다.
• 사용하는 이유는, 기본값 외에 추가로 변경이 필요한 옵션이 있으면 사용합니다.
• 문자열의 경우 VARCHAR(255)가 기본값인데, 사이즈를 500으로 늘리고 싶거나(ex: title), 타입을 TEXT로 변경하고 싶거나(ex: content) 등의 경우에 사용됩니다.
@NoArgsConstructor
• 기본 생성자 자동 추가
• public Posts () {} 와 같은 효과

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

Builder 패턴

 

JpaRepository 생성

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

public interface PostsRepository extends JpaRepository<Posts, Long> {
}
  • interface 로 생성된다.
  • JpaRepository<Entity 클래스, PK 타입>를 상속하면 기본 적인 CRUD 메소드가 자동으로 생성된다.

Repository Test 코드 작성 : save(), findAll() 기능 테스트

package com.example.boilerplate.domain.posts;

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 게시글저장_불러오기() {
        //given
        String title = "test post";
        String content = "test content";

        postsRepository.save(Posts.builder()
                                 .title(title)
                                 .content(content)
                                 .author("jojoldu@gmail.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에 있는 모든 데이터를 조회해오는 메소드입니다.