취미로 음악을 하는 개발자

[Spring Framework] Spring Data JPA Repository 테스트 본문

공대인/Spring[Boot]

[Spring Framework] Spring Data JPA Repository 테스트

영월특별시 2019. 10. 1. 00:26
728x90

* 프로젝트는 이전 포스트에서의 프로젝트와 같다. 테스트 클래스에서 메소드를 좀 더 추가할 것임.


1. 등록 작업 테스트


CrudRepository 인터페이스는 save()라는 메소드를 이용해서 데이터베이스에 엔티티의 정보를 추가하거나 수정하는 작업을 한다.

동일한 메소드로 insert와 update 작업도 이루어지는데 이 부분을 이해하기 위해서는 엔티티 매니저의 존재를 인식할 필요가 있다.


Repository 쪽에서 save()라는 메소드가 호출되면, 내부에서는 엔티티 매니저가 영속 컨텍스트에 해당 식별키를 가진 엔티티가 존재하는지를 확인한다.


만약 동일한 식별 데이터를 가지는 엔티티가 없다면 엔티티 매니저는 이를 영속 컨텍스트에 저장하고 데이터베이스에도 추가하게 된다. 

반대로 엔티티가 이미 존재한다면 메모리에 보관되는 엔티티를 수정하는 작업과 데이터베이스를 갱신하는 작업을 진행한다.


BoardRepositoryTests 클래스에 아래의 코드를 추가한다.

1
2
3
4
5
6
7
8
9
    @Test
    public void testInsert() {
        Board board = new Board();
        board.setTitle("게시물 제목");
        board.setContent("게시물 내용 넣기");
        board.setWriter("user00");
        
        boardRepo.save(board);
    }
cs


위의 코드는 Board 타입의 객체를 생성하고 title, content, writer 속성값만을 지정하고 있다.

식별 데이터인 bno는 자동 생성 방식을 이용하도록 설정되어 있어 지정하지 않는다.

코드를 실행하면 아래와 같이 처리된다.


--- (중략) ---


맨 윗 줄의 출력은 기존 테이블을 삭제하는 부분인데, application.properties 설정 중 아래와 같은 설정때문에 테이블 생성을 시도

spring.jpa.hibernate.ddl-auto=create


이 때 drop table로 기존의 테이블을 삭제하려고하는 것이다. 그 이후는 테이블이 생성되고 마지막에 insert 구문이 실행되는 것을 볼 수 있다. 최종 결과는 데이터베이스에 insert되었는지를 확인한다.


* Timestamp 타입으로 시, 분, 초의 데이터를 모두 기록할 수 있어서 기록된 시간이 정상적인지 확인해준다.



2. 조회 작업 테스트


등록 이외의 작업을 할 때에는 반드시 application.properties 파일의 설정을 확인해야 한다. 파일에 있는 spring.jpa.hibernate.ddl-auto=create 설정은 매번 테이블이 drop되고 생성되기 때문에 기존의 데이터를 확인할 수 없다.


테스트를 최초로 실행하는 경우에는 원하는 테이블이 정상적인 형태로 생성되는지 체크할 수 있기 때문에 create 설정을 사용하지만, 이후에는 기존 데이터를 체크할 수 없으므로 update나 none 등의 값을 조정해주고 테스트를 진행한다.


1
spring.jpa.hibernate.ddl-auto=update
cs


조회 테스트 코드로는 findById() 메소드를 이용한다. (구, findOne()) 이 메소드는 파라미터로 식별 데이터를 사용하는데 Spring Data JPA는 CrudRepository에서 <T, ID>와 같이 제네릭을 이용하기 때문에 별도의 변환 없이 다음과 같은 코드로 사용한다.


1
2
3
4
5
    @Test
    public void testRead() {
        Optional<Board> board = boardRepo.findById(1L);
        System.out.println(board.get());
    }
cs


Board 타입은 식별 데이터를 Long 타입으로 사용했으므로 1L 과 같이 Long 타입으로 파라미터를 지정한다. 물론 이 때 실제 데이터베이스에는 1번에 해당하는 데이터가 존재해야하므로 SQL을 이용해서 확인 후 테스트 코드를 실행한다. 

출력문에 get()을 호출한 이유는 여기서 board 타입은 Optional<Board>이기 때문에 Board 타입 값을 호출하기 위함이다.

