Dev/JPA

์˜์†์„ฑ ์ปจํ…์ŠคํŠธ(Entity Cache)

OK-๊ฐ€์ž 2022. 3. 2. 17:28

๐Ÿค” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” ์ผ์ข…์˜ JPA ์ปจํ…Œ์ด๋„ˆ ์•ˆ์—์„œ ๋™์ž‘ํ•˜๋Š” entity์˜ ๋งฅ๋ฝ์„ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด๋‹ค. ์ด ์•ˆ์—์„œ entity๋Š” ์ƒ์„ฑ๋˜๊ณ  ์ง€์›Œ์ง€๊ณ  ์กฐํšŒ๋œ๋‹ค. ๊ทธ context ์•ˆ์—์„œ ๊ฐ€์žฅ ์ค‘์š”ํ•œ ์—ญํ™œ์„ ํ•˜๋Š” ๊ฒƒ์ด EntityManager ๊ฐ์ฒด์ด๋‹ค. ์ด ์—ญํ™œ์„ ์•Œ์•„๋ณด๊ณ , EntityManager๊ฐ€ entity๋ฅผ ์ฒ˜๋ฆฌํ•˜๋Š” ๊ณผ์ •์—์„œ Cache๋ฅผ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ•˜๋Š”์ง€ ์•Œ์•„๋ณด์ž.

EntityManager

  • EntityManager๋Š” JPA์—์„œ ์ •์˜ํ•˜๊ณ  ์žˆ๋Š” Interface ์ด๋‹ค. persist merge remove find ๋“ฑ๋“ฑ ์ •์˜๋˜์–ด์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ตฌํ˜„์ฒด๋ฅผ ๋นˆ์œผ๋กœ ๋“ฑ๋กํ•˜๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— Autowire๋ฅผ ์ด์šฉํ•ด ์‚ฌ์šฉํ• ์ˆ˜ ์žˆ๋‹ค.
  • ์ฟผ๋ฆฌ๋ฉ”์„œ๋“œ๋‚˜, simplejparepository ๋“ฑ ๋‚ด๋ถ€์ ์ธ ์‹ค์ œ ๋™์ž‘์€ entityManager๋ฅผ ํ†ตํ•˜์—ฌ ์‹คํ–‰๋˜๊ธฐ ๋•Œ๋ฌธ์— SpringDataJpa์—์„œ ์ œ๊ณตํ•˜์ง€ ์•Š๋Š” ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๊ฑฐ๋‚˜, ์„ฑ๋Šฅ๋ฌธ์ œ๊ฐ€ ์žˆ์–ด ๋ณ„๋„๋กœ ์ปค์Šคํ„ฐ ๋งˆ์ด์ง•์„ ํ•ด์•ผํ•˜๋ฉด EntityManager๋ฅผ ๋ฐ›์•„์„œ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
@SpringBootTest
public class EntityManagerTest {

    @Autowired
    private EntityManager entityManager;

    @Test
    void entityManagerTest() {
        System.out.println(entityManager.createQuery("select u from User u").getResultList()); // entityManager์—์„œ ์ง์ ‘ ์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค์„œ ์‚ฌ์šฉ
        // userRepository.findAll(); ์ด๊ฑฐ์™€ ๊ฐ™๋‹ค.
    }
  • ์œ„์ฒ˜๋Ÿผ ์ปค์Šคํ…€์ฟผ๋ฆฌ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์ง€๋งŒ, ์ด๋ฒˆ ๊ฐ•์˜์—์„œ๋Š” EntityManager์˜ Cache๋ฅผ ๊ณต๋ถ€ํ• ๊บผ๋‹ˆ๊นŒ ๋„˜์–ด๊ฐ€์ž.

Entity ์บ์‹œ ์•Œ์•„๋ณด์ž

