[Spring JPA #10] 스프링 데이터 Common 리포지터리(Repository)

2021. 3. 25. 19:50 Spring Data/Spring Data JPA

 

| 스프링 데이터 Common 리포지터리 예제

 

프로젝트 구조

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── tutorial
│   │   │           └── springbootjpa
│   │   │               ├── Post.java
│   │   │               ├── PostRepository.java
│   │   │               ├── SpringBootJpaApplication.java
│   │   └── resources
│   │       ├── application.properties
│   │       ├── static
│   │       └── templates
│   └── test
│       └── java
│           └── com
│               └── tutorial
│                   └── springbootjpa
│                       └── PostRepositoryTest.java

 

의존성 관리

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

 

소스 코드

@Entity
public class Comment {

    @Id
    @GeneratedValue
    private Long id;

    private String comment;

    @ManyToOne
    private Post post;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getComment() {
        return comment;
    }

    public void setComment(String comment) {
        this.comment = comment;
    }

    public Post getPost() {
        return post;
    }

    public void setPost(Post post) {
        this.post = post;
    }
}
@Entity
public class Post {

    @Id
    @GeneratedValue
    private Long id;

    private String title;

    @OneToMany(mappedBy = "post", cascade = CascadeType.ALL)
    private Set<Comment> comments = new HashSet<>();

    public void addComment(Comment comment){
        this.comments.add(comment);
        comment.setPost(this);
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Set<Comment> getComments() {
        return comments;
    }

    public void setComments(Set<Comment> comments) {
        this.comments = comments;
    }

    @Override
    public String toString() {
        return "Post{" +
                "id=" + id +
                ", title='" + title + '\'' +
                '}';
    }
}
public interface PostRepository extends JpaRepository<Post, Long> {

}
  • JpaRepository 인터페이스는 save, findAll, findAllById ... 같은 기본적으로 Repository와 상호작용 할 수 있는 기본 메서드를 제공해줍니다.
  • 프로그래머가 Repository와 인터페이스 할 수 있는 메서드를 직접 구현하는 것도 가능합니다.

 

테스트 코드

@RunWith(SpringRunner.class)
@DataJpaTest
public class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    @Rollback(false)
    public void crudRepository() {
        Post post = new Post();
        post.setTitle("hello spring boot common");
        assertThat(post.getId()).isNull();

        Post newPost = postRepository.save(post);

        assertThat(newPost.getId()).isNotNull();
    }
}
  • @Autowired 어노테이션을 통해서 위에서 작성한 PostRepository 인터페이스의 구현체인 리포지터리 빈을 주입받아 사용할 수 있습니다. 이때는 pom.xml 에서 추가한 H2 인메모리 데이터베이스를 테스트에서 Repository로 사용하게 됩니다.
  • 위 테스트 코드에서 @Rollback(false)을 추가한 이유는 @DataJpaTest에서는 기본적으로 @Transational 어노테이션이 추가되어 있어 기본적으로 각 테스트들이 수행되었을 때 rollback를 하게 됩니다. 따라서 하이버네이트는 rollback될 쿼리는 수행할 필요가 없는 쿼리라 인식하여 Repository에 엔티티를 저장하는 insert문을 날리지 않게 됩니다. 따라서 원하는 테스트를 하기 위해서는 @Roallback(false)라는 어노테이션을 추가하여 rollback을 하지 않겠다는 정보를 부가해야합니다.
  • 위 코드를 실행하게 되면 Post 엔티티에 원래는 할당되지 않는 id 멤버 변수에 postRepository.save(post)를 실행하게 되면서 @GeneratedValue에 의해 값이 할당되게 됩니다. 따라서 위 테스트는 통과됩니다.

 

다음과 같이 페이징 기능도 쉽게 구현할 수 있습니다.

@RunWith(SpringRunner.class)
@DataJpaTest
public class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    @Rollback(false)
    public void crudRepository() {
        Post post = new Post();
        post.setTitle("hello spring boot common");
        assertThat(post.getId()).isNull();

        Post newPost = postRepository.save(post);

        assertThat(newPost.getId()).isNotNull();

        List<Post> posts = postRepository.findAll();

        assertThat(posts.size()).isEqualTo(1);
        assertThat(posts).contains(newPost);

        Page<Post> page = postRepository.findAll(PageRequest.of(0, 10));
        assertThat(page.getTotalElements()).isEqualTo(1);
        assertThat(page.getNumber()).isEqualTo(0);
        assertThat(page.getSize()).isEqualTo(10);
        assertThat(page.getNumberOfElements()).isEqualTo(1);
    }
}

 

| JpaRespository 인터페이스 메서드 추가

 

PostRepository 인터페이스에 다음과 같이 어떤 기능을 구현하기 위한 메서드를 추가하면 쉽게 Repository에 원하는 데이터를 찾아 올 수 있습니다. 단 여기서 Spring Data에서 요구하는 규칙을 만족해야지 제대로 된 기능이 추가됩니다.

 

소스 코드

public interface PostRepository extends JpaRepository<Post, Long> {

    Page<Post> findByTitleContains(String title, Pageable pageable);

    long countByTitleContains(String title);
}
  • findBy로 시작하는 메서드는 쿼리를 요청하는 메서드를 나타내는 것입니다. 이것은 Spring Data에서 요구하는 규칙입니다. 자세한 것은 스프링 문서를 참조하면 될 것 같습니다. findByTitleContains는 Post의 title 멤버변수가 메서드의 title 인수와 같은 엔티티를 찾아 Page 형태로 리턴하는 메서드입니다.
  • contBy로 시작하는 메서드는 쿼리 결과 레코드 수를 요청하는 메서드를 나타냅니다. countByTitleContains는 Post 테이블에서 해당 title에 해당하는 Post들의 개수를 찾아 반환하는 메서드입니다.

 

테스트 코드

@RunWith(SpringRunner.class)
@DataJpaTest
public class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    @Rollback(false)
    public void crudRepository() {
        Post post = new Post();
        post.setTitle("hello spring boot common");
        assertThat(post.getId()).isNull();

        Post newPost = postRepository.save(post);

        assertThat(newPost.getId()).isNotNull();

        List<Post> posts = postRepository.findAll();

        assertThat(posts.size()).isEqualTo(1);
        assertThat(posts).contains(newPost);

        Page<Post> page = postRepository.findAll(PageRequest.of(0, 10));
        assertThat(page.getTotalElements()).isEqualTo(1);
        assertThat(page.getNumber()).isEqualTo(0);
        assertThat(page.getSize()).isEqualTo(10);
        assertThat(page.getNumberOfElements()).isEqualTo(1);

        page = postRepository.findByTitleContains("spring", PageRequest.of(0, 10));
        assertThat(page.getTotalElements()).isEqualTo(1);
        assertThat(page.getNumber()).isEqualTo(0);
        assertThat(page.getSize()).isEqualTo(10);
        assertThat(page.getNumberOfElements()).isEqualTo(1);

        long spring = postRepository.countByTitleContains("spring");
        assertThat(spring).isEqualTo(1);
    }
}

 

https://www.inflearn.com/course/스프링-데이터-jpa



출처: https://engkimbs.tistory.com/822?category=772527 [새로비]