스프링

 

중급자를 위해 준비한
[웹 개발, 백엔드] 강의입니다.

스프링 데이터 JPA는 기존의 한계를 넘어 마치 마법처럼 리포지토리에 구현 클래스 없이 인터페이스만으로 개발을 완료할 수 있습니다. 그리고 반복 개발해온 기본 CRUD 기능도 모두 제공합니다. 스프링 데이터 JPA 실무 노하우를 전해드립니다.

✍️
이런 걸
배워요!

스프링 데이터 JPA를 기초부터 실무 활용까지 한번에 배울 수 있습니다.

실무에서 실제 사용하는 기능 위주로 학습합니다.

단순한 기능 설명을 넘어 실무 활용 노하우를 배울 수 있습니다.

JPA와 스프링 데이터 JPA의 차이를 명확하게 이해할 수 있습니다.

 

강좌 : https://www.inflearn.com/course/스프링-데이터-JPA-실전#

 

 

 

수업자료 : 

https://github.com/braverokmc79/jpa-basic-lecture-file2

 

 

 

소스 : https://github.com/braverokmc79/data-jpa

 

 

 

 

 

 

 

 

 

 

 

 

 

[3] 공통 인터페이스 기능

 

 

 

 

 

 

6. 순수 JPA 기반 리포지토리 만들기

 

강의 :

https://www.inflearn.com/course/lecture?courseSlug=스프링-데이터-JPA-실전&unitId=28004&tab=curriculum

 

 

공통 인터페이스 기능


순수 JPA 기반 리포지토리 만들기


스프링 데이터 JPA 공통 인터페이스 소개


스프링 데이터 JPA 공통 인터페이스 활용

 

 

 

 

순수 JPA 기반 리포지토리 만들기


순수한 JPA 기반 리포지토리를 만들자
기본 CRUD


저장
변경     :  => 변경감지 사용
삭제
전체 조회
단건 조회
카운트

 

 

> 참고: JPA에서 수정은 변경감지 기능을 사용하면 된다.   더티체킹


> 트랜잭션 안에서 엔티티를 조회한 다음에 데이터를 변경하면, 트랜잭션 종료 시점에 변경감지 기능이


작동해서 변경된 엔티티를 감지하고 UPDATE SQL을 실행한다.

 

 

 

순수 JPA 기반 리포지토리 - 회원

 

package study.datajpa.repository;

import org.springframework.stereotype.Repository;
import study.datajpa.entity.Member;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.swing.text.html.Option;
import java.util.List;
import java.util.Optional;

@Repository
public class MemberJpaRepository {

    @PersistenceContext
    private EntityManager em;

    public Member save(Member member){
        em.persist(member);
        return  member;
    }

    public void delete(Member member){
        em.remove(member);
    }


    public List<Member> findAll(){
        return  em.createQuery("select m from Member m ", Member.class).getResultList();
    }

    public Optional<Member> findById(Long id){
        Member member=em.find(Member.class, id);
        return Optional.ofNullable(member);
    }

    public long count(){
        return  em.createQuery("select count(m) from Member m", Long.class).getSingleResult();
    }



    public Member find(Long id){
        return  em.find(Member.class, id);
    }




}

 

 

 

순수 JPA 기반 리포지토리 - 팀

package study.datajpa.repository;


import org.springframework.stereotype.Repository;
import study.datajpa.entity.Team;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.util.List;
import java.util.Optional;

@Repository

public class TeamJpaRepository {

    @PersistenceContext
    private EntityManager em;


    public Team save(Team team){
        em.persist(team);
        return team;
    }

    public void delete(Team team){
        em.remove(team);
    }


    public List<Team>  findAll(){
        return em.createQuery("select t from Team t", Team.class).getResultList();
    }


    public Optional<Team> findById(Long id){
        Team team=em.find(Team.class, id);
        return Optional.ofNullable(team);
    }


    public long count(){
        return em.createQuery("select count(t) from Team t", Long.class).getSingleResult();
    }





}

 

회원 리포지토리와 거의 동일하다.

 

순수 JPA 기반 리포지토리 테스트

package study.datajpa.repository;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import study.datajpa.entity.Member;

import java.util.List;

@SpringBootTest
@Transactional
@Rollback(value = false)
class MemberJpaRepositoryTest {

    @Autowired
    MemberJpaRepository memberJpaRepository;


    @Test
    public void testMember(){
        Member member =new Member("memberA");
        Member saveMember = memberJpaRepository.save(member);

        Member findMember = memberJpaRepository.find(saveMember.getId());

        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        Assertions.assertThat(findMember).isEqualTo(member);
    }


    @Test
    public void basicCRUD(){
        Member member1 = new Member("member1");
        Member member2 = new Member("member2");
        memberJpaRepository.save(member1);
        memberJpaRepository.save(member2);

        //단건 조회 검증
        Member findMember1 =memberJpaRepository.findById(member1.getId()).get();
        Member findMember2 =memberJpaRepository.findById(member2.getId()).get();
        Assertions.assertThat(findMember1).isEqualTo(member1);
        Assertions.assertThat(findMember2).isEqualTo(member2);


        //리스트 조회 검증
        List<Member> all=memberJpaRepository.findAll();
        Assertions.assertThat(all.size()).isEqualTo(2);

        //카운트 검증
        long count=memberJpaRepository.count();
        Assertions.assertThat(all.size()).isEqualTo(2);

        //삭제 검증
        memberJpaRepository.delete(member1);
        memberJpaRepository.delete(member2);

        long deleteCount=memberJpaRepository.count();
        Assertions.assertThat(deleteCount).isEqualTo(0);

    }


}

 

 

