재학습/JPA

JPA 필요없는 쿼리가 나간다?

재이든 2021. 7. 21. 00:22
반응형

이상한 일이였다.

쿼리가 많이 나갈 건 알고있었던, 나의 로직.

 

아니나 다를까.. 속도가 너무 느렸다.

따라서 쏘는 쿼리들을 까본 결과

예상밖의 쿼리가 나가고있었다.

 

바로 필요없는 쿼리가 더 나가고 있었던 것..!

 

내가 짠 로직 상으로는 Insert쿼리만 나가면되는데,

대체 왜 Select -> Insert 순으로, 쿼리가 2번이 나가버리는걸까?

 

[배경]

1. Custom String ID를 사용한다. (ID를 정한 상태로 Persist합니다.)

2.JpaRepository를 상속하여 사용합니다.

 

[로직]

public void myLogic(User user) {
	User createdUser =  userRepository.save(user);      
	createShop(createdUser.getId());
}  


public void createShop(String userId) {
    
  	User user = userRepository.findById(userId);

  	String nextShopId = userRepository.getNextShopId();
  	Shop shop = Shop.of(nextShopId, user);

	return shopRepository.save(shop);
}

위의 로직이 실행된다고 했을 때,

나는 

1.userRepository.save로 인한 Insert쿼리

2.userRepository.getNextShopId로 인한 Select쿼리

3.shopRepository.save로 인한 Insert쿼리를 예상했다.

 

그러나 결과는,

1의 경우 Select User -> Insert User

2의 경우 Select 

3의 경우 Select Shop -> Insert Shop

이렇게 총 2번의 쿼리가 나갔다.

 

[원인]

이 동작의 원인은 JpaRepository의 save메소드에 있다.

isNew메서드를 통하여, 새로운 엔티티인지 아닌지를 판단한다.

새로운 엔티티면 persist를,

아니면 merge를 호출한다.

 

나는 새로운 엔티티를 Save하려는 것이니까, Persist가 나가야 마땅했으나,

Merge가 실행된것이다.

 

Why?

==> @ID 필드에 값이 이미 존재하기 때문에, DB에 이미 있는 데이터로 간주합니다!!

 

1.따라서 Select를 통해 Update할 항목이 있는지 체크합니다.

2.Select결과가 없기때문에 Insert를 수행합니다.

 

자.. 그렇다면 원인은 찾았다.

그렇다면 이를 어떻게 해결해 줄 수 있을까?

==> Persistable 인터페이스를 구현하자!

 

[Persistable 인터페이스]

1.Persistable인터페이스를 구현한다.

2.@Transient로 isNew필드를 만든다. (초기값 true)

-@Transient는 DB의 컬럼과는 상관없음을 선언해준다.

3.isNew를 Override해주어 isNew필드를 반환한다.

4.@PrePersist와 @PostLoad를 통해 영속성객체로 만들어질 때 ,isNew를 False로 변경한다.

-그러면 DB에 Insert가 나간 후 이거나, DB로부터 Load되었다면 isNew가 False가 된다.

 

자, 그럼 다시 JpaRepository의 save메소드를 보자.

Persistable적용 후,

1.isNew를 하면, 기본값 true이기 때문에 em.persist가 호출된다.

2.em.persist 또는 Find되었다면 isNew가 False가 된다.

3.이 다음, User엔티티를 수정하기위해 save를 호출한다면, isNew는 False이기떄문에 em.merge가 호출된다.

 

[결과]

위의 방법으로 불필요한 Select쿼리를 줄여 속도향상에 도움을 줄 수 있었다.

지금은 save가 많이 호출되는 로직이여서 티가났지, 아니였으면 찾아볼 생각도 하지 않았을것 같다.

 

만약 ID를 지정해주어 Save해주는 구조를 다시한번 개발할때가 온다면,

그때는 Persistable인터페이스를 통해, 개발단계에서부터 바로 최적화를 해주면 좋을것 같다.

반응형