728x90

πŸ“Œ JPAμ—μ„œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ™€ 객체 μ‚¬μ΄μ˜ μƒν˜Έμž‘μš©μ„ 효율적으둜 κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ λ§Œλ“€μ–΄μ§„ 핡심적인 κ³΅κ°„μž…λ‹ˆλ‹€.

 

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” Entity 객체의 μƒνƒœλ₯Ό κ΄€λ¦¬ν•˜κ³ , λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ μƒν˜Έμž‘μš©μ„ μ΅œμ ν™”ν•˜μ—¬ μ„±λŠ₯을 ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€.

 

JPAλ₯Ό μ‚¬μš©ν•˜λ©΄μ„œ μ—¬λŸ¬λΆ„μ΄ 자주 μ ‘ν•˜κ²Œ 될 1μ°¨ μΊμ‹œ, λ³€κ²½ 감지, μ“°κΈ° μ§€μ—° κ°μ†ŒλŠ” 이 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ μ€‘μš”ν•œ κΈ°λŠ₯λ“€μž…λ‹ˆλ‹€.

 

이번 ν¬μŠ€νŒ…μ—μ„œλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ΄λŸ¬ν•œ κΈ°λŠ₯듀을 톡해 μ–΄λ–»κ²Œ Entity 객체λ₯Ό 효율적으둜 κ΄€λ¦¬ν•˜κ³ , μ„±λŠ₯을 κ·ΉλŒ€ν™”ν•˜λŠ”μ§€μ— λŒ€ν•΄ μžμ„Ένžˆ μ‚΄νŽ΄λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

 

JPAμ—μ„œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 핡심 κΈ°λŠ₯_1μ°¨ μΊμ‹œ

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” λ‚΄λΆ€μ μœΌλ‘œ μΊμ‹œ μ €μž₯μ†Œλ₯Ό κ°€μ§€κ³  μžˆμŠ΅λ‹ˆλ‹€. μš°λ¦¬κ°€ μ €μž₯ν•˜λŠ” Entity 객체듀이 1μ°¨ μΊμ‹œ μ¦‰, μΊμ‹œ μ €μž₯μ†Œμ— μ €μž₯되게 λ©λ‹ˆλ‹€.



μΊμ‹œ μ €μž₯μ†ŒλŠ” Map 자료ꡬ쑰 ν˜•νƒœλ‘œ λ˜μ–΄μžˆμŠ΅λ‹ˆλ‹€.

  • key
    • @Id둜 λ§€ν•‘ν•œ κΈ°λ³Έ ν‚€ μ¦‰, μ‹λ³„μž 값을 μ €μž₯ν•©λ‹ˆλ‹€.
  • value
    •  ν•΄λ‹Ή Entity 클래슀의 객체λ₯Ό μ €μž₯ν•©λ‹ˆλ‹€.
  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” μΊμ‹œ μ €μž₯μ†Œ Key에 μ €μž₯ν•œ μ‹λ³„μžκ°’μ„ μ‚¬μš©ν•˜μ—¬ Entity 객체λ₯Ό κ΅¬λΆ„ν•˜κ³  κ΄€λ¦¬ν•©λ‹ˆλ‹€.

 

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” 1μ°¨ μΊμ‹œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ—”ν‹°ν‹° 객체λ₯Ό κ΄€λ¦¬ν•©λ‹ˆλ‹€. 이 μΊμ‹œλŠ” λ‹€μŒκ³Ό 같은 κΈ°λŠ₯을 μ œκ³΅ν•©λ‹ˆλ‹€.

Entity 객체의 μ €μž₯

  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μ‘°νšŒν•œ μ—”ν‹°ν‹° 객체λ₯Ό 1μ°¨ μΊμ‹œμ— μ €μž₯ν•©λ‹ˆλ‹€.
  • 이 μΊμ‹œλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ lifecycleλ™μ•ˆ μœ μ§€λ©λ‹ˆλ‹€.

쀑볡 쑰회 λ°©μ§€

  • λ™μΌν•œ 엔티티에 λŒ€ν•œ μ—¬λŸ¬ 번의 쑰회λ₯Ό λ°©μ§€ν•©λ‹ˆλ‹€.
  • 1μ°¨ μΊμ‹œμ— μ €μž₯된 μ—”ν‹°ν‹° κ°μ²΄λŠ” μ€‘λ³΅μœΌλ‘œ μ‘°νšŒλ˜μ§€ μ•ŠμœΌλ©°, λ°μ΄ν„°λ² μ΄μŠ€ 쑰회λ₯Ό μ΅œμ†Œν™”ν•©λ‹ˆλ‹€.

