03 스프링 부트에서 JPA로 데이터베이스를 다뤄보자

3.2 프로젝트에 Spring Data Jpa적용하기


1. build.gradle에 다음과 같이 의존성들을 등록합니다.

implementation('org.springframework.boot:spring-boot-starter-data-jpa')
implementation('com.h2database:h2')

2. com.jojoldu.book.springboot 하위에 domain 과 그 하위에 posts 패키지를 만든고 Posts클래스를 만든다.

@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;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }
}

 

새언어의 전환으로 롬복이 더이상 필요없을 경우 쉽게 삭제하기위해서 이 책의 필자는 주요 어노테이션을 클래스에 가깝게 둔다고합니다. 

@Entity - 테이블과 링크될 클래스임을 나타냅니다. 

@GeneratedValue - PK의 생성 규칙을 나타냅니다. 

참고 

더보기

웬만하면 Entity의 PK는 Long타입의 Auto_increment를 추천합니다. 주민등록번호와 같은 비즈니스상 유니크 키나, 여러키를 조합한 복합키를 PK로 잡을 경우 난감한 상황이 종종 발생합니다. 

  1. FK를 맺을 때 다른 테이블에서 복합키 전부를 갖고 있거나, 중간 테이블을 하나 더 둬야  하는 상황이 발생합니다.
  2. 인덱스에 좋은 영향을 끼치지 못합니다.
  3. 유니크한 조건이 변경될 경우 PK 전체를 수정해야 하는 일에 발생합니다. 주민등록번호, 복합키 등은 유니크 키로 별도로 추가하시는 것을 추천드립니다.

3. Posts클래스 생성이 끝났다면 Posts클래스로 Database를 접근하게 해줄 JpaRepository를 생성합니다.

public interface PostsRepository extends JpaRepository<Posts, Long> {
}

 

3.3 SpringDAtaJpa 테스트 코드 작성하기


 

1. 테스트를 진행하기위해 PostsRrpositoryTest 를 같은경로에 만들어주세요

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

    @Autowired
    PostsRepository postsRepository;

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

    @Test
    public void 게시글저장_불러오기() {
        //given
        String title = "테스트 게시글";
        String 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에서 단위 테스트가 끝날 때마다 수행되는 메소드를 지정

2. 테스트를 통과 했다면 이제 실제로 실행된 쿼리는 어떤 형태일까?에 집중합니다. src/main/resources 디렉토리 아래 application.properties 파일을 생성하고 다음 코드를 작성합니다.  

spring.jpa.show_sql=true

그리고 다시 테스트를 실행하고 콘솔을 확인하면

Hibernate: create table posts (id bigint generated by default as identity, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id)) 

Hibernate: insert into posts (id, author, content, title) values (null, ?, ?, ?) 쿼리문이 있는걸 확인 할 수 있습니다.

그리고 이후 출력되는 쿼리 로그를 mysql버전으로 변경하겠습니다. 다음 코드를 추가로 .preperties에 추가합니다.

spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect

다시 테스트를 돌리고 콘손을 확인하면 좀 다르다는 것을 확인 할 수 있습니다.

Hibernate: create table posts (id bigint not null auto_increment, author varchar(255), content TEXT not null, title varchar(500) not null, primary key (id)) engine=InnoDB

Hibernate: insert into posts (author, content, title) values (?, ?, ?)

3.4 등록/수정/조회 API 만들기


 

1.  다음 처럼 빨간색 가로줄이 있는 패키지와 클래스를 생성해준다.

순서 대로 클래스들이다.

@RequiredArgsConstructor
@Service
public class PostsService {
    private final PostsRepository postsRepository;

    @Transactional
    public Long save(PostsSaveRequestDto requestDto) {
        return postsRepository.save(requestDto.toEntity()).getId();
    }
}
@Getter
@NoArgsConstructor
public class PostsSaveRequestDto {
    private String title;
    private String content;
    private String author;

    @Builder
    public PostsSaveRequestDto(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public Posts toEntity() {
        return Posts.builder()
                .title(title)
                .content(content)
                .author(author)
                .build();
    }
}
@RequiredArgsConstructor
@RestController
public class PostsApiController {

    private final PostsService postsService;

    @PostMapping("/api/v1/posts")
    public Long save(@RequestBody PostsSaveRequestDto requestDto) {
        return postsService.save(requestDto);
    }
}

@Autowired는 권장하지 않는다고합니다. @RequiredArgsConstructor를 이용하여 생성자를 통해 bean으로 받아온것입니다.

 

 

복사했습니다!