관리 메뉴

bright jazz music

JPA 인터페이스 생성 + CRUD 테스트 (insert, findById, getOne, getReferenceById save, deleteById) 본문

Framework/Spring

JPA 인터페이스 생성 + CRUD 테스트 (insert, findById, getOne, getReferenceById save, deleteById)

bright jazz music 2022. 6. 12. 20:20

jpa 인터페이스 생성

package org.zerok.ex2.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.zerok.ex2.entity.Memo;

public interface MemoRepository extends JpaRepository<Memo, Long> {
    //JpaRepository를 사용할 때는 엔티티 타입 정보(여기서는 Memo클래스)와 @Id 타입을 지정한다.
    //SpringDataJpa는 인터페이스 선언만으로도 자동으로 bean으로 등록한다.
    //(내부적으로는 인터페이스 타입에 맞는 객체를 생성해서 빈으로 등록한다.)
    //선언이 끝났으면 test폴더-repository패키지생성-MemoRepositoryTests클래스를 작성해서 진행
}


/*
*JpaRepository 인터페이스
* Spring Data Jpa는 Jp의 구현체인 Hibernate를 이용하기 위한 여러 API를 제공한다.
* 그 중 가장 많이 사용하는 것이 JpaRepository라는 인터페이스이다.
* 이러한 종류의 인터페이스의 기능을 통해서 JPA 관련 작업을 별도의 코드 없이 처리할 수 있도록 지원한다.
* CRUD, 페이징, 정렬 등의 처리도 인터페이스의 메서드를 호출하는 형태로  처리하는데,
* 기능에 따라서 상속구조로 추가적인 기능을 제공한다.
*
* 예) [JpaRepository -> PagingAndSortRepository]->[CrudRepository] -> [Repository]
*
* 일반적인 기능만을 사용할 때는 CrudRepository를 사용하는 것이 좋고, 모든 JPA관련 기능을
* 사용하고 싶을 때는 JpaRepository를 이용한다. 특별한 경우가 아니라면 JpaRepository를 이용하는 것이 무난하다.
*
*
* JpaRepository는 인터페이스이고, Spring Data Jpa는 이를 상속하는 인터페이스를
* 선언하는 것만으로도 모든 처리가 끝난다. 실제 동작 시에는 스프링이 내부적으로 해당 인터페이스에 맞는
* 코드를 생성하는 방식을 이용한다.
*
*
* */

 

인터페이스를 생성했다면 Test

 

test -> repository(패키지 생성) -> MemoRepositoryTests.java 생성

 

package org.zerok.ex2.repository;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.zerok.ex2.entity.Memo;

import javax.transaction.Transactional;
import java.util.Optional;
import java.util.stream.IntStream;

@SpringBootTest
public class MemoRepositoryTests {

    @Autowired
    MemoRepository memoRepository;

    @Test
    public void testClass(){
        System.out.println(memoRepository.getClass().getName());

        /*
         * testClass()메서드는 MemoRepository 인터페이스 타입의 실제 객체가 어떤 것인지 확인한다.
         * 스프링이 내부적으로 해당 클래스를 자동생성(AOP)하는데, 이 때 선언한 적 없는 클래스의 이름이
         * com.sun.proxy.$Proxy110 와 같이 콘솔에 출력된다.(동적 프록시 방식으로 생성됨)
         *
         *
         * */
    }


    @Test //등록작업 테스트
    public void testInsertDummies(){
        IntStream.rangeClosed(1,100).forEach(i -> {
            Memo memo = Memo.builder().memoText("Sample..." + i).build();
            memoRepository.save(memo);
        });

        //한 번에 여러 개의 객체를 저장하도록 작성. 100개의 Memo 객체를 생성하고 MemoRepository를 이용해서 insert.
        // 두 번 실행하면 테이블에 행이 200번까지 만들어진다.
    }

    @Test   //조회작업 테스트: findById()사용
    public void testSelect(){
        //DB에 존재하는 mno
        Long mno = 100L;

        Optional<Memo> result = memoRepository.findById(mno);
        //100번이 존재하지 않더라도 오류를 반환하지 않음. 물론 객체도 반환되지 않음.

        System.out.println("==============================");

        if(result.isPresent()) {
            Memo memo = result.get();
            System.out.println(memo);
        }
        /*
        * 조회 작업의 테스트는 findById()나 getOne()을 이용해서 엔티티 객체를 조회할 수 있다.
        * findById()와 getOne()은 동작하는 방식이 조금 다르다.
        * DB를 먼저 이용하는지, 필요한 순간까지 미루는지에 대한 차이이다.
        *
        * findById()는 DB를 먼저 이용해서 값을 가져온다.
        * findById()를 실행한 순간에 이미 sql은 처리되었고
        *
        * findById()의 경우 java.util 패키지의 Optional 타입으로 반환되기 때문에
        * 한 번 더 결과가 존재하는지 체크하는 형태로 작성하게 된다.
        * "==============================" 부분은 SQL 처리 이후에 실행된다.
        * */
    }

