스프링부트를 연습하면서 데이터베이스는 필연적인 부분인데 이를 외부 데이터베이스를 연결시키지 않고 내 컴퓨터 램에다가 저장해버리는 인메모리 방식이 있다.
부트에서는 main 디렉토리의 resources안에 있는 application. yml or properties 파일에 DB를 연결한다.
문제는 test 디렉토리에서 db를 탐색해야하는 코드를 짰을 때 조회하는 부분은 main이 만든 db에 들어가서 빼오는건 잘 하는데 문제는 삽입 수정 삭제는 전혀 되지 않는다는 점이다.
@Component
@RequiredArgsConstructor
public class FoodInitializer implements ApplicationRunner {
private final UserRepository userRepository;
private final FoodRepository foodRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
User user = new User();
user.setName("카리나");
userRepository.save(user);
Food food = new Food();
food.setName("치킨");
food.setPrice(4900);
food.setUser(user);
foodRepository.save(food);
}
}
먼저 서버를 시작할 때 유저와 푸드를 하나씩 넣었다.
@Transactional
@SpringBootTest
public class OnetoOneTest {
@Autowired
FoodRepository foodRepository;
@Autowired
UserRepository userRepository;
@Test
@Rollback(value = false)
void test2() {
User user = userRepository.findById(1L).orElseThrow();
System.out.println(user.getName());
user.setName("윈터");
User user2 = userRepository.findById(1L).orElseThrow();
System.out.println(user2.getName());
}
@Test
@Rollback(value = false)
void test3() {
User user = userRepository.findById(1L).orElseThrow();
System.out.println(user.getName());
}
}
그리고 테스트 환경에서 수정을 진행해보고 수정한 것을 불러서 출력하는 test2와 그러고 난 후 실행하는 test3를 만들었다.
test2()
Hibernate:
select
u1_0.id,
u1_0.name
from
users u1_0
where
u1_0.id=?
카리나
윈터
Hibernate:
/* update
for com.sparta.jpaadvance.entity.User */update users
set
name=?
where
id=?
test3()
Hibernate:
select
u1_0.id,
u1_0.name
from
users u1_0
where
u1_0.id=?
카리나
DB에 반영이 전혀 안되는 모습이다.
user2에 관한 쿼리문이 생성되지 않는 이유는 영속성 캐시에 user의 정보가 있기 때문이다. 한 트랜잭션에서 같은 id로 검색했을 경우 이미 검색한 전례가 있기 때문에 캐시에서 user의 정보를 가져와서 user2에 넣어준다.
test2()
Hibernate:
select
u1_0.id,
u1_0.name
from
users u1_0
where
u1_0.id=?
카리나
윈터
test3()
Hibernate:
/* update
for com.sparta.jpaadvance.entity.User */update users
set
name=?
where
id=?
Hibernate:
select
u1_0.id,
u1_0.name
from
users u1_0
where
u1_0.id=?
윈터
수정은 제대로 적용된 모습이다. 그러나 이 경우도 Test의 동작이 끝난다면 Rollback이 이루어진다 false로 해놓았는데도 불구하고 말이다.
왜 이런 것일까?
test 디렉토리에 resources/application.propertise를 제작해서 인메모리 설정을 해놓고 ddl-auto를 none으로 해놓으면 답을 알 수 있다.
[test/resources/application.properties]
spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySql;
spring.datasource.username=sa
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
해당 세팅으로 해놓은 후에 main의 서버를 실행시켜 인메모리 DB 환경을 생성 해 놓은후에 FoodInitializer를 주석처리했다.
테스트 코드를 실행시키면
오류가난다.
USERS 테이블을 찾을 수가 없다고 한다.
Test를 실행했을 때 인메모리 DB의 경우 Test만의 단독 DB가 생성된다는 뜻이다. 그래서 앞에서 했던 것들은 properties 파일이 없었기 때문에 main이 가진 설정값들을 가져왔을 뿐이고 initalizer를 실행시켜 test만의 DB에 값(카리나, 치킨)을 넣은 것이다.
그렇기에 Test의 실행환경이 끝나면 Test만의 DB는 사라진다.
원래의 DB의 값이 롤백 된것 처럼 보였을 뿐 거기 데이터는 손도 안댔던 것이다.
메인 DB의 데이터로 테스트를 해야하는 경우는 실제 DB를 만들어서 거기에 연결시켜 테스트 하는 수 밖에 없다.