Java

[Spring Boot] Spring Data JPA 연동 방법

cob 2023. 7. 2. 01:08
ORM이란?
객체와 릴레이션(DB 테이블)간 맵핑할 때 발생하는 개념적 불일치를 해결하는 프레임워크

 

Spring Data JPA이란?
ORM(Object-Relational Mapping)을 보다 간편하게 사용할 수 있도록 추상화된 프레임워크입니다.

 

 


1. 개념적 불일치 종류

1. 객체와 테이블 간의 구조적인 불일치: 객체는 클래스와 멤버 변수로 구성되어 있으며, 상속, 다형성, 연관 관계 등의 개념을 가질 수 있습니다. 반면에 테이블은 로우와 컬럼으로 이루어져 있으며, 간단한 데이터 구조만을 표현할 수 있습니다. 객체와 테이블 사이의 이러한 구조적인 차이로 인해 매핑 작업이 필요합니다.

2. 객체 그래프와 테이블 간의 불일치: 객체는 서로 연관되어 있는 그래프 형태로 구성될 수 있습니다. 하지만 테이블은 외래 키를 사용하여 단일 테이블 간의 관계만을 표현할 수 있습니다. 객체 그래프와 테이블 간의 이러한 차이로 인해 매핑 작업이 필요하며, 연관 관계를 관리하고 객체 그래프를 효과적으로 로드하는 작업이 필요합니다.

3. 상속과 테이블 간의 불일치: 객체는 상속 관계를 가질 수 있으며, 상위 클래스와 하위 클래스 간에 데이터 및 메서드를 공유할 수 있습니다. 하지만 테이블은 상속을 직접적으로 지원하지 않으며, 상속을 표현하기 위해 다양한 전략(단일 테이블 전략, 구분 테이블 전략, 조인 테이블 전략 등)을 사용해야 합니다.

4. 객체 그래프와 테이블 간의 크기 불일치: 객체 그래프는 다양한 크기와 복잡성을 가질 수 있습니다. 반면에 테이블은 테이블당 하나의 로우를 가지고 있으며, 테이블 간의 연관 관계를 통해 데이터를 조회하거나 조작합니다. 객체 그래프와 테이블 간의 크기 불일치로 인해 로딩 전략, 지연 로딩, 프록시 객체 등의 기술을 사용하여 효율적인 데이터 로딩을 관리해야 합니다.

5. 데이터 타입 불일치: 객체는 다양한 데이터 타입을 가질 수 있지만, 테이블은 제한된 데이터 타입을 가지고 있습니다. 객체의 데이터 타입과 테이블의 컬럼 데이터 타입 간의 불일치로 인해 데이터 변환 작업이 필요할 수 있습니다.

 

 

 


2. 사용 방법

2-1) 의존성 추가

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

 

2-2) Entity 생성

테이블과 맵핑 시킬 엔티티를 생성한다. 
* Getter, Setter를 작성하기 번거롭다면 Lombok을 사용한다.

2022.10.07 - [Java] - [Spring Boot] 이클립스(Eclipse) 롬복(LOMBOK) 설치 및 어노테이션 (Annotation) 사용 방법

package com.example.demo.entity;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import java.util.Objects;

@Entity
public class Account {

    @Id @GeneratedValue
    private Long id;
    private String username;
    private String password;

    public Long getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }


    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Account account = (Account) o;
        return Objects.equals(id, account.id) &&
                Objects.equals(username, account.username) &&
                Objects.equals(password, account.password);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, username, password);
    }
}
  • @Id : 식별자(primary key) 필드를 나타내는 어노테이션
  • @GeneratedValue : 식별자(primary key) 값을 자동으로 생성하기 위해 사용된다. 이 어노테이션을 사용하면 JPA가 식별자 값을 자동으로 생성하고, 해당 값을 엔티티에 할당하여 데이터베이스에 저장하게 된다.

 

 

2-3) Repository 생성

package com.example.demo.persistence;

import com.example.demo.entity.Account;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface AccountRepository extends JpaRepository<Account, Long> {
    Account findByUsername(String username);


    // NativeQuery 사용 방법
    @Query(nativeQuery = true, value = "select * from account where username='{0}'")
    Account nativeQuety(String username);
}
  • JpaRepository<Entity타입, 식별자 타입>
  • @Query : NativeQuery(순수 쿼리)를 사용할 경우 선언할 때 사용된다.
  • 메서드를 등록하지 않아도 기본적인 CRUD는 자동 구현체를 활용해 사용할 수 있다.

 

 

2-4) DB 세팅 및 연동

2023.07.01 - [Java] - [Spring Boot] DBCP와 MySQL 개념 및 사용 방법

 

 

 


3. Repository  Test 코드 작성

JPA 슬라이스 테스트를 할 때는 인-메모리가 반드시 필요하다.

2023.06.26 - [Java] - [Spring Boot] 인메모리(H2) 데이터베이스 사용 방법

 <dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <scope>test</scope>
</dependency>
  • scope에 test를 준다.

 

package com.example.demo.persistence;

import com.example.demo.entity.Account;
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.jdbc.core.JdbcTemplate;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.DataSource;
import java.sql.SQLException;

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

@RunWith(SpringRunner.class)
@DataJpaTest
//@SpringBootTest
public class AccountRepositoryTest {

    @Autowired
    AccountRepository accountRepository;

    @Autowired
    JdbcTemplate jdbcTemplate;

    @Autowired
    DataSource dataSource;

    @Test
    public void id() throws SQLException {
        Account account = new Account();
        account.setUsername("coco");
        account.setPassword("pass");

        Account newAccount = accountRepository.save(account);
        assertThat(newAccount).isNotNull(); // 등록된 account 확인

        Account existingAccount = accountRepository.findByUsername(newAccount.getUsername());
        assertThat(existingAccount).isNotNull();    // username으로 가져온 account 확인

        Account nonExistingAccount = accountRepository.findByUsername("baba");
        assertThat(nonExistingAccount).isNull(); // 존재하지 않은 account 확인
    }

}
  • @DataJpaTest : 슬라이스 테스트(Repository를 포함한 관련된 Bean들만 등록)로 DataSource, JdbcTemplate, 생성한 Repository 등을 사용할 수 있다.
  • @SpringBootTest : 모든 Bean을 등록하기 때문에 application.properties도 등록되어 H2 인메모리 데이터베이스가 아니라 애플리케이션에  application.properties 설정된 데이터 베이스를 사용하게 된다. 
  • 테스트로 확인할 경우 인메모리 데이터 베이스가 훨씬 빠르기 때문에 인메모리 데이터베이스를 사용한다.

 

반응형