Programming/Querydsl

Querydsl - 중급 문법 (3) 수정, 삭제 벌크 연산 / SQL function 호출

잇(IT) 2023. 7. 28. 17:26
- 쿼리 한번으로 대량 데이터 수정

 

- 수정, 삭제의 벌크 연산의 경우 영속성 컨텍스트를 통하는 것이 아닌 DB에 직접 쿼리를 날리는 것이기 때문에, 영속성 컨텍스트의 데이터와 불일치하는 상황이 발생한다.

- 영속성 컨텍스트와 DB의 데이터를 일치시켜주지 않을 경우 데이터 일치성에 있어서 큰 문제가 발생 할 수 있다.

 

@Test
    public void bulkUpdate() {

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

- 위와 같이 코드를 작성하여 실행하게 되면 where문의 조건에 맞게 DB의 데이터들은 변경되지만 영속성 컨텍스트의 값은 해당 조건과 무관하게 변경되지 않는다.

 

- JPA는 select이 호출되면 DB에서 값을 가져와도 영속성 컨텍스트에 값이 있으면 DB에서 가져온 값을 버리고 영속성 컨텍스트에 있는 값을 사용한다. 즉, 영속성 컨텍스트가 항상 우선권을 가진다.

em.flush();
        em.clear();

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

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

- 때문에 위와 같이 flush()와 clear() 메서드를 사용하여 영속성 컨텍스를 비워주고 select 쿼리 호출 시 새롭게 DB에서 데이터를 가져와 영속성 컨텍스트를 채워넣어 줘야 데이터의 일치가 보장된다. 

 

1. flush(), clear()를 해주지 않은 경우

- 위 결과와 같이 DB에는 데이터가 변경되었지만 select를 통해 member 엔티티를 조회하게 되면 변경되지 않은 값이 출력되는 것을 확인 할 수 있다.

 

2. flush(), clear()를 메서드를 적용할 경우

- DB와 영속성 컨텍스트의 데이터가 일치하는 것을 확인 할 수 있다.


@Test
    public void bulkAdd() {
        long count = queryFactory
                .update(member)
//                .set(member.age, member.age.add(1))
                .set(member.age, member.age.multiply(2))
                .execute();
    }

    @Test
    public void bulkDelete() {
        long count = queryFactory
                .delete(member)
                .where(member.age.gt(18))
                .execute();
    }


- SQL function 호출하기

 

- SQL function은 JPA와 같이 Dialect에 등록된 내용만 호출 할 수 있다.

 

- member -> M으로 변경하는 replace 함수 사용

@Test
    public void sqlFunction() {
        List<String> result = queryFactory
                .select(Expressions.stringTemplate(
                        "function('replace', {0}, {1}, {2})",
                        member.username, "member", "M"
                ))
                .from(member)
                .fetch();

        for (String s : result) {
            System.out.println("s = " + s);
        }
    }

- function 뒤으 {0}, {1}, {2} 부분에 아래 작성된 member.username, "member", "M"이 대입되어 replace가 적용되어 데이터를 변경한다.

- replace가 어떻게 사용되는지 궁금할 수 있다.

- 위와 같이 H2Dialect 클래스에 많은 SQLFunction이 정의되어 있어 가져다가 사용하면 된다.

@Test
    public void sqlFunction2() {
        List<String> result = queryFactory
                .select(member.username)
                .from(member)
//                .where(member.username.eq(
//                        Expressions.stringTemplate("function('lower', {0})", member.username)))
                .where(member.username.eq(member.username.lower()))
                .fetch();

        for (String s : result) {
            System.out.println("s = " + s);
        }
    }

- 모든 DB에서 사용할 수 있는 간단한 SQL 문법들은 위와 같이

.where(member.username.eq(
	Expressions.stringTemplate("function('lower', {0})", member.username))

- SQLFunction을 굳이 사용하기보단

.where(member.username.eq(member.username.lower()))

- 위와 같이 JPA가 기본적으로 제공하기 때문에 사용하면 된다.

 

 

 

 

 

 

 

 

 

 

 

출처 : 인프런 - 김영한(실전! Querydsl)

728x90