Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Archives
Today
Total
관리 메뉴

개발자되기 프로젝트

Entity Cache 본문

JPA/영속성

Entity Cache

Seung__ 2021. 7. 2. 23:10

https://github.com/bsh6463/BookManager

 

bsh6463/BookManager

Contribute to bsh6463/BookManager development by creating an account on GitHub.

github.com

 

 

1. Entity Manager


Entity Manager란? 

 Entity의 저장, 수정, 삭제, 업데이트 등 말그대로 entity를 관리함.

 

기존에 사용한 simple jpa repository는 직접적으로entity manager를 사용하지 않도록 감싸 spring에서 제공했음

 

실제 내부 동작은 entity manager을 통해서 이루어진다.

 

따라서 spring data jpa에서 제공하지 않는 기능을 사용하거나 특별히 custom을 할 경우

entity manager를 직접 받아서 사용해야 한다.

 

 

1-1) Entity Manager 사용해보자.

@SpringBootTest
public class EntityManagerTest {

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private UserRepository userRepository;

    @Test
    void entityManagerTest(){

        System.out.println(entityManager.createQuery("select u from User u").getResultList());
        //u라는 User ENtity를 user의 정볼르 모두 가져와라.
        // = userRepository.findAll();

    }
}

 

"selec u from User u"의 의미는 u라는 User Entity에서 u의 정보를 모두 가져와라 

즉, userRepository.findAll()과 같은 의미이다.

 

1-2)결과

 findAll()과 같은 결과를 볼 수 있다. db내의 user정보를 모두 불러왔다.

 

아래에서 얘기하겠지만, save를 실행하는 시점과 DB에 반영되는 시점에는 차이가 발생할 수 있다.

 

즉,  영속성 context와 실제 DB사이에 data gap이 발생한다.

 

영속성 컨텍스트

1. Context란? framework에서 container가 관리하고 있는 내용을 context라함 Spring의 경우 bean들을 로딩, 관리하는 작업들을 spring context위에서 활용되고 있음 즉, persistance container가 관리하고 있는..

bsh-developer.tistory.com

 

2. Entity Cache


 2-1) Cache(캐시)란??

    캐시는 데이터나 값을 복사해 놓은 임시 장소이다. 효율성을 위해 사용이 된다. 

    예를 들어 같은 정보를 반복해서 불러올 때, 매 번 접근해서 가져오는 것 보다는

    복사해 놓은 cache를 가져오는 편이 빠르다.

 

2-2) cache 를 찾아보자.

  같은 정보를 연속으로 찾아보자.

 @Test
    void cacheFindTest(){

        System.out.println(userRepository.findById(1L).get());
        System.out.println(userRepository.findById(1L).get());
        System.out.println(userRepository.findById(1L).get());

    }

  해당 코드를 실행하면 select query가 3 번 실행된다.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
User(super=BaseEntity(createdAt=2021-07-02T20:40:26, updatedAt=2021-07-02T20:40:26), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
User(super=BaseEntity(createdAt=2021-07-02T20:40:26, updatedAt=2021-07-02T20:40:26), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
User(super=BaseEntity(createdAt=2021-07-02T20:40:26, updatedAt=2021-07-02T20:40:26), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)

 

2-3) Entity Cache 동작 확인

Test Class에 @Transactional을 붙여주자. @Transactional을 붙여주고 동일한 test를 실행해보자.

 

왜..? @Transactional을 붙여주면 id를 찾는 경우는 1차 캐시를 활용할 수 있다. 아래에서 자세히 설명.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
User(super=BaseEntity(createdAt=2021-07-02T20:42:46, updatedAt=2021-07-02T20:42:46), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-02T20:42:46, updatedAt=2021-07-02T20:42:46), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-02T20:42:46, updatedAt=2021-07-02T20:42:46), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)

실행해보니 Select Query가 한 번 실행되고, print는 3 번 출력된다.

 

즉, 조회 시 영속성 context 내에 존재하는 entity cache에서 직접 처리한 것을 알 수 있다.

 

진짜 db를 조회하지 않았다.(=select query가 여러 번 일어나지 않았다.)

 

2-3) 1차 Cache란?

