"인프런 - 실전! 스프링 부트와 JPA 활용1 강의를 듣고 작성한 글 입니다."
www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-JPA-%ED%99%9C%EC%9A%A9-1#
실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발 - 인프런
실무에 가까운 예제로, 스프링 부트와 JPA를 활용해서 웹 애플리케이션을 설계하고 개발합니다. 이 과정을 통해 스프링 부트와 JPA를 실무에서 어떻게 활용해야 하는지 이해할 수 있습니다. 초급
www.inflearn.com
프로젝트에서 JPA 와 DB 간 연동을 해보자.
우선 아래와 같이 프로젝트 환경 설정 파일 application.yml 를 작성해보자.
(프로젝트 환경 설정의 경우 스프링 공식문서를 살펴보면 자세히 알 수 있다.)
- application.yml
spring:
datasource:
url: jdbc:h2:tcp://localhost/~/jpashop_practice;
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
위의 파일에서 중요하게 살펴봐야 할 건 logging 옵션이다.
logging 옵션을 살펴보면 org.hibernate.SQL : debug 가 지정되어 있는데, 이는 Hibernate SQL 로그를 디버그 모드로 사용 하게끔 해주는 옵션이다.
이렇게 하면 Hibernate 가 남기는 모든 로그들이 디버그 모드가 되어 JPA 나 Hibernate 가 생성하는 모든 SQL 이 다 보이게 된다.(위에서 # 으로 주석처리된 show_sql 옵션보다 되도록이면 이걸 쓰도록하자.)
이렇게 프로젝트의 설정을 마쳤으면 이젠 스프링 부트가 정상적으로 잘 동작하는지 실험해보자.
Entity 클래스를 하나 생성한다.
- Member.java
@Entity
@Getter
@Setter
public class Member {
@Id @GeneratedValue
private Long id;
private String username;
}
이후 Repository 클래스를 하나 만든다.
- MemberRepository.java
@Repository
public class MemberRepository {
@PersistenceContext // 영속성 컨텍스트 어노테이션(JPA를 사용하기 때문에 EntityManager 가 필요하다.)
private EntityManager em;
// 스프링 부트에서 위와 같은 어노테이션이 있으면, EntityManager 를 주입해준다.
public Long save(Member member){ // Entity 객체를 저장하기 위한 메소드
em.persist(member);
return member.getId(); // 왜 키 값을 반환해야 할까?
// 커맨드와 쿼리를 분리하라?(CQRS?) -> 당장은 알 필요 없는것 같다.
// 저장을 하고 나면 사이드 이펙트를 일으킬 수 있는 커맨드 성 이기 때문에
// 리턴 값을 거의 만들지 않는다.
// 대신 아이디 값을 받아올 수 있으면 다음에 다시 조회 정도는 할 수 있다.
}
public Member find(Long id){
return em.find(Member.class, id);
}
}
이번엔 테스트 코드를 작성해보자.
Repository 클래스에서 Shift + Ctrl + T 를 누르면 자동으로 테스트 코드 클래스를 생성해주는 기능을 실행시킬 수 있다.
- MemberRepositoryTest.java
@RunWith(SpringRunner.class) //Junit 에게 스프링으로 테스트 한다고 알려주기 위한 어노테이션
@SpringBootTest
public class MemberRepositoryTest {
@Autowired MemberRepository memberRepository;
@Test
public void testMember() throws Exception {
Member member = new Member();
member.setUsername("memberA");
// Ctrl + Alt + V 단축키를 활용하여 코드에서 원하는 변수를 뽑아낼 수 있다.
Long saveId = memberRepository.save(member);
Member findMember = memberRepository.find(saveId);
// 검증에서 Assertions 는 assertj 라는 라이브러리를 스프링 테스트가 자동으로 가지고 있다.
Assertions.assertThat(findMember.getId()).isEqualTo(member.getId());
Assertions.assertThat(findMember.getUsername()).isEqualTo(member.getUsername());
}
}
그런데 위와 같이 코드를 작성한 후 테스트를 수행하면 아래와 같은 에러가 발생하게 된다.
No EntityManager with actual transaction available for current thread
위의 에러는 바로 트랜잭션이 존재하지 않는다는 뜻이다.
EntityManager 를 통한 모든 데이터 변경은 항상 트랜잭션 내부에서 수행되어야 한다.
그렇기 때문에 메소드에서 @Transactional 어노테이션을 선언하여 트랜잭션을 만들어 준 후 메소드를 실행해야 한다.
- MemberRepositoryTest.java
@Test
@Transactional
public void testMember() throws Exception {
..........
}
위와 같이 어노테이션을 통해 트랜잭션을 생성해준 후 테스트가 성공했을 때 H2 콘솔을 통해 데이터베이스의 상태를 보면 Member 테이블이 생성되어 있는 것을 확인할 수 있다.
하지만 테스트 코드에서 데이터를 save 메소드를 통해 삽입하였음에도 불구하고 데이터베이스에 데이터가 들어가 있지 않다.
어째서 그런것일까>
그 이유는 바로 일반적으로 테스트가 아닌곳에 Transactional 어노테이션이 있으면 정상적으로 트랜잭션이 수행되서 데이터가 저장되나, Test 에 있는 경우 데이터베이스를 롤백 해버림으로서 데이터가 모두 날아가지 때문이다.
여기서 데이터베이스를 롤백하는 이유는, 데이터가 그대로 남아있으면 반복적인 테스트를 하기 힘들기 때문이다.
그런데 여기서 @Rollback(false) 어노테이션을 하나 더 선언해주면 데이터베이스가 롤백되지 않고 데이터를 그대로 남겨두고 있는 것을 확인할 수 있다.
- MemberRepositoryTest.java
@Test
@Transactional
@Rollback(false) // 데이터베이스 롤백 방지
public void testMember() throws Exception {
..........
}
그렇다면 이번엔 아래의 코드를 실행해보자.
- MemberRepositoryTest.java
@Test
@Transactional
public void testMember() throws Exception {
..........
Assertions.assertThat(findMember).isEqualTo(member);
System.out.println("findmember == member : " + (findMember == member));
}
위의 코드의 결과는 어떻게 나올까? (findMember 와 member 의 == 비교)
비교 결과 true 가 출력된다.
어째서일까?
이전 JPA 강의를 들을때 배웠던 내용에서 처럼 한 트랜잭션에서 같은 영속성 컨텍스트에 있을 경우 아이디 값이 같다면 같은 Entity 로 보기 때문이다.
생성된 Hibernate SQL 을 잘 보면 select 절 조차 나오지 않은 것을 잘 알 수 있다.
- Hibernate SQL
findmember == member : true
'Spring boot' 카테고리의 다른 글
스프링 부트 - Entity 설계 시 주의할 점(2) (0) | 2020.12.28 |
---|---|
스프링 부트 - Entity 설계 시 주의할 점(1) (0) | 2020.12.28 |
스프링 부트 - JPA 와 DB 설정 및 동작확인 - Tip (0) | 2020.12.20 |
스프링 부트 View 환경 설정 (0) | 2020.12.19 |
spring-boot-starter-data-jpa 의존성 관련 오류 해결 (0) | 2020.08.26 |