  • EntityManager์—์„œ ์‚ฌ์šฉํ•˜๋Š” Entity์บ์‹œ๋Š” ๋ญ˜๊นŒ? ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ ๋‚ด์—์„œ ์—”ํ‹ฐํ‹ฐ๋“ค์„ ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋Š” EntityManager์—์„œ๋Š” Cache๋ฅผ ๊ฐ€์ง€๊ณ ์žˆ๋Š”์ œ ์‹ค์ œ๋กœ ์šฐ๋ฆฌ๊ฐ€ save๋ฉ”์„œ๋“œ๋ฅผ ์‹คํ–‰์‹œํ‚ค๋Š” ์‹œ์ ์— DB์— ๋ฐ˜์˜๋˜๋Š”๊ฒŒ ์•„๋‹ˆ๋‹ค. ์ฆ‰, ์šฐ๋ฆฌ๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋กœ ์‹ค์ œ DB์‚ฌ์ด์—์„œ Data ๊ฐญ์ด ์ผ์–ด๋‚œ๋‹ค๋Š” ๋œป์ด๋‹ค.
    @Test
    void cacheFindTest() {
        System.out.println(userRepository.findByEmail("martin@fast.com"));
        System.out.println(userRepository.findByEmail("martin@fast.com"));
        System.out.println(userRepository.findByEmail("martin@fast.com"));
        }
  • ์œ„ ์ฝ”๋“œ๋ฅผ Test ํ•˜์ž
      select
          user0_.id as id1_7_,
          user0_.created_at as created_2_7_,
          user0_.updated_at as updated_3_7_,
          user0_.email as email4_7_,
          user0_.gender as gender5_7_,
          user0_.name as name6_7_ 
      from
          user user0_ 
      where
          user0_.email=?
  • ์œ„ ์ฟผ๋ฆฌ๋ฅผ 3๋ฒˆ ๋œ๋‹ค. ์ด๋ฒˆ์—๋Š” Transactional์–ด๋…ธํ…Œ์ด์…˜์„ ๊ฑธ๊ณ  findById๋ฅผ ์‹คํ–‰ ํ•ด ๋ณด์ž.
@SpringBootTest
@Transactional
public class EntityManagerTest {
    @Test
    void cacheFindTest() {
        System.out.println(userRepository.findById(2L).get());
        System.out.println(userRepository.findById(2L).get());
        System.out.println(userRepository.findById(2L).get());
   }
    select
        ...
        ...
    where
        user0_.id=?
User(super=BaseEntity(createdAt=2022-02-11T16:44, updatedAt=2022-02-11T16:44), id=2, name=demis, email=demis@fast.com, gender=null)
User(super=BaseEntity(createdAt=2022-02-11T16:44, updatedAt=2022-02-11T16:44), id=2, name=demis, email=demis@fast.com, gender=null)
User(super=BaseEntity(createdAt=2022-02-11T16:44, updatedAt=2022-02-11T16:44), id=2, name=demis, email=demis@fast.com, gender=null)
  • ์œ„์™€๊ฐ™์ด select๋Š” ํ•œ๋ฒˆ ์‹คํ–‰๋ฌ๋Š”๋ฐ ๊ฒฐ๊ณผ๋Š” 3๊ฐœ ๋‚˜์˜ค๋Š”๊ฑธ ํ™•์ธํ• ์ˆ˜ ์žˆ๋‹ค. ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ์กด์žฌํ•˜๋Š” ์บ์‹œ๊ฐ€ ์ง์ ‘ ์ฒ˜๋ฆฌํ•œ ๊ฒƒ์ด๋‹ค. ์ง„์งœ DB์ฟผ๋ฆฌ๋ฅผ ์กฐํšŒํ•˜์ง€ ์•Š๊ณ  ๋ง์ด๋‹ค.
  • ์šฐ๋ฆฌ๊ฐ€ ๋”ฐ๋กœ ์บ์‹œ ์„ค์ •์„ ํ•˜์ง€ ์•Š์•˜์ง€๋งŒ, ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋‚ด์—์„œ ์ž๋™์œผ๋กœ ์—”ํ‹ฐํ‹ฐ์— ๋Œ€ํ•œ ์บ์‰ฌ์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒƒ์ด JPA์˜ 1์ฐจ Cache์ฒ˜๋ฆฌ๋ผ๊ณ  ํ•œ๋‹ค.
  • ๊ทธ๋ ‡๋‹ค๋ฉด findById๊ณผ findByEmail์—์„œ 1์ฐจ Cache๊ฐ€ ์ ์šฉ๋˜๊ณ , ๋˜์ง€ ์•Š๋Š”๊ฒƒ์˜ ์ฐจ์ด๊ฐ€ ๋ญ˜๊นŒ?
    • 1์ฐจ ์บ์‹œ๋Š” Map ํ˜•ํƒœ๋กœ ๋งŒ๋“ค์–ด ์ง€๊ณ  Key๋Š” Id๊ฐ’ value๋Š” ํ•ด๋‹น ์—”ํ‹ฐํ‹ฐ๊ฐ€ ๋“ค์–ด์žˆ๋‹ค.
    • Id๋กœ ์กฐํšŒํ•˜๋ฉด ๋จผ์ € ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋‚ด์— ์กด์žฌํ•˜๋Š” 1์ฐจ ์บ์‹œ์— ์กฐํšŒํ•ด ๋ณด๊ณ  ์žˆ์œผ๋ฉด DB์กฐํšŒ์—†์ด ๋ฐ”๋กœ ๊ฐ’์„ ๋ฆฌํ„ฐํ•ด ์ค€๋‹ค.
    • ๊ฐ’์ด ์—†์œผ๋ฉด ์‹ค์ œ ์ฟผ๋ฆฌ๋กœ DB์กฐํšŒํ•ด์„œ 1์ฐจ์บ์‹œ์— ์ €์žฅํ•˜๊ณ  ๋ฆฌํ„ดํ•ด์ค€๋‹ค.
  • 1์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•จ์— ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ์ ์ธ JPA ์กฐํšŒ ์„ฑ๋Šฅ์ด ์˜ฌ๋ผ๊ฐ„๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ์ง์ ‘ ID๊ฐ’์„ ์‚ฌ์šฉํ•ด์„œ ์กฐํšŒํ•˜๋Š” ๊ฒฝ์šฐ๋Š” ๋“œ๋ฌผ๋‹ค. findById(2L) ๊ฐ™์€๊ฑฐ ๋ง์ด๋‹ค.
  • JPA ํŠน์„ฑ์ƒ ID๊ฐ’์„ ์ด์šฉํ•œ ์กฐํšŒ๊ฐ€ ๋นˆ๋ฒˆํ•˜๊ฒŒ ์ผ์–ด๋‚œ๋‹ค. update๋‚˜ delete๊ฐ™์€๊ฑฐ ๋ง์ด๋‹ค. ๊ทธ๋ž˜์„œ ํ•ด๋‹น ๋กœ์ง์˜ ์„ฑ๋Šฅ์ €ํ•˜๊ฐ€ ์ผ์–ด๋‚œ๋‹ค. ์ด๋•Œ 1์ฐจ ์บ์‹œ๋ฅผ ํ™œ์šฉํ•ด์„œ ์„ฑ๋Šฅ์ €ํ•˜๋ฅผ ์ค„์ด๊ธฐ ์œ„ํ•œ ๋Œ€์ฑ…์ด ๋œ๋‹ค.
    @Test
    void cacheFindTest() {
        userRepository.deleteById(1L);
    }
Hibernate: 
    select
        ...
        ...
    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=?
  • ์œ„ ์ฒ˜๋Ÿผ JPA๋‚ด๋ถ€์ ์œผ๋กœ ID์— ๋Œ€ํ•œ ์กฐํšŒ๊ฐ€ ์ผ์–ด๋‚ ๋•Œ๊ฐ€ ๋งŽ์•„์„œ ํ•˜๋‚˜์˜ ํŠธ๋žœ์ ์…˜์„ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ 1์ฐจ ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ์จ ์„ฑ๋Šฅ์ €ํ•˜๋ฅผ ๋ฐฉ์ง€ํ•œ๋‹ค.
@Transactional
public class EntityManagerTest {