따로 설정을 하지 않았는데, 영속성 컨텍스트 내의 entity cache를 활용했다. 이 것을 JPA의 1차 cache라 한다.

 

1차 cache는 map의 형태로 만들어진다.  key는 id, value에는 해당 entity가 들어있다.

 

그래서 key 값인 id로 조회하는 경우는

영속성 컨텍스트 내의 존재하는 1차 캐시에 entity가 있는지 확인한다.

 

있으면 DB 조회 없이 바로 return!

 

1차 캐시내에 없으면 db조회하고 1차 캐시에 저장 후 return!

 

 

2-4) 그럼 id말고 email도 조회해보자.

  @Test
    void cacheFindTest(){

        System.out.println(userRepository.findByEmail("hyun@naver.com"));
        System.out.println(userRepository.findByEmail("hyun@naver.com"));
        System.out.println(userRepository.findByEmail("hyun@naver.com"));
        System.out.println(userRepository.findById(1L).get());
        System.out.println(userRepository.findById(1L).get());
        System.out.println(userRepository.findById(1L).get());

    }

 

실행한 결과 select query가 3번 만 실행되었다.

 

id로 찾지 않는 경우는 cache에서 처리되지 않았다.

 

이전 query로 인해 1차 cache에 id가 1인 entity가 저장이 되어 id로 찾는 경우는 query가 호출되지 않았다. 

 

Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
Hibernate: 
    select
        user0_.id as id1_6_,
        user0_.created_at as created_2_6_,
        user0_.updated_at as updated_3_6_,
        user0_.email as email4_6_,
        user0_.gender as gender5_6_,
        user0_.name as name6_6_ 
    from
        user user0_ 
    where
        user0_.email=?
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)
User(super=BaseEntity(createdAt=2021-07-02T20:49:41, updatedAt=2021-07-02T20:49:41), name=hyun, email=hyun@naver.com, id=1, testData=null, gender=null)

 

이처럼 1차 캐시 동작에 따라 기본적인  jpa의 조회성능이 올라간다.

사실 id를 통해 직접 조회하는 일은 드물다...고 한다.

 

하지만 jpa의 특성 상 id를 통해 조회하는 일이 자주 있음. 이 때 1차 캐시 활용하면 이득임.

 

2-5) 1차 캐시 활용

  @Transactional을 지워놓고( id가 1인 entity를 삭제해보자.)

  

   @Test
    void cacheFindTest(){

        userRepository.deleteById(1L);

    }

 

user id값으로 select를 한번 하고 delete를 진행한다. delete 직전에 select 가 진행되지 않는다.

즉 1차 캐시를 활용한 것을 알 수 있다.

 

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
Hibernate: 
    update
        review 
    set
        user_id=null 
    where
        user_id=?
Hibernate: 
    delete 
    from
        user 
    where
        id=?

 

이처럼  id로 조회하는 case가 자주  생긴다.

따라서 하나의  transaction내에서 동작할 때에는 1차 캐시를 사용해서 성능저하를 방지한다.

 

 

*참고) Transaction : 2021.07.02 - [JPA] - Transaction, @Transactional

 

Transaction, @Transactional

1. Transaction 데이터 베이스의 상태를 변환시키는 하나의 논리적 기능을 수행하기 위한 작업의 단위. 예를들어 A가 B에게 100만원을 송금한다 할 때. 논리적인 기능은 송금. 송금을 위한 작업은 "A인

bsh-developer.tistory.com

 

위에서 @Transactional을 주석처리한 이유는 1차캐시 역할 때문이다.

 

영속성 컨텍스트가 존재함에 다라 jpa의 특징인 "지연쓰기"가 발생한다.

 

실제로 @Transactional이 존재하면 db에 반영하는 시간을 최대한 늦춤

 

바로 위의 test를 @Transational 붙이고 돌려보자.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
2021-07-02 21:09:00.567  INFO 808 --- [    Test worker] o.s.t.c.transaction.TransactionContext   : Rolled back transaction for test: 

 

select 쿼리만 실행되고 delete 쿼리는 실행되지 않았다.

 

영속성 컨텍스트 내에서 entity manager가 자체적으로 entity 상태를 merge하고

