본문 바로가기


JPA

[JPA] save 와 saveAndFlush의 차이

OS : MacOs Mojave

DB : MySQL 5.7

DB Tool : Sequel Pro

Framework : Spring Boot 2.0

 

맨밑에 결론있음

1. 준비

다음과 같은 member Entity를 준비했다.

public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Long id;

    @Column(name = "uuid")
    private String uuid;

    @Column(name = "name")
    private String name;
}

2. @Transactional 없이 save와 saveAndFlush 비교

1. save or saveAndFlush 이후의 set을 통한 변경

각각 return 문에 break point를 걸고 수행해 보았다.

1. save 한 후 set 할 경우 

public Member createMember(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.save(member);
    member.setName("ChangeName");
    return member;
}

breakpoint 에서는 

Hibernate: 

    insert 

    into

        member

        (created_at, last_login, name, uuid) 

    values

        (?, ?, ?, ?)

위 같은 메세지가 console 에 출력되며 DB에도 업데이트가 된다.
 

해당 메소드 수행이 종료될 때 

DB 에 바뀐 name이 값으로 들어가지 않는다.

2. saveAndFlush 하고 set할 경우 에는

public Member createMember2(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush");
    return member;
}
breakpoint 에서는
Hibernate: 
    insert 
    into
        member
        (created_at, last_login, name, uuid) 
    values
        (?, ?, ?, ?)
위 같은 메세지가 console 에 출력되며 DB에도 업데이트가 된다.
 

해당 메소드 수행이 종료될 때 

DB 에 바뀐 name이 값으로 들어가지 않는다.

 

 

2. save or saveAndFlush 이후의 set을 통한 변경 후 다시 save or saveAndFlush

동일하게 return 문에 breakpoint를 걸었다.

3. save 한 후 set 하고 또 다시 save 할 경우

public Member createMember(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.save(member);
    member.setName("ChangeName");
    return memberRepository.save(member);
}
breakpoint 에서는
Hibernate: 
    insert 
    into
        member
        (created_at, last_login, name, uuid) 
    values
        (?, ?, ?, ?)
위 같은 메세지가 console 에 출력되며 DB에도 업데이트가 된다.
 

해당 메소드 수행이 종료될 때 

 
Hibernate: 
    update
        member 
    set
        created_at=?,
        last_login=?,
        name=?,
        uuid=? 
    where
        id=?
위 같은 메세지가 console 에 추가로 출력되며 DB에도 업데이트가 된다.

4. saveAndFlush 한 후 set 하고 또 다시 saveAndFlush 할 경우

public Member createMember2(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush");
    return memberRepository.saveAndFlush(member);
}
breakpoint 에서는
Hibernate: 
    insert 
    into
        member
        (created_at, last_login, name, uuid) 
    values
        (?, ?, ?, ?)
위 같은 메세지가 console 에 출력되며 DB에도 업데이트가 된다.
 

해당 메소드 수행이 종료될 때 

 
Hibernate: 
    update
        member 
    set
        created_at=?,
        last_login=?,
        name=?,
        uuid=? 
    where
        id=?
위 같은 메세지가 console 에 추가로 출력되며 DB에도 업데이트가 된다.
 

 

 
결과적으로 동일하게 수행된다.

3. @Transactional 있는 상태에서 save와 saveAndFlush 비교

1. save or saveAndFlush 이후의 set을 통한 변경

1. save 한 후 set 할 경우 

@Transactional
public Member createMember(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.save(member);
    member.setName("ChangeName");
    return member;
}
breakpoint 에서는
Hibernate: 
    insert 
    into
        member
        (created_at, last_login, name, uuid) 
    values
        (?, ?, ?, ?)
위 같은 메세지가 console 에 출력되며 DB에 업데이트가 되지 않는다.
 

해당 메소드 수행이 종료될 때 

 
Hibernate: 
    update
        member 
    set
        created_at=?,
        last_login=?,
        name=?,
        uuid=? 
    where
        id=?
위 같은 메세지가 console 에 추가로 출력되며 DB에 최종버전으로 업데이트가 된다.
 

1. saveAndFlush 한 후 set 할 경우 

@Transactional
public Member createMember2(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush");
    return member;
}
breakpoint 에서는
Hibernate: 
    insert 
    into
        member
        (created_at, last_login, name, uuid) 
    values
        (?, ?, ?, ?)
위 같은 메세지가 console 에 출력되며 DB에 업데이트가 되지 않는다.
 

해당 메소드 수행이 종료될 때 

 
Hibernate: 
    update
        member 
    set
        created_at=?,
        last_login=?,
        name=?,
        uuid=? 
    where
        id=?
위 같은 메세지가 console 에 추가로 출력되며 DB에 최종버전으로 업데이트가 된다.

2. save or saveAndFlush 이후의 set을 통한 변경 이후 save or saveAndFlush

 

3. save 한 후 set 하고 또 다시 save 할 경우

 

이렇게 하고 각각 최초 save 문을 제외하고 breakpoint 를 걸었다.

 

첫번째 breakpoint

Hibernate: 

    insert 

    into

        member

        (created_at, last_login, name, uuid) 

    values

        (?, ?, ?, ?)

console 출력 / DB업데이트 안됨

 

두번째 breakpoint

console 출력 없음 / DB 업데이트 안됨

 

세번째 breakpoint (return문)

console 출력 없음 / DB 업데이트 안됨

 

메소드 종료 후

Hibernate: 

    update

        member 

    set

        created_at=?,

        last_login=?,

        name=?,

        uuid=? 

    where

        id=?

console 출력 / DB 업데이트

 

4. saveAndFlush 한 후 set 하고 또 다시 saveAndFlush 할 경우

@Transactional
public Member createMember2(MemberRequest request) {
    Member member = Member.create(request);
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush");
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush2");
    member = memberRepository.saveAndFlush(member);
    member.setName("ChangeNameFlush3");
    return member;
}

첫번째 breakpoint

Hibernate: 

    insert 

    into

        member

        (created_at, last_login, name, uuid) 

    values

        (?, ?, ?, ?)

console 출력 / DB업데이트 안됨

 

두번째 breakpoint

Hibernate: 

    update

        member 

    set

        created_at=?,

        last_login=?,

        name=?,

        uuid=? 

    where

        id=?

console 출력 / DB업데이트 안됨

 

세번째 breakpoint (return문)

Hibernate: 

    update

        member 

    set

        created_at=?,

        last_login=?,

        name=?,

        uuid=? 

    where

        id=?

console 출력 / DB업데이트 안됨

 

메소드 종료 후

console 출력 / DB 업데이트

 

 

 

 

결론

위의 여러가지 동작 방식으로 알 수 있었던 점은 
JpaRepository의 saveAndFlush에 있는 Flush 는 DB에 업데이트를 하는 Flush 가 아니라 
다음과 같은 그림에서 Context내에 Query Space (임의로 지정한 이름입니다) 로 flush를 하는 과정이라고 생각된다.
saveAndFlus로 업데이트를하면 매번 query를 queryspace에 보내고 그걸 트랜잭션 종료시점에 db에 업데이트를 하고
save를 하면 마지막에 context에 존재하는 형태의 데이터를 query로 만들어서 트랙잭션 종료시점에 db에 업데이트 하게 되는 것이다.
따라서 효율성 측면에서 saveAndFlush 보다는 save를 추천한다.

주의 : insert (최초 data 생성과정은 무조건 query space에 업데이트된다)
 

 

 

끝!