쿼리 한번으로 대량의 데이터를 수정할 때 사용한다.
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
'Spring > QueryDSL' 카테고리의 다른 글
SQL function 호출하기 (0) | 2022.04.21 |
---|---|
동적 쿼리 - Where 다중 파라미터 사용 (0) | 2022.04.21 |
동적 쿼리 - BooleanBuilder (0) | 2022.04.21 |
프로젝션과 결과 반환 - @QueryProjection (0) | 2022.04.21 |
프로젝션 결과 반환 - DTO 조회 (0) | 2022.04.21 |