Blog

[Boot+JPA] JPA, DB 설정

Category
Author
citeFred
citeFred
Tags
PinOnMain
1 more property
H2 DB를 실행하고 JPA로 엔티티와 데이터베이스의 연결 확인
Table of Content

H2 데이터베이스 설치

H2 인메모리 데이터베이스를 로컬환경에 설치하여 사용하고자 한다. 과거 MacOS 설치 내용과 동일하므로 해당 내용을 그대로 사용한다.
최초 데이터베이스를 생성하기 위해서는 JDBC URL을 다음처럼 입력한다.
jdbc:h2:~/jpashop
Java
복사
이처럼 연결하고 나면 지정한 URL 경로에 다음과 같이 xx.mv.db와 같은 데이터베이스 파일이 생성되게 된다.
생성 이후부터는 로컬호스트로 접근이 가능하다.
jdbc:h2:tcp://localhost/~/jpashop
Java
복사
브라우저에서 localhost:8082 포트로 접근할 수 있다.

application.yml

application.properties 또는 application.yml 로 프로젝트의 각종 설정을 할 수 있다. properties와 yml의 대표적인 차이는 내부 구조가 있다. properties의 경우엔 각 줄마다 key=value의 형태로 이루어져 있지만, yml의 경우엔 들여쓰기로 구분되는 계층 구조 및 key: value의 형태로 이루어져 있다. 이번에는 yml을 사용해보고자 한다.
spring: datasource: url: jdbc:h2:tcp://localhost/~/jpashop;MVCC=TRUE username: sa password: driver-class-name: org.h2.Driver jpa: hibernate: ddl-auto: create properties: hibernate: show_sql: true format_sql: true logging: level: org.hibernate.sql: debug
Java
복사
큰 차이는 없지만 확실히 가독성이 높은것으로 보여진다. yml을 사용하는 것이 더 구조를 파악하기 쉽고, 중복되는 코드가 줄어들기 때문에 yml을 사용하는 것이 더 좋다고 생각한다. properties와 yml을 함께 사용하면 properties 파일이 우선순위가 높아 yml 파일에서 설정한 내용이 덮어씌워질 수 있다는 점을 유의해야 한다.

Member를 통한 테스트

간단한 회원 엔티티를 생성하고 기초 저장, 조회 기능이 작동하는지 테스트하려고 한다.
Member.java
package jpabook.jpashop; import lombok.Getter; import lombok.Setter; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity @Getter @Setter public class Member { @Id @GeneratedValue private Long id; private String username; }
Java
복사
MemberRepository.java
package jpabook.jpashop; import org.springframework.stereotype.Repository; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @Repository public class MemberRepository { @PersistenceContext private EntityManager em; public Long save(Member member) { em.persist(member); return member.getId(); } public Member find(Long id) { return em.find(Member.class, id); } }
Java
복사
간단하게 Member를 저장하고 조회하는 기능을 추가하였다.

테스트 코드

MemberRepository가 정상적으로 엔티티 클래스를 데이터베이스와 연결하여 작동하는지 확인하고자 한다.
package jpabook.jpashop; import org.assertj.core.api.Assertions; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import static org.assertj.core.api.Assertions.*; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest public class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long savedId = memberRepository.save(member); Member findMember = memberRepository.find(savedId); //then assertThat(findMember.getId()).isEqualTo(member.getId()); assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); } }
Java
복사
해당 테스트에서는 먼저 작동에 오류가 발생했는데, 첫 문구는 다음과 같다.
예제에서 커넥션 세팅을 명시한 application.yml에서 url:jdbc:h2:tcp://localhost/~/jpashop;MVCC=TRUE 과 같은 값에서 오류가 나타난 것인데 최근 H2 1.4.200 버전부터 MVCC 옵션이 제거되어 해당 부분을 제거하여 실행했다.
이후 테스트는 정상적으로 작동되었으며
application.yml 설정에서 ddl-auto: create 설정에 의해 실제 H2 DB에 테이블도 생성되었음을 확인 할 수 있다. 하지만 memberA가 저장되어 있지 않은데 기본적으로 commit하지 않고 rollback으로 설정되어 있기 때문이다.
테스트 코드에서 Member member = new Member();Member findMember = memberRepository.find(savedId);member, findMember는 동일할까?
영속성 컨텍스트는 내부에 캐시를 갖고 있는데 이를 1차 캐시라고 한다. 영속 상태의 엔티티는 모두 이곳에 저장되며, @Id 로 맵핑한 식별값을 이용하여 구분한다.
위 코드를 실행하면 member 를 1차 캐시에 저장한다. 마치 map 에서 ("ID#1", member 인스턴스) 와 같이 key를 @Id 식별값으로, value 는 인스턴스 로 저장하는 구조
DB 에 find 연산이 도달하기 전에 영속성 컨텍스트 안에서 id 에 해당하는 엔티티가있으면 곧바로 가져올 수 있으므로 "1차 캐시" 가 된다.
영속성 컨텍스트 특징에서 동일성을 보장하기 때문

P6Spy로 쿼리 로그 정확하게 살펴보기

기본 쿼리로그를 추적해보면 (?, ?)와 같이 실제 값들을 확인 할 수 없다. P6Spy 외부 라이브러리를 통해서는 해당 값들을 확인 할 수 있다.(실제 서비스에서는 성능 문제 등 고려가 필요하다.)
우선 외부 라이브러리 의존성을 추가해준다.
dependencies { ... //P6Spy query logging 사용 implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.5.6' ... }
Java
복사
이후 실제 DB로 쿼리문이 실행되야 하므로 테스트 코드에서 Rollback 기본값을 제거하여 실제 쿼리를 날려본다.
@Rollback(value=false)
@RunWith(SpringRunner.class) @SpringBootTest public class MemberRepositoryTest { @Autowired MemberRepository memberRepository; @Test @Transactional @Rollback(value=false) public void testMember() throws Exception { //given Member member = new Member(); member.setUsername("memberA"); //when Long savedId = memberRepository.save(member); Member findMember = memberRepository.find(savedId); //then assertThat(findMember.getId()).isEqualTo(member.getId()); assertThat(findMember.getUsername()).isEqualTo(member.getUsername()); assertThat(findMember).isEqualTo(member); System.out.println("findMember == member :" + (findMember == member)); } }
Java
복사
이제 해당 테스트코드를 실행하여 실제 쿼리를 날려보면 전달된 필드 값들을 확인 할 수 있다.
여기까지 과정을 통해서 프로젝트 준비와 JPA 작동 확인까지 했으므로 이제 도메인 설계와 기능 구현들로 진행하고자 한다.
Search
 | Main Page | Category |  Tags | About Me | Contact | Portfolio