가자공부하러!

Spring Data Common(1) - 개발환경 설정, 기초사용방법 본문

공부/Spring Boot

Spring Data Common(1) - 개발환경 설정, 기초사용방법

오피스엑소더스 2019. 11. 27. 14:35

1. 개발환경 설정

1.1. 개발환경

  - Spring Boot 2.2.1

  - Java 8

  - JPA v2.*, Hibernate v5.*

  - postgres DB

1.2. 설정파일

  - application.yml : datasource, ddl-auto option

  - JpaRunner.class : JPA의 핵심인 EntityManager를 가지고 엔티티 영속화 작업을 수행해주는 클래스

 

2. Spring Data

2.1. 역할 : SQL, NoSQL 저장소 지원 프로젝트의 묶음

2.2. 구성

  - Spring Data Common : 여러 저장소 지원 프로젝트의 공통 기능 제공

  - Spring Data REST : 저장소의 데이터를 하이퍼미디어 기반 HTTP 리소스, REST API리소스로 제공하는 프로젝트

  - Spring Data JPA : Spring Data Common이 제공하는 기능에 추가적으로 JPA 관련 기능 제공

  - Spring Data JDBC

  - Spring Data KeyValue

  - Spring Data MongoDB

  - Spring Data Redis

  - Spring Data ...

 

3. Spring Data Common - Repository 기초 사용방법

3.1. 구조

  - JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> extends Repository<T, ID>

  - Repository 인터페이스는 마커 인터페이스

  - @NoRepositoryBean : 다른 저장소용 레파지토리가 실제 빈을 만들어서 등록하는 것을 방지하기 위함 == 실제 레파지토리가 아님을 알려줌

  - CrudRepository

<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();

  - PagingAndSorting

Iterable<T> findAll(Sort sort);
Page<T> findAll(Pageable pageable);

  - JpaRepository : 오버라이딩 메소드 다수

@Override List<T> findAll();
@Override List<T> findAll(Sort sort);
@Override List<T> findAllById(Iterable<ID> ids);
@Override <S extends T> List<S> saveAll(Iterable<S> entities);
void flush();
<S extends T> S saveAndFlush(S entity);
void deleteInBatch(Iterable<T> entities);
void deleteAllInBatch();
T getOne(ID id);
@Override <S extends T> List<S> findAll(Example<S> example);
@Override <S extends T> List<S> findAll(Example<S> example, Sort sort);

3.2. 사용방법

  - 적당한 레파지토리 상속받아서 쓰면 됨

    > 페이징 쿼리

package com.exam.demo.repo;

import com.exam.demo.domain.Post;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

/**
 * JpaRepository가 갖는 메소드를 사용할 수 있으면서 직접 정의한 메소드도 사용할 수 있다
 */
public interface PostRepository extends JpaRepository<Post, Long> {

    //특정 키워드를 갖는 엔티티의 수
    long countByTitleContains(String title);

    //특정 키워드를 갖는 목록을 찾는 메소드
    Page<Post> findByTitleContains(String title, Pageable pageable);

}
package com.exam.demo.repo;

import com.exam.demo.domain.Post;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@RunWith(SpringRunner.class)
@DataJpaTest //Data Access Layer에서만, Transactional옵션이 들어있기 때문에 아래 Test들에 대한 내용은 모두 롤
//@DataJpaTest는 디펜던시에 H2 DB가 있으면 H2에서만 테스트돌림(원래 쓰던 DB 말고)
public class PostRepositoryTest {

    @Autowired
    PostRepository postRepository;

    @Test
    @Rollback(false)//Transactional 옵션 때문에 하이버네이트가 쿼리를 안보내기 때문에 Rollback false로 설정해야 테스트 가
    public void repositoryCrudTest(){
        //Given
        Post post = new Post();
        post.setTitle("hello spring boot common");
        Post newPost = postRepository.save(post);

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

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

  - 공통으로 사용할 메소드를 모은 Repository를 직접 정의해서 사용 가능

package com.exam.demo.repo;

import com.exam.demo.domain.Comment;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;

import java.io.Serializable;
import java.util.List;

/**
 * 여러 레파지토리에서 공통으로 사용하고 싶은 메소드가 있을 때 공통 메소드들을 설정하는 방법
 */
@NoRepositoryBean
public interface MyRepository<T, Id extends Serializable> extends Repository<T, Id> {

    //타입 T와 T를 상속받는 하위타입 E까지 허용
    <E extends T> E save(E entity);

    List<T> findAll();

    long count();

}
package com.exam.demo.repo;

import com.exam.demo.domain.Comment;
import org.springframework.data.repository.RepositoryDefinition;

import java.util.List;

/**
 * Repository나 JpaRepository를 상속받을 때 너무 많은 메소드가 생기는게 싫은 경우
 * @RepositoryDefinition 애노테이션을 활용하게되면
 * 내가 정의한 메소드만 사용할 수 있도록 설정이 가능하다.
 *
 * 공통메소드를 모아둔 MyRepository가 Repository를 상속받는 경우에도
 * MyRepository에 명시된 메소드만 사용할 수 있게된다.
 */
@RepositoryDefinition(domainClass = Comment.class, idClass = Long.class)
public interface CommentRepository extends MyRepository<Comment, Long>{
}
@Entity
@Getter @Setter
public class Comment {
    @Id @GeneratedValue
    private Long id;

    private String comment;
}

 

 

Comments