    @Test
    void cacheFindTest2() {
        User user = userRepository.findById(1L).get();
        user.setName("marrrrrrrtin");
        userRepository.save(user);

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

        user.setEmail("marrrrrrtin@fast.com");
        userRepository.save(user);

    }
  • ์œ„ ์ฒ˜๋Ÿผ update๋ฅผ 2๋ฒˆ save๋ฅผ 2๋ฒˆ ํ•˜์—ฌ๋„ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ ์บ์‹œ๊ฐ€ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค๊ฐ€ merge๋ฅผ ํ•ด์„œ ํ•œ๋ฒˆ์— ์ ์šฉํ•œ๋‹ค. ๋งŒ์•ฝ @Transactional์ด ๊ฑธ๋ ค์žˆ์œผ๋ฉด ์ตœ์ข… merge๋ฅผ ํ•œ Data์™€ DB๋ž‘ ์ฐจ์ด๊ฐ€ ์—†์–ด์„œ ์ฟผ๋ฆฌ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์„๊ฒƒ์ด๋‹ค.(์ด๊ฒŒ ๋งž๋Š”์ง€ ๋ชจ๋ฅด๊ฒ ๋„ค....)
  • ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์˜ cache๋ฅผ ์ž˜ ์ดํ•ดํ•˜๊ฒŒ ๋˜๋ฉด ์ด์ „์— ๋ฐฐ์šด flush์˜ ์—ญํ™œ๋„ ์ดํ•ดํ• ์ˆ˜ ์žˆ๋‹ค. flush๋Š” ๋ชจ์—ฌ์žˆ๋Š”๊ฑธ ๋น„์›Œ๋‚ธ๋‹ค๋Š” ๋œป์ด๋‹ค. ์ฆ‰, cache์— ์žˆ๋Š” ์ฟผ๋ฆฌ๋ฅผ DB์— ๊ฐ•์ œ๋กœ ์ ์šฉ ์‹œํ‚ค๊ฒ ๋‹ค๋Š” ๊ฑฐ๋‹ค.
  • ๊ทธ๋ ‡๋‹คํ•˜๋”๋ผ๊ณ  flush๋ฅผ ๋‚จ๋ฐœํ•˜๋ฉด ์•ˆ๋œ๋‹ค. cache์˜ ์—ญํ™œ์„ ๋ฌดํšจํ™” ์‹œํ‚ค๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ €ํ•˜๊ฐ€ ์ผ์–ด๋‚ ์ˆ˜ ์žˆ๋‹ค.
    • ์œ„ ์ฟผ๋ฆฌ์˜ save ๋ฐ‘์— "userRepository.flush();"๋ฅผ ๊ฐ๊ฐ ์ž…๋ ฅํ•˜๋ฉด insert๋‚˜ updateํ• ๋•Œ history์— ์ €์žฅํ•˜๋Š” ์ฟผ๋ฆฌ๋Š” 2๋ฒˆ ํ˜ธ์ถœํ•˜๊ฒŒ ๋˜๋Š”๊ฒƒ ์ฒ˜๋Ÿผ ๋ง์ด๋‹ค.
  • cache์ง„์งœ ์–ด๋ ต๋‹ค. ๋‚ด๊ฐ€ saveํ• ๋ ค๊ณ  saveํ–ˆ๋Š”๋ฐ save๋ฅผ ์•ˆํ•ด... ๊ทธ๋Ÿผ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์™€ DB๊ฐ€ ๋™๊ธฐํ™” ๋˜๋Š” ์‹œ์ ์€ ์–ธ์ œ์ผ๊นŒ?
    • ์ฒซ ๋ฒˆ์งธ๋กœ๋Š” flush()๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋ ๋•Œ. ๋™๊ธฐํ™” ๋œ๋‹ค.
    • ๋‘ ๋ฒˆ์งธ๋กœ๋Š” ํŠธ๋žœ์ ์…˜์ด ๋๋‚˜์„œ commit ๋ ๋•Œ์ด๋‹ค.
    • ๋งˆ์ง€๋ง‰์œผ๋กœ id๊ฐ’์ด ์•„๋‹Œ JPQL ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋ ๋•Œ AutoFlush๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋ณต์žกํ•œ ์ฟผ๋ฆฌ๊ฐ€ ์ผ์–ด๋‚˜๋ฉด Autoflush๊ฐ€ ๋œ๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด๋œ๋‹ค.