최종적으로 DB에 반영해야 하는 내용에 대해서만 query가 실행된다.

 

이 경우 최종 commit이 되지 않고 test기 때문에 DB에 반영될 필요가 없어서 Roll back transaction이 실행됨.

 

즉 해당 delete 쿼리가 영속성 컨텍스트 내에 만  존재하고 실제 DB로 전달되지 않았다.

 

2-6)1차캐시 활용 2

(@Transactional 미적용)

@Test
    void cacheFindTest2(){

        User user = userRepository.findById(1L).get();
       user.setName("hyhyhyhyhy");

       userRepository.save(user);

        System.out.println("----------------------------");
        
       user.setEmail("hyhyhyhyhy@anver.com");

       userRepository.save(user);


    }

update query가 두 번 실행된다.

 

Transaction이 묶여있지 않기 때문에, 각 쿼리는 바로바로 반영되어야 한다.

 

즉 상위에서 transacion을 묶지 않아 sava각각이 transaction이 되어 바로바로 db에 반영이 된 것이다.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_ 
    from
        user user0_ 
    where
        user0_.id=?
Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?
----------------------------
Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_ 
    from
        user user0_ 
    where
        user0_.id=?
Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?

 

2-7) flush() : 영속성 context의 data를 의도적으로 DB에 반영

@Transactional을 적용하면 지연 쓰기가 발생한다. Test기 때문에 실제 db에 반영이 안되고 roll back을 하게되는데,

DB에 반영시키기 위해 userRepository.flush()를 추가해줬다.

@Test
    void cacheFindTest2(){

        User user = userRepository.findById(1L).get();
       user.setName("hyhyhyhyhy");

       userRepository.save(user);

        System.out.println("----------------------------");

       user.setEmail("hyhyhyhyhy@anver.com");

       userRepository.save(user);
       
       userRepository.flush();

    }

 

실행한 결과 update query가 한 번 밖에 없다.

 

영속성 컨텍스트 내에서 각각의 변경 내용을 가지고 있다가 merge를 해서 한 번의 update로 처리를 완료했다.

 

두 번을 해야하는 것을 entity cache를 통해서 두 쿼리를 merging하고 실제 db반영은 update 한 번으로 끝.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
----------------------------
Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?
Hibernate: 
    insert 
    into
        user_history
        (created_at, updated_at, email, name, user_id) 
    values
        (?, ?, ?, ?, ?)

 

영속성 컨텍스트에 쌓여 있는 data는 entity manager가 자체적으로 DB에 영속화를 해주지만

 

개발자가 의도한 time에 영속화가 이루어 지지 않는다.

 

개발자가 원하는 time에 영속화를 진행하기 위해 flush를 사용하게 된다.

 

flush를 남발하면 영속성 cache의 장점을 퇴색하게 된다. 알잘딱하자.

 

하나의 method에서 여러  번 update를 할 필요 없으면 자동으로 영속성 캐시 내애서 자동으로 merge하여

한 번만 업데이트 하는게 간결하다.

 

3. 실제 영속성 context와 DB가 동기화 되는 시점??


1) flush() 호출되는 경우

  의도적으로 영속성 캐시를 DB에 반영

 

(@Transactional)

  @Test
    void cacheFindTest2(){

        User user = userRepository.findById(1L).get();
       user.setName("hyhyhyhyhy");

       userRepository.save(user);

        System.out.println("----------------------------");

       user.setEmail("hyhyhyhyhy@anver.com");

       userRepository.save(user);

        System.out.println(">>>1 : " + userRepository.findById(1L).get());

        userRepository.flush();

        System.out.println(">>>2: " + userRepository.findById(2L).get());


    }

 

log를 보면 update query 진행되지는 않았지만 1번 결과에  name과 email이 업데이트 된 것 처럼 보인다.

 

이는 영속성 컨텍스트에서 가지고 있는 값과 DB값에 차이가 발생하는 순간이다!

 

flush()이후에 update query가 실행되고, DB에 반영하게 된다.

 

참고로 2번 출력은  캐시에서 불러왔다 ㅋㅋㅋ select query를 통해 DB에서 불러오지 않았다.

