스프링데이터 + JPA/QueryDSL

25. 수정, 삭제 벌크연산

sdafdq 2023. 12. 2. 14:12

벌크란 여러 row들을 한번에 처리 하는 것.

 

근데 벌크는, 영속성 컨텍스트를 무시하고 직접 DB에 쿼리를 넣는다.

그래서, 만약 조회해 온 다음에 벌크를 하게 되면, 영속성 컨텍스트에 있는 데이터와 DB에 있는 데이터가 맞지 않을 수도 있다.

 

영속성 컨텍스트에 엔티티가 있으면 조회했을 때 그걸 주니까. 

 

보통 그래서 해결책은

벌크연산 이후에 데이터를 조회해오거나

벌크연산 후 영속성 컨텍스트를 초기화 시켜버리는 것

https://qwefdg3.tistory.com/800

 

그래서 아예 스프링 Data JPA에서는 옵션을 제공해 줌

https://qwefdg3.tistory.com/886

이렇게 수정 등을 했을 때(delete도 수정)  자동으로 영속성 컨텍스트를 초기화 해 준다던지 그런 옵션이 있음.

 

 

@Test
@Commit
public void bulkUpdate(){
    long count = query.update(m).set(m.username, "비회원").where(m.age.lt(28))
            .execute();

    assertThat(count).isEqualTo(2);
}

문법은 간단함

QueryFactory.update(엔티티).set(바꿀열, 데이터).where(대상조건).excute();

저런 업데이트 쿼리같은 건 execute()로 실행.

 

excute()는 영향받은 쿼리의 수를 반환

 

테스트 통과고, DB상에서 봐도 반영이 되었음.

/* update
    Member member1 
set
    member1.username = ?1 
where
    member1.age < ?2 */ update member 
set
    username=? 
where
    age<?

정확히 친 데로 m.age가 < 28인 것의 username을 바꿈

 

 

 

 

@Test
public void bulkAdd(){
    long count = query.update(m).set(m.age, m.age.add(1))
            .execute();

    assertThat(count).isEqualTo(4);
}

더할 때.

m.age.add()  이런 표현식이 있었음.

근데 minus같은 건 없음. 그냥 m.age.add(-1) 이런 식으로 하면 됨.

/* update
    Member member1 
set
    member1.age = member1.age + ?1 */ update member 
set
    age=(age+cast(? as integer))

보면 m.age.add  하면 m.age + ? 이렇게 됨.

 

 

 

 

 

@Test
@Commit
public void bulkMultiple(){
    long count = query.update(m).set(m.age, m.age.multiply(2))
            .execute();

    assertThat(count).isEqualTo(4);
}

곱하기는 있음.

divide() 라고 해서 나누기도 지원.

 

/* update
    Member member1 
set
    member1.age = member1.age * ?1 */ update member 
set
    age=(age*cast(? as integer))

잘 나간다. SQL에서 값은 integer로 캐스팅해서 계산하는 것은 방어적으로 하기 위해 우선 integer인지 확인하는 용도 인 듯.

 

 

 

@Test
@Commit
public void bulkDelete(){
    long count = query.delete(m).where(m.age.gt(18))
                    .execute();

    assertThat(count).isEqualTo(3);
}

지우는 거

QueryFactory.delete(m).where(대상조건).excute()
/* delete 
from
    Member member1 
where
    member1.age > ?1 */ delete 
from
    member 
where
    age>?

delete 쿼리 잘 나감. 

 

 

 

그러니까 벌크연산으로 일어날 수 있는 부작용이란게, 

예를 들어 조회해 온 후, 

그렇게 영속성 컨텍스트에 남아있는 상태에서 저 delete 벌크 연산을 때려버리면,

조회해올때 DB에 먼저 날리기 보다 영속성 컨텍스트에 있으면 그걸 반환해 주는데,

근데 delete 벌크 연산 날려서 이미 DB에는 없는데 영속성 컨텍스트에는 있어서 조회가 될 수도 있음.

 

그래서, 벌크연산 날린 후는 em.flush(), em.clear()  이렇게 해서 비워놔야 함.

 

 

'스프링데이터 + JPA > QueryDSL' 카테고리의 다른 글

27. 순수 JPA와 QueryDSL  (0) 2023.12.03
26. SQL 함수 호출  (0) 2023.12.02
24. 동적쿼리. Where 다중 파라미터  (0) 2023.12.01
23. 동적쿼리. BooleanBuilder 방식  (0) 2023.12.01
22. @QueryProjection  (0) 2023.12.01