Spring/SpringDataJPA

공통 인터페이스 기반 - 순수 JPA 기반 레포지토리 만들기

느리지만 꾸준하게 2022. 4. 15. 20:01

일단 순수하게 JPA 기반을 레포지토리를 한번 만들어보자.

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

 

=> JPA에서 수정은 변경가지 기능을 사용하면 되고

트랜잭션 안에서 엔티티 조회한 다음 데이터 변경하고 트랜잭션 종료 시점에 변경

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

 

 

 

 

기본적인 CRUD를 만들어본다.

JPA가 제공하는 JPQL이라는 기술을 사용하자(객체를 대상으로 하는 쿼리)

SQL로 번역이 된 다음에 실제 db에서 데이터를 가져와서 반환을 해준다.

 

delete, findById, Optional, count을 사용해서 만들어준다.

 

package study.datajpa.repository;


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

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

@Repository
public class MemberJpaRepository {

    @PersistenceContext
    // 엔티티 집어넣고 jpa가 db에다 insert query를 날려서 저장을 하게됨
    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) {
        // jpa가 알아서 Member entity에 맞는 select query를 db에다가 가져오게 된다.
        return em.find(Member.class, id);
    }

}

 

JPA는 기본적으로 엔티티를 변경을 할 때 변경감지라는 기능으로(Dirty Checking) 데이터를 바꾼다.

그래서 업데이트라는 메서드가 필요가 없다.

public void update(Member member) {
        
    }

JPA에서 엔티티 매니저 em을 통해서 조회를 해온 다음에 엔티티를 직접 수정을 하고 트랜잭션 커밋하면 자동으로 변경된 걸 인지를 해서 

db에 업데이트 쿼리를 날린다.

즉 자바 컬렉션과 동일한 (순수한 객체 대상 방식) 방식으로 객체를 업데이트 한다.

 

 

 

 

 

 

 

TeamRepository MemberRepository와 비슷하게 지정을 해준다.

package study.datajpa.repository;


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

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

@Repository
public class TeamRepository {

    @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();
    }
}

 

 

MemberJPARepositoryTest를 단건조회 검증으로 작성해보자. 

빌드를 돌리면 빌드창에서 녹색불 나온다.

package study.datajpa.repository;


// junit의 jupiter test
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 static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

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

    @Autowired
    MemberJpaRepository memberJpaRepository;

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

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

        // 검증
        assertThat(findMember.getId()).isEqualTo(member.getId());
        assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        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();
        assertThat(findMember1).isEqualTo(member1);
        assertThat(findMember2).isEqualTo(member2);
    }
}

 

 

 

이제 리스트 조회 검증 및 카운트 검증 코드와

삭제 검증 및 카운트 검증 코드를 각각 작성한 후 빌드를 돌려보자.

package study.datajpa.repository;


// junit의 jupiter test
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;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

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

    @Autowired
    MemberJpaRepository memberJpaRepository;

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

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

        // 검증
        assertThat(findMember.getId()).isEqualTo(member.getId());
        assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        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();
        assertThat(findMember1).isEqualTo(member1);
        assertThat(findMember2).isEqualTo(member2);

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

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

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

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

    }
}

빌드를 확인해보면 단건조회해서 데이터 조회한거 다 맞고 리스트 조회해서 카운트 2개 한거 맞고

실제 카운트 쿼리에 카운트 개수 2개 한거 맞고 2개 삭제한 다음에 0개확인한게 맞다.

 

변경감지를 한번 적용해보자.

 

package study.datajpa.repository;


// junit의 jupiter test
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;

import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;

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

    @Autowired
    MemberJpaRepository memberJpaRepository;

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

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

        // 검증
        assertThat(findMember.getId()).isEqualTo(member.getId());
        assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
        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();
        assertThat(findMember1).isEqualTo(member1);
        assertThat(findMember2).isEqualTo(member2);

        findMember1.setUsername("member!!!!!!!");

 

빌드창을 확인해보면 아래와 같고

update member set age=?, tema_id=?, username=? where member_id=?
update member set age=0, tema_id=NULL, username='member!!!!!!!' where member_id=1;

h2 db에 아래와 같이 되어있다.

 

 

다음으로 Spring Data JPA를 이용해서 공통 인터페이스 설정을 작성해본다.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 실전! 스프링 데이터 JPA >

https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%8D%B0%EC%9D%B4%ED%84%B0-JPA-%EC%8B%A4%EC%A0%84/dashboard

 

실전! 스프링 데이터 JPA - 인프런 | 강의

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

www.inflearn.com