코드를 실행하면 내부적으로 select가 실행되는 것을 확인할 수 있다.



Spring Data JPA는 기본적으로 Hibernate라는 JPA 구현체를 이용한다. Hibernate는 내부적으로 지정되는 DB에 맞게 SQL문을 생성하는 Dialect가 존재한다.


Dialect는 Hibernate가 다양한 데이터베이스를 처리하기 위해 각 데이터베이스마다 다른 SQL 문법을 처리하기 위해 존재하는 것인데 JPA를 통해서 호출하면 설정된 데이터베이스에 맞게 SQL문이 생성되고 이 역할을 하는 존재Dialect라고 한다.

application.properties에 데이터베이스의 종류를 지정하면 기본적으로 해당 데이터베이스에 맞는 Dialect가 지정되지만 특정 버전을 명시해줄 수도 있다.


1
2
spring.jpa.database=mysql
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
cs


MySQL을 사용하는 경우 MySQL용 Dialect가 동작하면서 앞서 본 것과 같은 SQL문을 생성해 낸다.


조회 작업에는 내부적으로 1차 캐시라는 것이 등잔하는데 외부에서 특정한 엔티티를 조회하게 되면 내부에서는 1차 캐시 안에 엔티티가 존재하는지를 살펴보고 없는 경우에는 SQL문을 통해서 데이터베이스에 가져오게 된다.



3. 수정 작업 테스트


수정 작업은 등록 작업과 동일하게 save() 메소드를 호출한다. 현재 1번 엔티티의 속성 값을 변경해보는 코드를 작성해보겠다.

* spring.jpa.hibernate.ddl-auto 속성 값이 create가 아닌지 확인한 후에 진행한다.


1
2
3
4
5
6
7
8
9
10
11
    @Test
    public void testUpdate() {
        System.out.println("Read First.........................");
        Optional<Board> board = boardRepo.findById(1L);
        
        System.out.println("Update Title.........................");
        board.get().setTitle("수정된 제목입니다");
        
        System.out.println("Call Save().........................");
        boardRepo.save(board.get());
    }
cs




코드를 실행하면 Read First 부분이 실행되는데 1번 엔티티의 값을 읽어들이는 과정이 필요하기 때문에 select문이 동작한다.

데이터베이스에서 조회했기 때문에 board 변수가 참조하는 객체는 식별 데이터를 가진 상태가 되고 Call Save() 이하의 결과를 보면 select와 update가 실행되는 것을 볼 수 있다.


이처럼 JPA는 데이터베이스에 바로 작업을 하는 JDBC와는 달리 스스로 엔티티 객체들을 메모리 상에서 관리하고, 필요한 경우에 데이터베이스에 작업을 하게 된다. 따라서 수정과 삭제 작업은 직접 데이터베이스에 바로 SQL문을 실행하는 것이 아니라, 엔티티 객체가 우선적으로 메모리상에 존재하고 있어야 한다. 이 과정을 위해서 select가 동작하게 되는 것이다.


테스트 코드는 매번 새롭게 실행되기 때문에 관리되고 있는 엔티티 객체가 없어서 현재 작성하는 수정과 삭제 작업에는 select가 우선으로 실행된다. 만일 이 때 조회한 엔티티와 수정된 엔티티가 동일한 값들을 가지고 있어서 수정할 필요가 없다면 update문은 실행하지 않는다.



4. 삭제 작업 테스트


Spring Data JPA에서는 delete()를 이용해서 삭제 처리를 한다. delete() 시에 파라미터는 두 가지 방법 중 하나로 SQL문을 구성한다.


1) 식별키 값을 파라미터로 전달

2) 삭제하려는 엔티티 객체를 전달


1
2
3
4
5
    @Test
    public void testDelete() {
        System.out.println("DELETE Entity");
        boardRepo.deleteById(1L);
    }
cs



테스트 결과를 보면 수정과 마찬가지로 삭제하기 전에 엔티티 객체가 관리되지 않았기 때문에 먼저 select를 통해 엔티티 객체를 보관하고 이후에 delete가 실행되는 것을 볼 수 있다.


좀 더 자세한 실행 로그를 보고 싶다면 application.properties에서 loggin.level.org.hibernate 값을 info에서 debug로 변경하면 된다.

여기까지의 테스트는 너무 간단했지만 다음 포스트에서는 좀 더 복잡한 처리를 해 볼 것이다.


Comments