[Spring] 입문-7. Jpa / Spring Data Jpa / 통합 테스트

Jina·2023년 6월 20일
0

Spring

목록 보기
7/9

다음의 모든 내용은 김영한님의 스프링 입문 강의에서 가져온 것임을 밝힙니다.

📍 Jpa

Jpa란?

Java 표준 ORM (object reltaional mapping) 기술로, 객체를 매핑한다.

cf. MyBatis는 ORM이 아니라 SQL Mapper로, 쿼리를 매핑한다!

Jpa의 필요성

JDBC에서의 반복 코드 제거는 물론, 기본적인 SQL도 만들어서 실행해준다!

따라서 SQL과 데이터 중심 -> 객체 중심 설계로의 패러다임 전환이 가능!
개발 생산성을 크게 향상시킬 수 있고, 유지보수가 쉬워졌다.

사용 방법

1. build.gradle에 jpa 라이브러리 추가

spring-boot-starter-data-jpa는 내부에 Jdbc 관련 라이브러리를 포함하고 있으니, 기존의 Jdbc 라이브러리는 제거해도 된다.

	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'	//jpa 관련 라이브러리

2. resources의 application.properties

  • show-sql = true : JPA가 생성하는 SQL을 출력해준다.
spring.jpa.show-sql = true

jpa는 객체(여기선 Member)를 보고 테이블도 자동으로 생성하지만,
현재는 먼저 만들어둔 상태이므로 none으로 꺼준다. (create면 자동으로 생성)

spring.jpa.show-sql = true

3. domian의 Member.java 파일

  • @Entity : javax가 관리하는 객체가 됨
  • @Id : PK(primary key)가 됨
  • @GeneratedValue : IDENTITY로 설정하면, db가 자동으로 생성한 값
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Entity
public class Member {

    /**
     * 요구 데이터
     */
    @Id @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;        //회원 id를 pk로 설정
    
    private String name;    //회원 이름

4. repository에 JpaMemberRepository 생성

드디어 jpa를 활용한 리포지토리로 바꿔볼 시간이다!

JpaMemberRepositorysms MemberRepository를 implement 한다.

EntityManager

    private final EntityManager em;
    public JpaMemberRepository(EntityManager em){
        this.em = em;
    }

PK를 이용하는 저장, 조회는 이대로만

  • INSERT : em.persist()
  • SELECT by ID : em.find()

이외의 pk 기반이 아닌 것들은 Jpql 작성이 필요

  • Find all :
    테이블이 아닌 entity 대상의 쿼리로, 엔티티 자체를 셀렉트 한다. 이후 jpa에 의해 SQL로 변환된다!
    @Override
    public List<Member> findAll() {
        return em.createQuery("select m from Member m", Member.class)
                .getResultList();   
    }
  • Find by name :
   @Override
    public Optional<Member> findByName(String name) {
        List<Member> result = em.createQuery("select m from Member m where m where m.name = name", Member.class)
                .setParameter("name", name)
                .getResultList();
        
        return result.stream().findAny();
    }

5. @Transaction

jpa 사용시에는 모든 데이터 변경이 transaction 안에서 실행되어야 한다! 따라서 service 파일에 @Transaction을 붙여주자.

스프링은 @Transaction이 붙은 클래스의 메소드를 실행할 때 transaction을 시작하고, 메소드가 정상 종료되면 transaction을 커밋한다. (런타임 예외시 롤백)

@Transactional
public class MemberService {
//중략

6. Config 파일

  • SpringConfig.java
    기존 Jdbc를 쓰기 위해 만들었던 DataSource 관련 코드는 삭제하고, JPA를 위해 EntityManager를 만들었다.
/**
     * For jdbc
    private final DataSource dataSource;
    @Autowired
    public SpringConfig(DataSource dataSource){
        this.dataSource = dataSource;
    }**/

    /**
     * For JPA
     */
    private EntityManager em;
    @Autowired
    public SpringConfig(EntityManager em){
        this.em = em;
    }

이후 Jpa로 만든 리포지토리를 리턴하면 된다!

    @Bean
    public MemberService memberService(){
        return new MemberService(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository(){
        //return new MemoryMemberRepository();
        //return new JdbcTemplateMemberRepository(dataSource);    // Jdbc template 사용
        return new JpaMemberRepository(em);     //JPA 사용
    }

7. 테스트

참고: Jpa는 인터페이스이고, 실제 구현체는 다양한 오픈소스들이 있다! 기본값은 Hibernate이다.

이처럼 Hibernate가 SQL문을 만들어주고 있다.

[주의] 당연히 H2 데이터베이스를 실행하는 중이어야 한다!


📍 스프링 데이터 JPA

스프링 데이터 JPA는 반복되던 기본 CRUD 기능까지 모두 제공한다! 띠리서 repository에 구현 클래스 없이, 인터페이스 만으로 개발 가능한 것!

[주의] 스프링 데이터 JPA는 JPA를 편리하게 사용하도록 도와주는 기술이다! 따라서 JPA를 먼저 학습해야 한다. 스프링 부트와 JPA라는 기반 위에, 스프링 데이터 JPA라는 프레임워크를 더해보자

1. 스프링 데이터 JPA를 사용하는 리포지토리 새로 생성

SpringDataJpaMemberRepository 인터페이스를 먼저 만들었다.

이 인터페이스는 JpaRepository<Member, Long> 인터페이스와 기존에 만들었던 MemberRepository를 extends 한다!

스프링 데이터 JPA는 인터페이스가 JpaRepository를 extends 하면,
구현체를 만들어 해당 인터페이스를 스프링 Bean에 자동으로 등록해준다. (Proxy 기술 사용)

JpaRepository가 기본적인 CRUD, findAll, findById 등 기본 메소드를 모두 제공하니, extends 해서 가져다 쓰면 되는 것!


또한, name을 기준으로 찾고싶을 땐 findByName(String name) 등.. 규칙에 따라 이름만 잘 설정해도 만들어준다.

  • SpringDataJpaMemberRepository.java
package hello.hellospring.repository;

import hello.hellospring.domain.Member;
import org.springframework.data.jpa.repository.JpaRepository;

import java.util.Optional;

public interface SpringDataJpaMemberRepository extends JpaRepository<Member, Long>, MemberRepository {

    @Override
    Optional<Member> findByName(String name);
}

2. Config 파일 수정

  • SpringConfig.java
    private final MemberRepository memberRepository;

    @Autowired
    public SpringConfig(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }


    @Bean
    public MemberService memberService(){
//        return new MemberService(memberRepository()); // For JPA
        return new MemberService(memberRepository);
    }

[참고] 실무에서는 JPA와 스프링 데이터 JPA를 기본으로 사용하고, 복잡한 동적 쿼리는 Querydsl이라는 라이브러리를 사용한다!

Querydsl을 사용하면 쿼리를 자바 코드로 안전하게 작성할 수 있고, 동적 쿼리도 편리하게 작성할 수 있다. 이 조합으로도 해결하기 어려운 쿼리는 JPA가 제공하는 네이티브 쿼리를 사용하거나, 앞서 학습한 스프링 JdbcTemplate를 사용한다.

0개의 댓글