μ„±λŠ₯ ν–₯상

  • 1μ°¨ μΊμ‹œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ™μΌν•œ νŠΈλžœμž­μ…˜ λ‚΄μ—μ„œ λ°μ΄ν„°λ² μ΄μŠ€μ— λŒ€ν•œ μ€‘볡 μš”μ²­μ„ 쀄일 수 있으며, μ„±λŠ₯을 ν–₯μƒμ‹œν‚΅λ‹ˆλ‹€.

μžλ™ 동기화

  • μ—”ν‹°ν‹°μ˜ μƒνƒœκ°€ λ³€κ²½λ˜λ©΄, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” μžλ™μœΌλ‘œ 이λ₯Ό λ°˜μ˜ν•˜λ©°, νŠΈλžœμž­μ…˜ μ»€λ°‹ μ‹œ λ³€κ²½ 사항을 λ°μ΄ν„°λ² μ΄μŠ€μ— μ €μž₯ν•©λ‹ˆλ‹€.

동일성 보μž₯ 

@Test
@DisplayName("객체 동일성 보μž₯")
void test4() {
    EntityTransaction et = em.getTransaction();

    et.begin();
    
    try {
        Memo memo3 = new Memo();
        memo3.setId(2L);
        memo3.setUsername("Robbert");
        memo3.setContents("객체 동일성 보μž₯");
        em.persist(memo3);

        Memo memo1 = em.find(Memo.class, 1);
        Memo memo2 = em.find(Memo.class, 1);
        Memo memo  = em.find(Memo.class, 2);

        System.out.println(memo1 == memo2);
        System.out.println(memo1 == memo);

        et.commit();
    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

μœ„ μ‹€ν–‰κ²°κ³Όλ₯Ό 보면

  • 같은 값을 μ‘°νšŒν•˜λŠ” memo1κ³Ό memo2λŠ” == κ²°κ³Ό trueλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
  • memo1κ³Ό λ‹€λ₯Έ 값을 μ‘°νšŒν•˜λŠ” memoλŠ” == κ²°κ³Ό falseλ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

근데 μ΄μƒν•œμ μ΄ μžˆμŠ΅λ‹ˆλ‹€. μžλ°”λ₯Ό 배울 λ•Œ κ°μ²΄λ₯Ό λ‘κ°œ μƒμ„±ν–ˆμ„ λ•Œ λ‘ 개의 μ£Όμ†ŒλŠ” κ°™μ„ 수 μ—†λ‹€κ³  μ•Œκ³  μžˆμŠ΅λ‹ˆλ‹€.

 

κ·ΈλŸ¬λ‚˜ JPA의 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ κ΄€λ ¨ν•˜μ—¬ find() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ, κ°™μ€ μ‹λ³„μž(PK)λ₯Ό κ°€μ§„ μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•˜λ©΄ λ‘ 객체가 κ°™μ€ μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•˜λŠ” κ²½μš°κ°€ μžˆμŠ΅λ‹ˆλ‹€.

 

이 ν˜„μƒμ€ JPA의 1μ°¨ μΊμ‹œ κΈ°λŠ₯으둜 μ„€λͺ…ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

ꡬ체적으둜, 1μ°¨ μΊμ‹œλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ λ™μΌν•œ μ‹λ³„μžλ₯Ό κ°€μ§„ Entityκ°€ ν•˜λ‚˜λ§Œ μ‘΄μž¬ν•˜λ„λ‘ 보μž₯ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 μ—”ν‹°ν‹°μ˜ 동일성 보μž₯κ³Ό 쀑볡 쑰회 λ°©μ§€ κΈ°λŠ₯이 κ΅¬ν˜„λ©λ‹ˆλ‹€.

 

즉 μ •λ¦¬ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  • μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ λ™μΌν•œ μ‹λ³„μžλ₯Ό κ°€μ§„ μ—”ν‹°ν‹°λŠ” ν•˜λ‚˜μ˜ μΈμŠ€ν„΄μŠ€λ§Œμ„ κ°€μ§€λ©°, λͺ¨λ“  μ‘°νšŒλŠ” 이 λ‹¨μΌ μΈμŠ€ν„΄μŠ€λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€. λ”°λΌμ„œ 1μ°¨ μΊμ‹œλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ μ—”ν‹°ν‹°μ˜ λ™μΌμ„±μ„ 보μž₯ν•©λ‹ˆλ‹€.

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ μΊμ‹œ μ €μž₯μ†Œ ν™œμš©

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ 이 μΊμ‹œ μ €μž₯μ†Œλ₯Ό μ–΄λ–»κ²Œ ν™œμš©ν•˜κ³  μžˆλŠ”μ§€ μ‹€μ œ μ½”λ“œλ₯Ό λ³΄λ©΄μ„œ 확인해 λ³΄κ² μŠ΅λ‹ˆλ‹€.

 

Entityμ €μž₯


@Test
@DisplayName("1μ°¨ μΊμ‹œ : Entity μ €μž₯")
void test1() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = new Memo();
        memo.setId(1L);
        memo.setUsername("Robbie");
        memo.setContents("1μ°¨ μΊμ‹œ Entity μ €μž₯");

        em.persist(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

em.persist(memo); λ©”μ„œλ“œκ°€ ν˜ΈμΆœλ˜λ©΄ memo Entity κ°μ²΄λ₯Ό μΊμ‹œ μ €μž₯μ†Œμ— μ €μž₯ν•©λ‹ˆλ‹€.

 

μΊμ‹œ μ €μž₯μ†Œ μ €μž₯ κ²°κ³Ό

μœ„ "μΊμ‹œ μ €μž₯μ†Œ μ €μž₯ κ²°κ³Ό"λ₯Ό 보면 em ➑️ persistenceContext ➑️ entitiesBykeyλ₯Ό 확인해 보면 key-value ν˜•νƒœλ‘œ 정보가 μ €μž₯λ˜μ–΄ μžˆμŒμ„ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.


Entity쑰회

Entity쑰회 같은 κ²½μš°μ—λŠ” 크게 두가 μ§€ 경우둜 λ‚˜λ‰˜κ²Œ λ©λ‹ˆλ‹€.

 

1. μΊμ‹œ μ €μž₯μ†Œμ— μ‘°νšŒν•˜λŠ” Idκ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ€ 경우

@Test
@DisplayName("Entity 쑰회 : μΊμ‹œ μ €μž₯μ†Œμ— ν•΄λ‹Ήν•˜λŠ” Idκ°€ μ‘΄μž¬ν•˜μ§€ μ•Šμ€ 경우")
void test2() {
    try {

        Memo memo = em.find(Memo.class, 1);
        System.out.println("memo.getId() = " + memo.getId());
        System.out.println("memo.getUsername() = " + memo.getUsername());
        System.out.println("memo.getContents() = " + memo.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

1. μΊμ‹œ μ €μž₯μ†Œ 쑰회

1. 1μ°¨ μΊμ‹œμ—μ„œ μ°ΎλŠ”λ‹€.


2. DB SELECT 쑰회 ν›„ μΊμ‹œ μ €μž₯μ†Œμ— μ €μž₯

2. DBμ—μ„œ μ°ΎλŠ”λ‹€.

 

find() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ μ‘°νšŒλ₯Ό ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

λ§Œμ•½ μœ„ 그림처럼 id : 1인 PKλ₯Ό κ°€μ§€κ³  μ‘°νšŒλ₯Ό ν•˜λŠ”λ° 1μ°¨ μΊμ‹œμ— μ—†μœΌλ©΄ DB에 SELECT쿼리λ₯Ό λ³΄λ‚΄κ²Œ λ©λ‹ˆλ‹€. DBμ—μ„œ μ‘°νšŒλ₯Ό ν•˜κ³  κ·Έ 값을 1μ°¨ μΊμ‹œμ— μ €μž₯을 ν•©λ‹ˆλ‹€.

 

그리고 1μ°¨ μΊμ‹œμ— μ €μž₯λ˜μ–΄ μžˆλŠ” Entity값을 λ°˜ν™˜ν•˜μ—¬ EntityManagerν•œν…Œ λ°˜ν™˜ν•˜μ—¬ 이 값을 μ‚¬μš©ν•˜λ„λ‘ ν•©λ‹ˆλ‹€.

 

 

μœ„ κ²°κ³Όλ₯Ό 보면 find() λ©”μ„œλ“œλ₯Ό 보내고 1μ°¨ μΊμ‹œμ— μ—†κΈ° λ•Œλ¬Έμ— DB에 Select문을 보내고 λ‹€μ‹œ 1μ°¨ μΊμ‹œμ— μ €μž₯ν•˜κ³  이 1μ°¨ μΊμ‹œμ— μ €μž₯λ˜μ–΄ μžˆλŠ” Entity값을 λ°˜ν™˜ν•˜μ—¬ EntityMangerκ°€ μ‚¬μš©ν•œ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.


2. μΊμ‹œ μ €μž₯μ†Œμ— μ‘°νšŒν•˜λŠ” Idκ°€ μ‘΄μž¬ν•˜λŠ” 경우

 

μΊμ‹œ μ €μž₯μ†Œμ— μ‘°νšŒν•˜λŠ” μ‹λ³„μž 값이 1이고, Memo Entityνƒ€μž… μΈ 값이 μžˆλ‹€λ©΄ ν•΄λ‹Ή Entity 객체λ₯Ό λ°”λ‘œ λ°˜ν™˜ν•©λ‹ˆλ‹€.


@Test
@DisplayName("Entity 쑰회 : μΊμ‹œ μ €μž₯μ†Œμ— ν•΄λ‹Ήν•˜λŠ” Idκ°€ μ‘΄μž¬ν•˜λŠ” 경우")
void test3() {
    try {

        Memo memo1 = em.find(Memo.class, 1);
        System.out.println("memo1 쑰회 ν›„ μΊμ‹œ μ €μž₯μ†Œμ— μ €μž₯\n");

        Memo memo2 = em.find(Memo.class, 1);
        System.out.println("memo2.getId() = " + memo2.getId());
        System.out.println("memo2.getUsername() = " + memo2.getUsername());
        System.out.println("memo2.getContents() = " + memo2.getContents());


    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        em.close();
    }

    emf.close();
}

 

 

μœ„ μ‹€ν–‰κ²°κ³Όλ₯Ό 보면 ν•œ 번의 Selectκ°€ 보내진 것을 μ•Œ 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” μœ„ μ½”λ“œμ—μ„œ find() λ©”μ„œλ“œλ‘œ DBμ—μ„œ select둜 쑰회λ₯Ό ν•˜κ³  1μ°¨ μΊμ‹œλ‘œ κ°€μ Έμ˜¨ 데이터λ₯Ό memo2κ°€ λ‹€μ‹œ ν•œλ²ˆ find() ν–ˆκΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€.

 


Entityμ‚­μ œ

1.Entity 쑰회 및 μΊμ‹œ μ €μž₯μ†Œ 확인

 

Entityλ₯Ό μ‚­μ œν•˜κΈ° μœ„ν•΄μ„œλŠ” λ¨Όμ € ν•΄λ‹Ή Entityκ°€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 1μ°¨ μΊμ‹œμ— μžˆλŠ”μ§€ ν™•μΈν•©λ‹ˆλ‹€.

 

λ§Œμ•½ μΊμ‹œ μ €μž₯μ†Œμ— μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄, JPAλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ ν•΄λ‹Ή Entityλ₯Ό μ‘°νšŒν•˜μ—¬ μΊμ‹œ μ €μž₯μ†Œμ— μ €μž₯ν•©λ‹ˆλ‹€.

 

 

em.remove(entity) λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜λ©΄, JPAλŠ” ν•΄λ‹Ή Entityλ₯Ό μ‚­μ œν•  μ€€λΉ„λ₯Ό ν•©λ‹ˆλ‹€.

 

remove() λ©”μ„œλ“œλŠ” μ‚­μ œν•  Entityλ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ λ‚΄μ—μ„œ DELETED μƒνƒœλ‘œ λ³€κ²½ν•˜λ©°, μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€μ˜ λ³€κ²½ 사항은 νŠΈλžœμž­μ…˜μ΄ 컀밋될 λ•Œ μ μš©λ©λ‹ˆλ‹€.

 

νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜λ©΄, JPAλŠ” λ°μ΄ν„°λ² μ΄μŠ€μ— DELETE SQL λͺ…령을 λ°œμ†‘ν•˜μ—¬ ν•΄λ‹Ή Entityλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€.

 


@Test
@DisplayName("Entity μ‚­μ œ")
void test5() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {

        Memo memo = em.find(Memo.class, 2);

        em.remove(memo);

        et.commit();

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

JPAμ—μ„œ Entityλ₯Ό μ‚­μ œν•˜κΈ° μœ„ν•œ 과정은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.

  1. Entity 쑰회
  2. λ¨Όμ €, em.find(EntityClass.class, PK) λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ νŠΉμ • Entityλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, PKκ°€ 2인 Memo 객체λ₯Ό μ‘°νšŒν•œλ‹€κ³  κ°€μ •ν•  λ•Œ, κ²°κ³ΌλŠ” Memo#2κ°€ λ©λ‹ˆλ‹€. 이 μ‹œμ μ—μ„œ 이 EntityλŠ” 관리(MANAGED) μƒνƒœλ‘œ, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ΄€λ¦¬λ˜κ³  μžˆμŒμ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

 

μœ„ 디버깅 κ²°κ³Όλ₯Ό 보면 λ¨Όμ €, em.find(EntityClass.class, PK) λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ νŠΉμ • Entityλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.

 

μœ„ μ½”λ“œλ₯Ό κΈ°μ€€μœΌλ‘œ, PKκ°€ 2인 Memo 객체λ₯Ό μ‘°νšŒν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€. κ²°κ³ΌλŠ” Memo#2κ°€ λ©λ‹ˆλ‹€. 이 μ‹œμ μ—μ„œ 이 EntityλŠ” 관리(MANAGED) μƒνƒœλ‘œ, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ΄€λ¦¬λ˜κ³  μžˆμŒμ„ λ‚˜νƒ€λƒ…λ‹ˆλ‹€.

 

 

Entityλ₯Ό μ‚­μ œν•˜κΈ° μœ„ν•΄ em.remove(entity) λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€. 이 λ©”μ„œλ“œ 호좜 ν›„, Entity의 μƒνƒœλŠ” DELETED둜 λ³€κ²½λ©λ‹ˆλ‹€.

 

DELETED μƒνƒœλŠ” ν•΄λ‹Ή Entityκ°€ μ‚­μ œ λŒ€κΈ° μ€‘μž„μ„ μ˜λ―Έν•©λ‹ˆλ‹€. μ‹€μ œ μ‚­μ œ μž‘μ—…μ€ νŠΈλžœμž­μ…˜μ΄ 컀밋(COMMIT)될 λ•Œ λ°μ΄ν„°λ² μ΄μŠ€μ— DELETE SQL 쿼리가 λ°œμ†‘λ˜λ©΄μ„œ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€.

 

 

μœ„ μ΅œμ’… κ²°κ³Όλ₯Ό 보면 select둜 데이터가 μ‘΄μž¬ν•˜λŠ”μ§€ ν™•μΈν•˜κ³  delete 쿼리가 λ™μž‘ν•œ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 

 

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ  (Write-Behind Storage)λž€β“

 

JPA의 νŠΈλžœμž­μ…˜ 처리 λ°©μ‹μ—μ„œλŠ” SQL λͺ…령을 λͺ¨μ•„μ„œ μΌκ΄„μ μœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€μ— μ μš©ν•˜λŠ” 방식이 핡심적인 역할을 ν•©λ‹ˆλ‹€.

 

μ΄λŸ¬ν•œ μ ‘κ·Ό 방식은 μ„±λŠ₯을 μ΅œμ ν™”ν•˜κ³  λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ μƒν˜Έμž‘μš©μ„ 효율적으둜 κ΄€λ¦¬ν•˜λŠ” 데 μ€‘μš”ν•œλ°, κ·Έ κ³Όμ •μ—μ„œ μ€‘μš”ν•œ 역할을 ν•˜λŠ” κ°œλ…μ΄ λ°”λ‘œ μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ(Write-Behind Cache)μž…λ‹ˆλ‹€.

 

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œβ“λŠ” JPA의 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ λ³€κ²½λœ Entity μƒνƒœλ₯Ό λ©”λͺ¨λ¦¬μ— μž„μ‹œλ‘œ μ €μž₯ν•˜λŠ” ꡬ쑰λ₯Ό μ˜λ―Έν•©λ‹ˆλ‹€.

 

λ°μ΄ν„°λ² μ΄μŠ€μ— 직접적인 λ°˜μ˜μ„ μ§€μ—°μ‹œν‚€κ³ , νŠΈλžœμž­μ…˜μ΄ 컀밋될 λ•Œ λ³€κ²½ 사항을 μΌκ΄„μ μœΌλ‘œ μ²˜λ¦¬ν•˜μ—¬ 효율적인 λ°μ΄ν„°λ² μ΄μŠ€ 관리와 μ„±λŠ₯ ν–₯상을 κ°€μ Έμ˜΅λ‹ˆλ‹€.

 

 

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ  (Write-Behind Storage)의 μ£Όμš” κ°œλ…

λ³€κ²½ μ‚¬ν•­μ˜ μž„μ‹œ μ €μž₯

Entity의 μƒνƒœλ₯Ό λ³€κ²½ν•  λ•Œ, JPAλŠ” κ·Έ λ³€κ²½ 사항을 μ¦‰μ‹œ λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜ν•˜λŠ” λŒ€μ‹ , μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μž„μ‹œλ‘œ μ €μž₯ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 λΉˆλ²ˆν•œ λ°μ΄ν„°λ² μ΄μŠ€ 접근을 쀄이고, λ©”λͺ¨λ¦¬μ—μ„œ λ³€κ²½ 사항을 κ΄€λ¦¬ν•©λ‹ˆλ‹€.

 

νŠΈλžœμž­μ…˜ 컀밋 μ‹œμ μ— 반영

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μ €μž₯된 λ³€κ²½ 사항은 νŠΈλžœμž­μ…˜μ΄ 컀밋될 λ•Œ λ°μ΄ν„°λ² μ΄μŠ€μ— μ‹€μ œλ‘œ λ°˜μ˜λ©λ‹ˆλ‹€.

 

JPAλŠ” 이 μ‹œμ μ— λͺ¨λ“  λ³€κ²½ 사항을 μΌκ΄„μ μœΌλ‘œ μ²˜λ¦¬ν•˜μ—¬, λ°μ΄ν„°λ² μ΄μŠ€μ— μ μ ˆν•œ SQL λͺ…λ Ή(INSERT, UPDATE, DELETE)을 λ°œμ†‘ν•©λ‹ˆλ‹€.

 

μ„±λŠ₯ ν–₯상

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œλ₯Ό μ‚¬μš©ν•˜λ©΄ λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ μƒν˜Έμž‘μš©μ„ μ΅œμ ν™”ν•˜κ³  μ„±λŠ₯을 κ°œμ„ ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ©”λͺ¨λ¦¬μ— μž„μ‹œλ‘œ μ €μž₯된 λ³€κ²½ 사항을 ν•œ λ²ˆμ— μ²˜λ¦¬ν•¨μœΌλ‘œμ¨, λ°μ΄ν„°λ² μ΄μŠ€μ˜ λΆ€ν•˜λ₯Ό 쀄이고 전체적인 μ„±λŠ₯을 ν–₯μƒν•©λ‹ˆλ‹€.

 

 

μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ  (Write-Behind Storage)의 λ™μž‘ 방식

@Test
@DisplayName("μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ 확인")
void test6() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {
        Memo memo = new Memo();
        memo.setId(2L);
        memo.setUsername("Robbert");
        memo.setContents("μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ");
        em.persist(memo);

        Memo memo2 = new Memo();
        memo2.setId(3L);
        memo2.setUsername("Bob");
        memo2.setContents("κ³Όμ—° μ €μž₯을 잘 ν•˜κ³  μžˆμ„κΉŒ?");
        em.persist(memo2);

        System.out.println("νŠΈλžœμž­μ…˜ commit μ „");
        et.commit();
        System.out.println("νŠΈλžœμž­μ…˜ commit ν›„");

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

μœ„ 디버깅 μ‹€ν–‰κ²°κ³Όλ₯Ό 보면 μ“°κΈ° μ§€μ—° μ €μž₯μ†Œ(actionQueue)λ₯Ό 보면 λ°”λ‘œ 데이터 λ² μ΄μŠ€μ— λ°˜μ˜λ˜μ§€ μ•Šκ³  insertions에 λ³€κ²½ 사항이 μ €μž₯이 μ €μž₯된 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.


commit() λ©”μ„œλ“œ μ‹€ν–‰ ν›„(commit ν›„)

 

μ§€μ—° μ €μž₯μ†Œ(actionQueue)에 μž„μ‹œλ‘œ μ €μž₯λ˜μ–΄ 있던 insertions 데이터가 사라진 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 


 

μ‹€μ œ 기둝을 확인해 보면, νŠΈλžœμž­μ…˜ 컀밋 μ „κΉŒμ§€ SQL μš”μ²­μ΄ μ—†λ˜ 것이, 컀밋 ν›„ ν•œ λ²ˆμ— INSERT SQL 두 κ°œκ°€ μˆœμ„œλŒ€λ‘œ λ°μ΄ν„°λ² μ΄μŠ€μ— μš”μ²­λœ 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 

flush() λ©”μ„œλ“œ

flush() λ©”μ„œλ“œλŠ” JPA의 EntityManagerμ—μ„œ μ€‘μš”ν•œ 역할을 ν•˜λŠ” λ©”μ„œλ“œλ‘œ, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½ 사항을 λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜ν•˜λŠ” 과정을 μ œμ–΄ν•©λ‹ˆλ‹€. 이λ₯Ό 톡해 λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ 동기화λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μˆ˜ν–‰ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

즉, μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ˜ SQL듀을 DB에 μš”μ²­ν•˜λŠ” 역할을 μˆ˜ν–‰ν•©λ‹ˆλ‹€.

 

flsuh() λ©”μ„œλ“œκ°€ μ‹€μ œλ‘œ μ–΄λ–»κ²Œ λ™μž‘ν•˜λŠ”μ§€ 직접 ν˜ΈμΆœν•΄ 보면

 

@Test
@DisplayName("flush() λ©”μ„œλ“œ 확인")
void test7() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {
        Memo memo = new Memo();
        memo.setId(4L);
        memo.setUsername("Flush");
        memo.setContents("Flush() λ©”μ„œλ“œ 호좜");
        em.persist(memo);

        System.out.println("flush() μ „");
        em.flush(); // flush() 직접 호좜
        System.out.println("flush() ν›„\n");
        

        System.out.println("νŠΈλžœμž­μ…˜ commit μ „");
        et.commit();
        System.out.println("νŠΈλžœμž­μ…˜ commit ν›„");

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

 

  • μ—”ν‹°ν‹° μƒνƒœμ˜ λ³€κ²½
    • μ½”λ“œμ—μ„œ memo μ—”ν‹°ν‹°λ₯Ό μƒμ„±ν•˜κ³ , 이λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯ν•˜κΈ° μœ„ν•΄ em.persist(memo)λ₯Ό ν˜ΈμΆœν•©λ‹ˆλ‹€. 이 μ—”ν‹°ν‹°λŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μ €μž₯λ©λ‹ˆλ‹€.

 


 

flush() λ©”μ„œλ“œ ν˜ΈμΆœμ„ ν˜ΈμΆœν•˜λ©΄, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯된 λ³€κ²½ 사항이 λ°μ΄ν„°λ² μ΄μŠ€μ— μ¦‰μ‹œ 반영된 것을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

 

이 μ‹œμ μ—μ„œ, JPAλŠ” μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μžˆλŠ” INSERT SQL λͺ…령을 λ°μ΄ν„°λ² μ΄μŠ€μ— μš”μ²­ν•©λ‹ˆλ‹€. 이둜 인해 λ°μ΄ν„°λ² μ΄μŠ€μ˜ μƒνƒœλŠ” memo μ—”ν‹°ν‹°μ˜ λ³€κ²½ 사항을 ν¬ν•¨ν•˜κ²Œ λ©λ‹ˆλ‹€.

 

 

λ³€κ²½ 감지(dirty checking)λž€β“

JPAμ—μ„œ μ—”ν‹°ν‹°μ˜ μƒνƒœκ°€ 변경될 λ•Œλ§ˆλ‹€ UPDATE SQL이 μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μ €μž₯λœλ‹€λ©΄, λΉ„νš¨μœ¨μ μΈ 상황이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

μ—¬λŸ¬ 번의 UPDATE SQL이 각각 λ°μ΄ν„°λ² μ΄μŠ€μ— μš”μ²­λ˜λ©΄, λ°μ΄ν„°λ² μ΄μŠ€μ™€μ˜ 톡신 λΉ„μš©μ΄ μ¦κ°€ν•˜κ³  μ„±λŠ₯에 μ•…μ˜ν–₯을 λ―ΈμΉ  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŸ¬ν•œ 문제λ₯Ό λ°©μ§€ν•˜κΈ° μœ„ν•΄JPAλŠ” λ³€κ²½ 감지 κΈ°λŠ₯(dirty checking)을 μ œκ³΅ν•©λ‹ˆλ‹€.

 

 

λ³€κ²½ 감지(Dirty Checking)λŠ” JPA의 핡심 κΈ°λŠ₯ 쀑 ν•˜λ‚˜λ‘œ, μ—”ν‹°ν‹°μ˜ μƒνƒœ 변경을 μžλ™μœΌλ‘œ μΆ”μ ν•˜κ³  효율적으둜 λ°μ΄ν„°λ² μ΄μŠ€μ— λ°˜μ˜ν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€.

 

λ³€κ²½ 감지(dirty checking)의 λ™μž‘ 방식

 

λ³€κ²½ 감지(Dirty Checking)와 μ—”ν‹°ν‹°μ˜ μƒνƒœλ₯Ό κ΄€λ¦¬ν•˜κΈ° μœ„ν•΄ JPAλŠ” LoadedState와 같은 κ°œλ…μ„ ν™œμš©ν•©λ‹ˆλ‹€.

 

JPAλŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ—”ν‹°ν‹°λ₯Ό μ €μž₯ν•  λ•Œ, 졜초 μƒνƒœ(LoadedState)λ₯Ό κΈ°λ‘ν•©λ‹ˆλ‹€.

 

이후 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λ˜κ±°λ‚˜ em.flush()κ°€ 호좜되면 JPAλŠ” μ—”ν‹°ν‹°μ˜ ν˜„μž¬ μƒνƒœμ™€ 졜초 μƒνƒœλ₯Ό λΉ„κ΅ν•˜μ—¬, λ³€κ²½λœ 뢀뢄을 효율적으둜 μ²˜λ¦¬ν•©λ‹ˆλ‹€. 이 과정을 λ³€κ²½ 감지(Dirty Checking)라고 λΆ€λ¦…λ‹ˆλ‹€.

 

그러면 μ‹€μ œλ‘œ λ™μž‘ν•˜λŠ” 과정은 λ‹€μŒκ³Ό κ°™μŠ΅λ‹ˆλ‹€.


@Test
@DisplayName("λ³€κ²½ 감지 확인")
void test8() {
    EntityTransaction et = em.getTransaction();

    et.begin();

    try {
        System.out.println("λ³€κ²½ν•  데이터λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€.");
        Memo memo = em.find(Memo.class, 4);
        System.out.println("memo.getId() = " + memo.getId());
        System.out.println("memo.getUsername() = " + memo.getUsername());
        System.out.println("memo.getContents() = " + memo.getContents());

        System.out.println("\nμˆ˜μ •μ„ μ§„ν–‰ν•©λ‹ˆλ‹€.");
        memo.setUsername("Update");
        memo.setContents("λ³€κ²½ 감지 확인");

        System.out.println("νŠΈλžœμž­μ…˜ commit μ „");
        et.commit();
        System.out.println("νŠΈλžœμž­μ…˜ commit ν›„");

    } catch (Exception ex) {
        ex.printStackTrace();
        et.rollback();
    } finally {
        em.close();
    }

    emf.close();
}

 

  • μ—”ν‹°ν‹° 쑰회 및 초기 μƒνƒœ 좜λ ₯
    • em.find(Memo.class, 4)λ₯Ό 톡해 μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. μ΄λ•Œ μ—”ν‹°ν‹°λŠ” μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ΄€λ¦¬λ©λ‹ˆλ‹€.
    • 쑰회된 μ—”ν‹°ν‹°μ˜ ν˜„μž¬ μƒνƒœ(memo.getUsername(), memo.getContents())λ₯Ό 좜λ ₯ν•©λ‹ˆλ‹€. 이 μƒνƒœλŠ” LoadedState둜, μ—”ν‹°ν‹°μ˜ 초기 μƒνƒœλ₯Ό λ‚˜νƒ€λƒ…λ‹ˆλ‹€.
  • μ—”ν‹°ν‹° μƒνƒœ λ³€κ²½
    • memo.setUsername("Update")와 memo.setContents("λ³€κ²½ 감지 확인") λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ μ—”ν‹°ν‹°μ˜ μƒνƒœλ₯Ό λ³€κ²½ν•©λ‹ˆλ‹€. 이 μ‹œμ μ—μ„œ μ—”ν‹°ν‹°μ˜ μƒνƒœλŠ” Managed μƒνƒœμ—μ„œ λ³€κ²½λ˜μ—ˆμœΌλ©°, JPAλŠ” 이 λ³€κ²½ 사항을 κ°μ§€ν•©λ‹ˆλ‹€.

 

 

 

  • νŠΈλžœμž­μ…˜ 컀밋
    • et.commit()을 ν˜ΈμΆœν•˜λ©΄, JPAλŠ” ν˜„μž¬ μƒνƒœμ™€ 졜초 μƒνƒœλ₯Ό λΉ„κ΅ν•˜μ—¬ λ³€κ²½λœ λΆ€λΆ„λ§Œμ„ ν¬ν•¨ν•˜λŠ” UPDATE SQL을 μƒμ„±ν•©λ‹ˆλ‹€.
    • 이 UPDATE SQL은 μ“°κΈ° μ§€μ—° μ €μž₯μ†Œμ— μ €μž₯된 ν›„, λ°μ΄ν„°λ² μ΄μŠ€μ— μ „μ†‘λ˜λ©΄μ„œ μ‹€μ œλ‘œ μƒνƒœκ°€ μ—…λ°μ΄νŠΈλ©λ‹ˆλ‹€.
  • κ²°κ³Ό 확인
    • νŠΈλžœμž­μ…˜ 컀밋 ν›„ UPDATE SQL이 λ°μ΄ν„°λ² μ΄μŠ€μ— μš”μ²­λœ 것을 확인할 수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” JPAκ°€ μžλ™μœΌλ‘œ λ³€κ²½ 사항을 κ°μ§€ν•˜κ³  μ²˜λ¦¬ν–ˆμŒμ„ μ˜λ―Έν•©λ‹ˆλ‹€.