기본 CRUD를 검증한다
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

7. 공통 인터페이스 설정

 

강의 :

https://www.inflearn.com/course/lecture?courseSlug=스프링-데이터-JPA-실전&unitId=28005&tab=curriculum

 

 

 

JavaConfig 설정- 스프링 부트 사용시 생략 가능

@Configuration
@EnableJpaRepositories(basePackages = "jpabook.jpashop.repository")
public class AppConfig {}

 

스프링 부트 사용시 @SpringBootApplication 위치를 지정(해당 패키지와 하위 패키지 인식)
만약 위치가 달라지면 @EnableJpaRepositories 필요

 

 

 

스프링 데이터 JPA가 구현 클래스 대신 생성

 

 

org.springframework.data.repository.Repository 를 구현한 클래스는 스캔 대상


MemberRepository 인터페이스가 동작한 이유


실제 출력해보기(Proxy)
memberRepository.getClass() class com.sun.proxy.$ProxyXXX

 

 

 

 

@Repository 애노테이션 생략 가능


컴포넌트 스캔을 스프링 데이터 JPA가 자동으로 처리 JPA 예외를 스프링 예외로 변환하는 과정도 자동으로 처리

 

 

 

 

 

 

 

 

 

8. 공통 인터페이스 적용

 

강의 :

https://www.inflearn.com/course/lecture?courseSlug=스프링-데이터-JPA-실전&unitId=28006&tab=curriculum

 

 

순수 JPA로 구현한 MemberJpaRepository 대신에 스프링 데이터 JPA가 제공하는 공통 인터페이스 사용

package study.datajpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import study.datajpa.entity.Member;

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

 

 

MemberRepository 테스트
 

package study.datajpa.repository;

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import study.datajpa.entity.Member;

import java.util.List;

@SpringBootTest
@Transactional
@Rollback(value = false)
class MemberRepositoryTest {

    @Autowired
    MemberRepository memberRepository;

    @Test
    public void testMember(){
        Member member =new Member("memberA");
        Member saveMember = memberRepository.save(member);

        Member findMember = memberRepository.findById(saveMember.getId()).get();

        Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
        Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        Assertions.assertThat(findMember).isEqualTo(member);
    }


    @Test
    public void basicCRUD(){
        Member member1 = new Member("member1");
        Member member2 = new Member("member2");
        memberRepository.save(member1);
        memberRepository.save(member2);

        //단건 조회 검증
        Member findMember1 =memberRepository.findById(member1.getId()).get();
        Member findMember2 =memberRepository.findById(member2.getId()).get();
        Assertions.assertThat(findMember1).isEqualTo(member1);
        Assertions.assertThat(findMember2).isEqualTo(member2);


        //리스트 조회 검증
        List<Member> all=memberRepository.findAll();
        Assertions.assertThat(all.size()).isEqualTo(2);

        //카운트 검증
        long count=memberRepository.count();
        Assertions.assertThat(all.size()).isEqualTo(2);

        //삭제 검증
        memberRepository.delete(member1);
        memberRepository.delete(member2);

        long deleteCount=memberRepository.count();
        Assertions.assertThat(deleteCount).isEqualTo(0);

    }


}

 

 

기존 순수 JPA 기반 테스트에서 사용했던 코드를 그대로 스프링 데이터 JPA 리포지토리 기반 테스트로
변경해도 동일한 방식으로 동작

 

 

TeamRepository 생성

package study.datajpa.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import study.datajpa.entity.Team;

public interface TeamRepository  extends JpaRepository<Team, Long> {
}

 

 

TeamRepository는 테스트 생략
Generic
T: 엔티티 타입
ID: 식별자 타입(PK)

 

 

 

 

 

 

 

 

 

 

 

9. 공통 인터페이스 분석

 

강의 :

https://www.inflearn.com/course/lecture?courseSlug=스프링-데이터-JPA-실전&unitId=28007&tab=curriculum

 

JpaRepository 인터페이스: 공통 CRUD 제공


제네릭은 <엔티티 타입, 식별자 타입> 설정

 

 

 

 

주의


T findOne(ID) Optional<T> findById(ID) 변경


boolean exists(ID) boolean existsById(ID) 변경

 

 

제네릭 타입
T : 엔티티
ID : 엔티티의 식별자 타입
S : 엔티티와 그 자식 타입

 

 

 


주요 메서드


save(S) : 새로운 엔티티는 저장하고 이미 있는 엔티티는 병합한다.


delete(T) : 엔티티 하나를 삭제한다. 내부에서 EntityManager.remove() 호출


findById(ID) : 엔티티 하나를 조회한다. 내부에서 EntityManager.find() 호출


getOne(ID) : 엔티티를 프록시로 조회한다. 내부에서 EntityManager.getReference() 호출


findAll(…) : 모든 엔티티를 조회한다. 정렬( Sort )이나 페이징( Pageable ) 조건을 파라미터로 제공할 수 있다.
> 참고: JpaRepository 는 대부분의 공통 메서드를 제공한다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

spring

 

about author

PHRASE

Level 60  라이트

바람 부는 날 가루 팔러 간다 , 하필 조건이 좋지 않은 때에 일을 시작함을 이르는 말.

댓글 ( 4)

댓글 남기기

작성