반응형

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

반응형

| 스프링 데이터 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


반응형

이 글을 공유하기

댓글

Designed by JB FACTORY