Hibernate: 
    select
        user0_.id as id1_6_0_,
        user0_.created_at as created_2_6_0_,
        user0_.updated_at as updated_3_6_0_,
        user0_.email as email4_6_0_,
        user0_.gender as gender5_6_0_,
        user0_.name as name6_6_0_,
        userhistor1_.user_id as user_id6_7_1_,
        userhistor1_.id as id1_7_1_,
        userhistor1_.id as id1_7_2_,
        userhistor1_.created_at as created_2_7_2_,
        userhistor1_.updated_at as updated_3_7_2_,
        userhistor1_.email as email4_7_2_,
        userhistor1_.name as name5_7_2_,
        userhistor1_.user_id as user_id6_7_2_ 
    from
        user user0_ 
    left outer join
        user_history userhistor1_ 
            on user0_.id=userhistor1_.user_id 
    where
        user0_.id=?
----------------------------
>>>1 : User(super=BaseEntity(createdAt=2021-07-02T21:53:33, updatedAt=2021-07-02T21:53:33), name=hyhyhyhyhy, email=hyhyhyhyhy@anver.com, id=1, testData=null, gender=null)
Hibernate: 
    update
        user 
    set
        created_at=?,
        updated_at=?,
        email=?,
        gender=?,
        name=? 
    where
        id=?

>>>2: User(super=BaseEntity(createdAt=2021-07-02T21:53:33, updatedAt=2021-07-02T21:53:33), name=park, email=park@google.com, id=2, testData=null, gender=null)
2021-07-02 21:53:36.369  INFO 20048 --- [    Test worker] o.s.t.c.transaction.TransactionContext   : Rolled back 

 

2) Transaction 종료 시점

@Transactional을 명시적으로 표시하지 않은 경우에는 line 각각이 transaction이다. 따라서 바로바로 DB에 반영된다.

 

이때는 개발자가 명시하지 않아도 flush가 자동으로 진행된다.

 

만약 method에 @Transactional이 적용되면

transaction이 끝나는 시점에 영속성 context에서 변경된 내용이 있는지 확인

변경된 내용이 있다면 DB에 영속화 진행

 

3) id값이 아닌 jpql 쿼리가 실행될 경우

JPQL : Java Persistence Query Language, table이 아닌 entity객체를 조회하는 query.

쉽게말해 복잡한 조건의 쿼리가 실행되면 auto flush가 실행된다.

 

왜?  아래와 같이 test코드를 수정해보자.

 

(@Transactional)

 @Test
    void cacheFindTest2(){

        User user = userRepository.findById(1L).get();
       user.setName("hyhyhyhyhy");

       userRepository.save(user);

        System.out.println("----------------------------");

       user.setEmail("hyhyhyhyhy@anver.com");

       userRepository.save(user);

        System.out.println(userRepository.findAll());

    }

 

자, 이상태에서 entity manger가 어떻게 동작할지 생각해보자.

 - class에 @Transactional이 있으니 쓰기지연이 발생함

 - flush가 없음, 영속성 컨텍스트의 내용를 의도적으로 DB에 반영시키지는 않음

 - findAll은 select query로 DB에서 모든 데이터를 가져와야함

 - 올바른 data(최신 값)를 가져오기 위해서는 DB에 반영이 필요함.

 - 따라서 select query 이전에 flush 실행 되어야 함.

 - 즉 findAll()로 인한 select 쿼리 전에 영속성 컨텍스트와 DB가 동기화가 이루어져야함.

 

 - 실제 실행 결과를 보면 update이후에 select 쿼리가 진행된다.

 

 

 

4. 동기화 정리!


 영속성 캐시가 플러시가 돼서 DB에 반영되는 것은 

1) flush 명식적으로 호출되는 시점

2) transaction 종료 시점

3) 복잡한 조회조건으로 인한 jpql 쿼리 실행 시.

'JPA > 영속성' 카테고리의 다른 글

Transaction Manager-3 : Isolation  (0) 2021.07.04
Transaction Manager - 2  (0) 2021.07.04
Transaction Manager - 1  (0) 2021.07.04
Entity 생애 주기  (0) 2021.07.04
영속성 컨텍스트  (0) 2021.07.01
Comments