    @Transactional //트랜잭션 처리를 위해 사용하는 어노테이션. 없으면 오류 반환: no session
    @Test   //조회작업 테스트: getOne()사용
    public void testSelect2(){
        Long mno = 100L;

        Memo memo = memoRepository.getOne(mno);
        //리턴 값은 해당 객체이지만 필요한 순간까지 sql을 실행하지 않는다.
        //해당 값이 테이블에 존재하지 않으면 오류 반환: Unable to find org.zerok.ex2.entity.Memo with id 100
        //getOne()은 depricated됨. getReferenceById()로 대체. testSelect3()에서 사용

        System.out.println("==============================");

        System.out.println(memo);//실제 객체를 사용하는 순간에 SQL이 동작한다.

    }

    @Transactional //트랜잭션 처리를 위해 사용하는 어노테이션. 없으면 오류 반환: no session
    @Test   //조회작업 테스트: getOne()사용
    public void testSelect3(){
        Long mno = 100L;

        Memo memo = memoRepository.getReferenceById(mno); //getOne() 대신에 사용
        //리턴 값은 해당 객체이지만 필요한 순간까지 sql을 실행하지 않는다.
        //해당 값이 테이블에 존재하지 않으면 오류 반환 : Unable to find org.zerok.ex2.entity.Memo with id 100
        //getOne()은 depricated됨. getReferenceById()로 대체. testSelect3()에서 사용

        System.out.println("==============================");

        System.out.println(memo);//실제 객체를 사용하는 순간에 SQL이 동작한다.

    }

    @Test
    public void testUpdate(){ //수정작업
        Memo memo = Memo.builder().mno(100L).memoText("Update Text").build();

        System.out.println(memoRepository.save(memo));

        /*
        * 수정작업은 등록작업과 동일하게 save()를 이용해서 처리한다.
        * 내부적으로 해당 엔티티의 @Id 값이 일치하는지 확인해서 insert 혹은 update 작업을 처리한다.
        *
        * 여기서는 100번의 Memo객체를 만들고, save()를 호출한다.
        * 호출결과를 보면 내부적으로 select 쿼리로 해당 번호의 Memo 객체를 확인하고
        * 이를 update하는 것을 볼 수 있다.
        *
        * JPA는 엔티티 객체들을 메모리 상에 보관하려고 하기 때문에 특정한 엔티티 객체가 존재하는지
        * 확인하는 select가 먼저 실행되고 해당 @Id를 가진 엔티티 객체가 있다면 update를,
        * 그렇지 않다면 insert를 실행한다.
        * */
    }

    @Test
    public void testDelete(){
        Long mno = 100L;
        memoRepository.deleteById(mno);

        /*
        * 삭제 작업도 위와 동일한 개념이 적용된다. 삭제하려는 번호(mno)의 엔티티 객체가 있는지
        * 먼저 확인(select)하고 이를 삭제 시도한다.
        * deleteById()의 리턴 타입은 void이고 만일 해당 데이터가 존재하지 않으면
        * org.springframework.dao.EmptyResultAccessException 예외를 발생시킴.
        * */
    }

}

 

 

testInsertDummies()

 

 

 

 

findById()

sql이 먼저 실행되고 =====

 

 

 

getOne() : depricated됨. getReferenceById()를 사용할 것을 권장.

=========먼저 실행됨. 그리고 나서 System.out.println(meno) 코드에서 객체가 실제 사용되어야 할 때 호출됨.

 

getReferenceById() : getOne()과 동일

=========먼저 실행됨. 그리고 나서 System.out.println(meno) 코드에서 객체가 실제 사용되어야 할 때 호출됨.

=========먼저 실행됨. 그리고 나서 System.out.println(meno) 코드에서 객체가 실제 사용되어야 할 때 호출됨.

 

@Test

public void testUpdate()

Memo memo = Memo.builder().mno(100L).memoText("Update Text").build();

memoRepository.save(memo)

 

 

 

deleteById()

 

100번이 삭제됨.

 

 

***

 

getOne()은 deprecated 처리 되었다.

이 메소드 대신에 

getReferenceById() 를 사용할 것을 권장

 

https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/JpaRepository.html

 

JpaRepository (Spring Data JPA 2.7.1 API)

getReferenceById T getReferenceById(ID id) Returns a reference to the entity with the given identifier. Depending on how the JPA persistence provider is implemented this is very likely to always return an instance and throw an EntityNotFoundException on

docs.spring.io

 

 

Comments