본문 바로가기
Spring/Spring Data JPA

[Spring Data JPA] JPA Entity, Repository, Service 클래스 작성 (조회 및 저장) - QuickStart 2

by 임채훈 2021. 12. 12.

2021.12.12 - [Spring/Spring Data JPA] - [Spring Data JPA] 예제 프로젝트 생성 및 초기 환경 구성 - QuickStart 1

 

이전글의 내용을 이어서 작성합니다.

 

# 해당 시리즈 게시글은 Notion에서 작성된 내용을 그대로 옮겨오는 과정에서 서식의 깨짐 및 부자연스러움이 발생할 수 있습니다. 

 

Entity 클래스 및 모델 클래스 작성

  • io.starter.jpatutorial.domain.jpo.PostJpo
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "post")
public class PostJpo {
    /**
     * 게시글 번호 (Auto Increment)
     */
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long no = 0L;

    /**
     * 게시글 제목
     */
    private String title;

    /**
     * 게시글 내용
     */
    private String content;

    /**
     * 게시글 작성 일시
     */
    private LocalDateTime createdAt = LocalDateTime.now();

    /**
     * 조회수
     */
    private int views = 0;
}
  • io.starter.jpatutorial.domain.model.Post
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Post {
    private Long no;
    private String title;
    private String content;
    private LocalDateTime createdAt;
    private int views;

        /**
     * Jpo -> Domain 객체 변환
     */
    public static Post jpoOf(PostJpo postJpo) {
        Post post = new Post();
        BeanUtils.copyProperties(postJpo, post);
        return post;
    }

    /**
     * Domain 객체 -> Jpo 변환
     */
    public PostJpo asJpo() {
        PostJpo postJpo = new PostJpo();
        BeanUtils.copyProperties(this, postJpo);
        return postJpo;
    }
}

Jpo는 Java Persistence Object로 영속성 Layer에서 Database와 Application Layer에서 사용을 하기 위한 클래스입니다.

쉽게 말하여 Application 레벨에서 Database 와의 데이터 조회 및 갱신은 Entity 객체로 이루어져야 되는 것이고, View 또는 데이터가 보여지는 즉 Client와 데이터를 주고 받는 영역에서 DTO 클래스를 통해 이루어져야 됩니다.

그에 따라 Jpo ↔ Dto 클래스 간에는 속성이 흡사할 수 있고, 서로 변환이 가능한 구조로 만들어줍니다.

해당 내용을 도식화해보면 다음과 같습니다.

 

Repository Interface 작성

  • io.starter.jpatutorial.domain.repository.PostMariaRepository
@Repository
public interface PostMariaRepository extends JpaRepository<PostJpo, Long> {
}

Spring Data JPA 모듈에서 제공하는 Repository Interface는 대표적으로 CrudRepository, JpaRepository, PagingAndSortingRepository 등이 있습니다.

용도 및 환경에 따라 다를 수 있으나 일반적으로 간단하게 가장 많이 사용되는 JpaRepository을 상속받아 Generic 구체 타입은 특정 Entity 클래스와 해당 클래스의 @Id가 되는 필드의 타입을 선언합니다.

JpaRepository 클래스 기준으로 보면 다음과 같은 메소드들이 이미 선언이 되어있으며 이미 명칭에 따라 구현된 로직으로 실행이 됩니다.

  • save
  • saveAll
  • findById
  • findAll
  • exists
  • deleteById
  • deleteAll

 

Service 클래스 조회 로직 작성

  • io.starter.jpatutorial.service.PostListService
@Service
@RequiredArgsConstructor
public class PostListService {
    private final PostMariaRepository postMariaRepository;

    @Transactional(readOnly = true)
    public List<Post> fetch() {
        return postMariaRepository.findAll()
                .stream()
                .map(Post::jpoOf)
                .collect(Collectors.toList());
    }
}

여기까지 작성이 되고난 후 현재의 프로젝트 패키지 구조는 아래와 같습니다.

 

Entity 조회(Select) 테스트

  • io.starter.jpatutorial.JpaTutorialApplicationTests
package io.starter.jpatutorial;

import io.starter.jpatutorial.domain.model.Post;
import io.starter.jpatutorial.service.PostListService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class JpaTutorialApplicationTests {
    @Autowired
    private PostListService postListService;

    @Test
    @DisplayName("게시글 목록 전체 조회 테스트")
    void fetch() {
        List<Post> posts = postListService.fetch();
        Assertions.assertNotNull(posts);
        System.out.println("Posts --> " + posts);
    }
}

간단하게 Junit을 활용한 테스트 코드를 작성해줌으로 손쉽게 테스트를 해볼 수 있습니다.

최초에 실행할 시 별도로 데이터베이스에서 테이블을 생성해주지 않았는데, spring.jpa.hibernate.ddl-auto 설정을 해줌으로 인해 서비스가 Bootstrap 되는 과정에서 내가 설계한 Entity 클래스의 내용을 바탕으로 자동으로 테이블을 생성해주는것을 알 수 있습니다.

또한 지금은 임의의 데이터를 생성해주지 않았음으로 결과 레코드는 조회가 되지 않는것이 정상입니다.

추가로 임의로 Entity에 필드를 추가하거나하는 등의 스키마 구조가 변경되는 경우 아래에서 확인할 수 있듯이 자동으로 데이터베이스 테이블 속성 변경이 이루어지는것도 알 수 있습니다.

 

Service 클래스 저장(Insert) 로직 작성

  • io.starter.jpatutorial.service.PostListService
@Service
@RequiredArgsConstructor
public class PostListService {
    private final PostMariaRepository postMariaRepository;

    @Transactional(readOnly = true)
    public List<Post> fetch() {
        return postMariaRepository.findAll()
                .stream()
                .map(Post::jpoOf)
                .collect(Collectors.toList());
    }

    @Transactional
    public void save(List<Post> posts) {
        List<PostJpo> postJpos = posts.stream()
                .map(Post::asJpo)
                .collect(Collectors.toList());

        postMariaRepository.saveAll(postJpos);
    }
}

 

Entity 저장(Insert) 로직 테스트

  • io.starter.jpatutorial.JpaTutorialApplicationTests
package io.starter.jpatutorial;

import io.starter.jpatutorial.domain.model.Post;
import io.starter.jpatutorial.service.PostListService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
class JpaTutorialApplicationTests {
    @Autowired
    private PostListService postListService;

    @Test
    @DisplayName("게시글 목록 전체 조회 테스트")
    void fetch() {
        List<Post> posts = postListService.fetch();
        Assertions.assertNotNull(posts);
        System.out.println("Posts --> " + posts);
    }

    @Test
    @DisplayName("게시글 생성 테스트")
    void save() {
        List<Post> posts = Arrays.asList(
                Post.builder().title("게시글 1").content("게시글 1 내용").build(),
                Post.builder().title("게시글 2").content("게시글 2 내용").build(),
                Post.builder().title("게시글 3").content("게시글 3 내용").build()
        );
        postListService.save(posts);
    }
}

임의의 임시 Post 객체들을 생성하여 save 로직을 수행하면 아래와 같이 insert SQL문이 수행되면서 데이터베이스에 정상적으로 생성됨을 확인할 수 있습니다.


프로젝트 전체 소스 코드는 아래 Github에서 참고 가능합니다.

Github Source Code

 

GitHub - youspend8/spring-data-jpa-tutorial

Contribute to youspend8/spring-data-jpa-tutorial development by creating an account on GitHub.

github.com

 

댓글