본문 바로가기
Java, Kotlin, Spring/Spring, Spring Boot

Springboot - Spring Data JPA

by Wordbe 2021. 1. 20.
728x90

Springboot 014 - Spring Data JPA

 

ORM (Object Relational Mapping)

자바 코드는 객체 지향 프로그래밍인데 이를 릴레이션과 매핑할 때 개념적으로 불일치가 일어난다.

예를들면 객체의 다양한 크기들을 데이터 테이블에 어떻게 매핑을 시킬 수 있는가? 객체는 상속이 있지만, 테이블은 상속이 없는데 어떻게 매핑 할 것인가? Identity 는 객체의 경우 해쉬코드, equals method 를 통해 알지만, 테이블의 경우 인덱스 컬럼이 있다. 서로 다른 Object 와 Relation은 어떻게 일치시키는가? 에 관한 문제가 있다.

ORM 은 이를 해결하기 위한 프레임워크고, 자바 (EE) ORM 표준으로 JPA 가 있다.

스프링 데이터 JPA는 위에서 말한 JPA를 스프링 데이터 기반으로 추상화 시켜놓은 것이다. 그 구현체로 hibernate를 사용한다.

스프링 데이터 JPA는 Repository를 빈으로 자동 등록하며, 쿼리 메소드를 자동으로 구현한다. @EnableJpaRepositories 를 어노테이션으로 붙이면 스프링부트가 자동으로 설정해준다.

  • 추상화 : 스프링 데이터 JPA → JPA → Hibernate → Datasource

 

 


JPA test 예제

 

SDJ를 사용해보자.

JPA 의존성을 추가한다.

 

pom.xml

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

 

Entity (릴레이션, 테이블) 을 클래스에 매핑하자.

@Getter @Setter @EqualsAndHashCode
@Entity
public class Food {

    @Id
    @GeneratedValue
    private Long id;

    private String name;

    private String category;
}

 

lombok 의 @EqulasAndHashCode 는 아래의 코드를 만들어준다.

@Override
public boolean equals(Object o) {
  if (this == o) return true;
  if (o == null || getClass() != o.getClass()) return false;
  Food food = (Food) o;
  return Objects.equals(id, food.id) && Objects.equals(name, food.name) && Objects.equals(category, food.category);
}

@Override
public int hashCode() {
  return Objects.hash(id, name, category);
}

 

레포지토리를 만들어준다. DAO 와 비슷하다.

public interface FoodRepository extends JpaRepository<Food, Long> {
}

 

슬라이싱 테스트를 위해서는 인메모리 DB 를 사용하면 편리하다.

테스트용 DB를 따로 만들지 않아도 되고, 메모리 위에서 작동하니 빠르게 실행된다.

따라서 test 스코프로 h2 DB 의존성을 추가하자.

 

pom.xml

<dependency>
  <groupId>com.h2database</groupId>
  <artifactId>h2</artifactId>
  <scope>test</scope>
</dependency>

일단 올바르게 동작하는지 확인해보자. 로거로 URL, 드라이버이름, 사용자이름도 찍어본다.

@DataJpaTest 는 슬라이싱테스트를 지원해준다.

@SpringBootTest 는 모든 빈들을 다 검사하는 통합테스트이므로, 슬라이싱테스트가 효율적이다.

@DataJpaTest
class FoodRepositoryTest {

    Logger logger = LoggerFactory.getLogger(FoodRepositoryTest.class);

    @Autowired
    DataSource dataSource;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    FoodRepository foodRepository;

    @Test
    public void testFood() throws SQLException {
        try (Connection connection =  dataSource.getConnection()) {
            DatabaseMetaData metaData = connection.getMetaData();
            logger.info(metaData.getURL());
            logger.info(metaData.getDriverName());
            logger.info(metaData.getUserName());
        }
    }
}

 

잘 작동하면 데이터를 삽입(save)하고, 조회(find)하여 올바른지 확인해보자.

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

@DataJpaTest
class FoodRepositoryTest {

    @Autowired
    DataSource dataSource;

    @Autowired
    FoodRepository foodRepository;

    @Test
    public void testFood() throws SQLException {
        Food food1 = new Food();
        food1.setName("strawberry");
        food1.setCategory("fruit");

          // INSERT
        Food food = foodRepository.save(food1);
        assertThat(food).isNotNull();

          // SELECT
        Optional<Food> foodName = foodRepository.findByName(food.getName());
        assertThat(foodName).isNotEmpty();

          // SELECT
        Optional<Food> nonExistingFoodName = foodRepository.findByName("apple");
        assertThat(nonExistingFoodName).isEmpty();
    }
}
public interface FoodRepository extends JpaRepository<Food, Long> {
    Optional<Food> findByName(String name);
}

레포지토리에 새로운 메소드를 정의해서 사용해보자. 이름으로 객체를 찾는다. 없으면 null을 반환한다.

 

 

 

 

728x90

'Java, Kotlin, Spring > Spring, Spring Boot' 카테고리의 다른 글

Spring Data - Neo4j 사용  (0) 2021.01.20
Spinrg data 초기화, migration  (0) 2021.01.20
Spring Data - PostgreSQL  (0) 2021.01.20
Spring Data - MySQL  (0) 2021.01.20
Spring Data - 인메모리 DB  (0) 2021.01.20

댓글