Spring
JPA 리 오류들 체크 리스트
sehunbang
2024. 3. 6. 19:00
오해 풀기
- 실습하다보면 내가 생각한대로 JPA가 동작하지 않을때가 많죠???
1. 쿼리 최적화
JPA 가 1차캐시(영속성 컨텍스트) 를 통해 필요없는 쿼리는 날라가지 않도록 쿼리를 최적화 해준다고 하는데….
최적화가 안되고 Insert, Delete 쿼리가 다 DB로 날라가는 로그가 찍히는데 왜그런건가요?
쿼리 최적화를 발생시키기 위해서는 아래 3가지를 확인해봐야 합니다.
1. 먼저 해당 함수나 클래스가 Transaction 안에 포함되고 있는지 봐야합니다.
- @Transactional 으로 함께 감싸져 있어야만 쿼리 최적화가 동작합니다.
- Transaction 으로 포함되어있지 않으면 repository 메소드 내부에서만 Transcation 이 최적화됩니다.
- Transaction Propagation (전파) 전략 체크해봐야합니다. (심화)
2. 두번째로, 해당 엔티티의 ID 식별자 생성전략을@GeneratedValue(strategy = GenerationType.IDENTITY)로 사용한건 아닌지 확인해봐야 합니다.
- GenerationType.IDENTITY 로 키필드가 설정되어 있으면 데이터베이스에 실제로 저장을 해야 유일한 식별자를 구할 수 있으므로 Insert 쿼리가 즉시 데이터베이스에 전달됩니다.
- GenerationType.SEQUENCE 로 설정하길 추천드립니다. 이렇게하면 DB에 select nextval('thread_seq') 쿼리만 날라갑니다.
- GenerationType.AUTO 로 설정해서 DB에서 선호하는 방식으로 자동적용시켜줄 수도 있습니다.
3. 마지막으로, orphanRemoval() 영속성 전이에 의해 자식의 삭제작업이 이루어지는건 아닌지 확인해봐야합니다.
- 이런경우 1차캐시 최적화가 아닌 삭제쿼리가 쓰기지연 저장소에 저장되어 동작하게 되므로 후처리로 발생하게 됩니다.
save() 를 수행했는데 Insert, Select 쿼리가 날라갔어요!
ID(식별자 키) 값이 존재하는 Entity를 save() 하면 업데이트할 필드가 있는지 확인하기 위해 Select 쿼리를 먼저 날려서 조회 해옵니다.
save() 를 수행하는 코드:
@Transactional
@Override
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (entityInformation.isNew(entity)) {
em.persist(entity);
return entity;
} else {
return em.merge(entity);
}
}
- 즉, save(S) 메소드는 엔티티에 식별자 값이 없으면(null이면) 새로운 엔티티로 판단해서 EntityManager.persist를 호출하고,
- 2식별자 값이 있으면 이미 있는 엔티티로 판단해서 EntityManager.merge()를 호출합니다.
- EntityManager.merge() 를 수행할때 수정할 필드가 없는지 확인하기 위해 식별자 값을 가지고 Select 쿼리를 먼저 날려서 조회 해오고,
- 조회된 필드들을 엔티티와 비교해서 수정된 사항이 있으면 UPDATE 쿼리를 날리고, 모든 필드가 같으면 아무 쿼리도 날리지 않습니다.
Test 클래스에 @
Spring 통합 테스트 vs JPA 슬라이스 테스트
위 2개를 비교했을때 통합테스트는 아래와 같은 단점이 있기 때문에 가능하다면 슬라이스 테스트를 추천드립니다.
- 실제 구동되는 애플리케이션의 설정, 모든 Bean을 로드하기 때문에 시간이 오래걸리고 무겁다.
- 테스트 단위가 크기 때문에 디버깅이 어려운 편이다.
- 결과적으로 웹을 실행시키지 않고 테스트 코드를 통해 빠른 피드백을 받을 수 있다는 장점이 희석된다.
Spring 통합 테스트 @
@SpringBootTest
// SpringApplication 띄울때의 빈들을 모두 생성해줍니다.
@Transactional
// 테스트 메소드들이 모두 트랜잭션에 포함되어 최적화 되도록 합니다.
// 테스트 대상 함수의 실행환경에서는 Transaction이 안걸려 있을 수 있으니 실무에 사용시 주의
@Rollback(value = false) // 테스트 데이터가 롤백되지 않고 실제 DB에 반영되도록 합니다.
JPA 관련 빈 만 사용하는 JPA 슬라이스 테스트 @
@DataJpaTest
// SpringDataJpa 테스트에 필요한 빈들만 생성해줍니다.
@Transactional
// 테스트 메소드들이 모두 트랜잭션에 포함되어 최적화 되도록 합니다.
// 테스트 대상 함수의 실행환경에서는 Transaction이 안걸려 있을 수 있으니 실무에 사용시 주의
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
// 테스트 용 DB를 따로 설정하지 않고 main 환경 DB를 그대로 사용하도록 합니다.
@Import(JPAConfiguration.class)
// JPAQueryFactory와 같이 테스트시 필요한 빈들을 정의해놓은 Configuration 설정합니다.
@Rollback(value = false)
// 테스트 데이터가 롤백되지 않고 실제 DB에 반영되도록 합니다.