Spring/QueryDSL

수정, 삭제 벌크 연산 - 배치 쿼리

느리지만 꾸준하게 2022. 4. 21. 03:03

쿼리 한번으로 대량의 데이터를 수정할 때 사용한다.

 

JPA는 기본적으로  엔티티를 가지고 와서 엔티티의 값만 바꾸면 트랜잭션 commit할 때 flush가 일어나면서(변경 감지)

엔티티가 바뀐걸 감지하고 업데이트 쿼리가 만들어지고 db에 업데이트 쿼리가 나가게 된다.

 

모든 개발자의 연봉 50% 인상해 => 쿼리 한번에 날리고 트랜잭션 커밋하는게 낫다.

 

 

실행을 시키고 h2 db에서 확인을 해보면 아래와 같이 나오게 된다.

@Test
@Commit
public void bulkUpdate() {


    // member1 = 10 -> 비회원
    // member2 = 20 -> 비회원
    // member3 = 30 -> 유지
    // member4 = 40 -> 유지

    long count = queryFactory
            .update(member)
            .set(member.username, "비회원")
            .where(member.age.lt(28))
            .execute();
}

 

 

 

 

 

출력은 아래와 같이 출력이 된다.

// db의 쿼리는 select 쿼리가 제대로 나갔다.

/* select
        member1 
    from
        Member member1 */ select
            member0_.id as id1_1_,
            member0_.age as age2_1_,
            member0_.team_id as team_id4_1_,
            member0_.username as username3_1_ 
        from
            member member0_
member1 = Member(id=3, username=member1, age=10)
member1 = Member(id=4, username=member2, age=20)
member1 = Member(id=5, username=member3, age=30)
member1 = Member(id=6, username=member4, age=40)

 

 

 

h2 db를 보면 비회원 두개가 있는데

영속성 컨텍스트에는 member 1 2로 남아있다. 즉 db에 있는걸 버린다.  영속성 컨텍스트에 있는게 항상 우선권을 가지는 것이다.

Repeatable read

//1 member1 = 10 -> 1 DB 비회원
//2 member2 = 20 -> 2 DB 비회원
//3 member3 = 30 -> 3 member3
//4 member4 = 40 -> 4 member4

 

 

 

 

 

 

flush clear를 하면 결과가 제대로 나오는 것을 볼 수 있다.

@Test
    @Commit
    public void bulkUpdate() {

        // 쿼리 실행전
        // member1 = 10 -> DB member1
        // member2 = 20 -> DB member2
        // member3 = 30 -> DB member3
        // member4 = 40 -> DB member4


        // 벌크연산은 영속성 컨텍스트를 무시하고 db에 바로 쿼리가 날라간다.
        // db의 상태와 영속성 컨텍스트의 상태가 달라진다.
        long count = queryFactory
                .update(member)
                .set(member.username, "비회원")
                .where(member.age.lt(28))
                .execute();

        // 그냥 이렇게 나타내자.

        // 영속성 컨텍스트에 있는걸 flush 해서 다 보내고 = db랑 데이터 맞추고
        em.flush();
        // 영속성 컨텍스트에 있는 데이터 초기화를 해버린다.(벌크연산 한거 자체가 db랑 영속성 컨텍스트가 서로 안맞기 때문에 초기화 )
        em.clear();




        
// 쿼리가 실행된 이후에는 아래와 같다. 이전이랑 상태가 안맞다.
        // JPA는 기본적으로 db에서 가져온걸 영속성 컨텍스트에 넣어줘야 한다.
        // db에서 select해서 4줄이 와도 영속성 컨텍스트에 다시 보관해야하느데
        // 그때 중복이 있다 그러면 1 member1 그대로 유지가 된다.

        //1 member1 = 10 -> 1 DB 비회원
        //2 member2 = 20 -> 2 DB 비회원
        //3 member3 = 30 -> 3 member3
        //4 member4 = 40 -> 4 member4

        List<Member> result = queryFactory
                .selectFrom(member)
                .fetch();

        for (Member member1 : result) {
            System.out.println("member1 = " + member1);
        }
    }

 

영속성 컨텍스트를 다 날려버렸기 때문에 결과가 제대로 나온다.

즉 벌크연산을 실행하고 나면 영속성 컨텍스트를 초기화하는게 낫다.

member1 = Member(id=3, username=비회원, age=10)
member1 = Member(id=4, username=비회원, age=20)
member1 = Member(id=5, username=member3, age=30)
member1 = Member(id=6, username=member4, age=40)

 

기존연산에 더하거나 곱해주는 add와 multiply로 테스트를 돌려보자.

// 기존쿼리에 더하기 add로 
    @Test
    public void bulkAdd() {
        long count = queryFactory
                .update(member)
                .set(member.age, member.age.add(1))
                .execute();
    }
/* update
        Member member1 
    set
        member1.age = member1.age + ?1 */ update
            member 
        set
            age=age+?

 

 

// 기존쿼리에 곱하기
@Test
public void bulkAdd() {
    long count = queryFactory
            .update(member)
            .set(member.age, member.age.multiply(2))
            .execute();
}
 /* update
        Member member1 
    set
        member1.age = member1.age * ?1 */ update
            member 
        set
            age=age*?

 

 

 

delete를 해주는 연산을 해주면 delete query가 나가게 된다.

@Test
public void bulkDelete() {
    queryFactory
            .delete(member)
            .where(member.age.gt(18))
            .execute();
}
/* delete 
    from
        Member member1 
    where
        member1.age > ?1 */ delete 
        from
            member 
        where
            age>?

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<출처 김영한: 실전! Querydsl >

https://www.inflearn.com/course/Querydsl-%EC%8B%A4%EC%A0%84/dashboard

 

실전! Querydsl - 인프런 | 강의

Querydsl의 기초부터 실무 활용까지, 한번에 해결해보세요!, - 강의 소개 | 인프런...

www.inflearn.com