@Override
public void deleteTagMappingByCardId(Long cardId) {
queryFactory
.delete(cardHashTag)
.where(cardHashTag.card.id.eq(cardId))
.execute();
}
어제 만들어놨던 코드인데, 위 부분에 문제가 있다는 것을 알 수 있다.
벌크 연산인데, EntityManager flush(), clear() 을 하지 않는다는 점이다.
flush및 clear를 추가하지 않은 이유는 밑에와 같다.
위의 이유에서 추가적인 이유가 있었다.
나는 편의를 위해 JpaConfig 라는 클래스를 만들어서 JPAQueryFactory를 빈으로 만들고 주입받고 있었다.
@RequiredArgsConstructor
@EnableJpaAuditing
@Configuration
public class JpaConfig {
private final EntityManager em;
@Bean
JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
근데 Repository에서 flush및 clear를 하려면 만들어 놨던 JPAQueryFactory 빈을 사용하지 않고,
위와 같은 방법으로 사용해야하는데, 나는 이게 진짜 마음에 안들었던 것이다.
왜 JPAQueryFactory 를 만들 때, EntityManager를 쓰는데 JPAQueryFactory 자체에서는 flush 및 clear 기능을 안 넣어둔걸까?
그러면 정말 깔끔하게 쓸 수 있을 텐데, 코드가 이쁘지도 않고 진짜 너무 마음에 안들었다.
그러다가
@Modifying 이라는 어노테이션을 알게 됐는데
@Modifying
JPQL에서 이 어노테이션을 붙이면 메소드 실행 이후 flush및 clear를 자동으로 해준다.
이런 점 때문에 벌크 연산이 들어가는 메소드에 사용한다
자세한 설명은 위 블로그나 검색해서 보면 될 거 같다.
? 그러면 QueryDsl도 내부적으로는 JPQL을 쓰고 있으니까 이 어노테이션을 붙이면 되지 않을까?
테스트를 해봤다.
테스트 시나리오
기존 - em.flush() / em.clear() 를 하고 나서 레포지토리에서 find를 하면 한번 더 Select 문을 날린다.
테스트 - @Modifying이 달려있는 상태에서, 레포지토리에 find를 하면 한번 더 Select문을 날려야한다. (컨택스트가 비어져있기 때문에)
em.flush() 및 em.clear() 를 했을 때
public class CardHashTagQueryRepositoryImpl implements CardHashTagQueryRepository {
private final JPAQueryFactory queryFactory;
private final EntityManager em;
public CardHashTagQueryRepositoryImpl(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
@Override
public void deleteTagMappingByCardId(Long cardId) {
queryFactory
.delete(cardHashTag)
.where(cardHashTag.card.id.eq(cardId))
.execute();
em.flush();
em.clear();
}
}
@Test
void test() {
User user = User.builder().username("username").password("password").role(UserRoleEnum.USER).build();
User savedUser = userRepository.save(user);
Card card = new Card("제목", "내용#나나", savedUser);
HashTag hashTag = HashTag.builder().name("#나나").build();
HashTag savedHashTag = hashTagRepository.save(hashTag);
Card savedCard = cardRepository.save(card);
CardHashTag cardHashTag = new CardHashTag(savedCard,savedHashTag);
CardHashTag savedCardHashTag = cardHashTagRepository.save(cardHashTag);
cardHashTagRepository.findById(savedCardHashTag.getId());
System.out.println("--------------------------------------");
System.out.println("--------------------------------------");
System.out.println("--------------------------------------");
cardHashTagRepository.deleteTagMappingByCardId(savedCard.getId());
System.out.println("------벌크연산 이후-------");
System.out.println("------벌크연산 이후-------");
System.out.println("------벌크연산 이후-------");
cardHashTagRepository.findById(cardHashTag.getId());
}
---벌크연산 이후--- 만 보면 된다.
결과
------벌크연산 이후-------
------벌크연산 이후-------
------벌크연산 이후-------
Hibernate:
select
c1_0.id,
c2_0.id,
c2_0.content,
c2_0.created_at,
c2_0.is_done,
c2_0.modified_at,
c2_0.title,
c2_0.user_id,
u1_0.id,
u1_0.created_at,
u1_0.password,
u1_0.role,
u1_0.username,
h1_0.id,
h1_0.name
from
card_hashtags c1_0
left join
cards c2_0
on c2_0.id=c1_0.card_id
left join
users u1_0
on u1_0.id=c2_0.user_id
left join
hashtags h1_0
on h1_0.id=c1_0.hashtag_id
where
c1_0.id=?
벌크 연산 이후 Select 문이 나갔다, 이건 뭐 예상 한 결과
@Modifying 을 달았을 때
public class CardHashTagQueryRepositoryImpl implements CardHashTagQueryRepository {
private final JPAQueryFactory queryFactory;
private final EntityManager em;
public CardHashTagQueryRepositoryImpl(EntityManager em) {
this.em = em;
this.queryFactory = new JPAQueryFactory(em);
}
@Override
@Modifying(clearAutomatically = true)
public void deleteTagMappingByCardId(Long cardId) {
queryFactory
.delete(cardHashTag)
.where(cardHashTag.card.id.eq(cardId))
.execute();
// em.flush();
// em.clear();
}
}
결과
------벌크연산 이후-------
------벌크연산 이후-------
------벌크연산 이후-------
2023-12-21T12:24:01.900+09:00 INFO 12256 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
적용이 안되는 것을 볼 수 있다.
@Modyfing을 인터페이스 추상 메서드에다가 달아볼까??
package com.sparta.todoapp.domain.card_hashtag.repository;
import org.springframework.data.jpa.repository.Modifying;
public interface CardHashTagQueryRepository {
@Modifying(clearAutomatically = true)
void deleteTagMappingByCardId(Long cardId);
}
결과
------벌크연산 이후-------
------벌크연산 이후-------
------벌크연산 이후-------
2023-12-21T12:26:59.850+09:00 INFO 23224 --- [ionShutdownHook] j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
> Task :test
안되는 것을 볼 수 있다..........
왜 안될까??
진짜 불만이다
JPAQueryFactory를 Config 클래스에 등록해서 간단하게 쓰고싶은데, Repository 마다 EntityManager로 주입해야한다는게 마음에 안든다.
그래서 쿼리를 쓰는 곳 위에 붙여주면 자동으로 clear및 flush를 해주는 Modifying 을 테스트 해본건데, QueryDsl에는 적용이 안되는 모습을 볼 수 있다...
아쉽게도 QueryFactory 를 빈으로 받는 편리함은 포기해야 할 거 같다.
'TIL' 카테고리의 다른 글
TIL 2023-12-26 @ColumnDefault 과 Nullable (0) | 2023.12.26 |
---|---|
TIL 2023-12-22 의존성에 대한... (0) | 2023.12.22 |
TIL 2023-12-20 스프링 해시태그 기능 구현 (0) | 2023.12.20 |
TIL 2023-12-19 @WebMvcTest 와 Application JPAQueryFactory Bean 등록 오류 (2) | 2023.12.19 |
TIL 2023-12-18 QueryDSL Qclass 살펴보기... 삽질 (0) | 2023.12.18 |