Search

[springboot] JPA - 2. μ˜μ†μ„± 관리

νƒ€μž…
μŠ€ν„°λ””
νƒœκ·Έ
springboot
μƒνƒœ
Published
생성일
2022/08/01 13:26
μ΅œμ’… νŽΈμ§‘ μΌμ‹œ
2023/02/27 03:17
μƒμœ„ ν•­λͺ©
1 more property

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ (Persistence Context) λž€?

μ—”ν‹°ν‹° λ§€λ‹ˆμ €λ₯Ό 톡해 μ ‘κ·Όκ°€λŠ₯ν•œ 논리적인 ν™˜κ²½μœΌλ‘œ, μ—”ν‹°ν‹°λ₯Ό 영ꡬ μ €μž₯ν•˜λŠ” ν™˜κ²½μ΄λ‹€.

μ—”ν‹°ν‹°μ˜ 생λͺ…μ£ΌκΈ°

β€’
λΉ„μ˜μ† (new / transient)
μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ™€ μ „ν˜€ 관계가 μ—†λŠ” μƒˆλ‘œμš΄ μƒνƒœ
β€’
μ˜μ† (managed)
μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ˜ν•΄ κ΄€λ¦¬λ˜λŠ” μƒνƒœ
β€’
μ€€μ˜μ† (detached)
μ˜μ†μ„± μ»¨ν…ŒμŠ€νŠΈμ— μ €μž₯λ˜μ—ˆλ‹€κ°€ λΆ„λ¦¬λœ μƒνƒœ
β€’
μ‚­μ œ (removed)
μ‚­μ œλœ μƒνƒœ

λΉ„μ˜μ†

λ‹¨μˆœ μžλ°” 객체λ₯Ό μƒμ„±ν•œ μƒνƒœλŠ” λΉ„μ˜μ† μƒνƒœμ΄λ‹€.
//객체λ₯Ό μƒμ„±ν•œ μƒνƒœ(λΉ„μ˜μ†) Member member = new Member(); member.setId("member1"); member.setUsername("νšŒμ›1");
Java
볡사

μ˜μ†

객체λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ— μ €μž₯ν•œ μƒνƒœμ΄λ‹€.
//객체λ₯Ό μƒμ„±ν•œ μƒνƒœ(λΉ„μ˜μ†) Member member = new Member(); member.setId("member1"); member.setUsername("νšŒμ›1"); EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); //객체λ₯Ό μ €μž₯ν•œ μƒνƒœ(μ˜μ†) em.persist(member);
Java
볡사
em.persist() λ₯Ό ν•  λ•Œ DB 에 쿼리가 κ°€λŠ” 것이 μ•„λ‹ˆλΌ, 이후에 commit 에 쿼리가 λ‚ μ•„κ°„λ‹€.

μ€€μ˜μ†

객체λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ λΆ„λ¦¬ν•œ μƒνƒœμ΄λ‹€. (객체 μžμ²΄λŠ” μ‘΄μž¬ν•¨)
//νšŒμ› μ—”ν‹°ν‹°λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ 뢄리, μ€€μ˜μ† μƒνƒœ em.detach(member);
Java
볡사

μ‚­μ œ

μ—”ν‹°ν‹° 자체λ₯Ό DBμ—μ„œ μ‚­μ œν•œλ‹€.
//μ‚­μ œ λŒ€μƒ μ—”ν‹°ν‹° 쑰회 Member memberA = em.find(Member.class, "memberA"); em.remove(memberA); //μ—”ν‹°ν‹° μ‚­μ œ
Java
볡사

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 이점

β€’
1μ°¨ μΊμ‹œ
β€’
동일성 보μž₯
β€’
νŠΈλž™μž­μ…˜μ„ μ§€μ›ν•˜λŠ” μ“°κΈ° 지연
β€’
λ³€κ²½ 감지
β€’
지연 λ‘œλ”©

1μ°¨ μΊμ‹œ

@SpringBootApplication public class HelloJpaApplication { public static void main(String[] args) { SpringApplication.run(HelloJpaApplication.class, args); EntityManagerFactory emf = Persistence.createEntityManagerFactory("hello"); EntityManager em = emf.createEntityManager(); // em : DB connection EntityTransaction tx = em.getTransaction(); tx.begin(); // νŠΈλžœμž­μ…˜ μ‹œμž‘ try { // λΉ„μ˜μ† Member member = new Member(); member.setId(2L); member.setName("HelloA"); // μ˜μ†ν™” em.persist(member); // 1μ°¨ μΊμ‹œμ— μ €μž₯ // SELECT Member findMember = em.find(Member.class, 2L); // findMember : JPA λ₯Ό ν†΅ν•΄μ„œ κ°€μ Έμ˜¨ κ°μ²΄λŠ” DB와 μ—°κ²°λΌμžˆλ‹€κ³  보면 됨 // 1μ°¨ μΊμ‹œμ— member κ°€ 있기 λ•Œλ¬Έμ— DB에 쑰회쿼리λ₯Ό μ•ˆ 날리고 μΊμ‹œμ—μ„œ μ‘°νšŒν•΄μ˜΄ System.out.println("findMember.id = " + findMember.getId()); System.out.println("findMember.name = " + findMember.getName()); tx.commit(); // νŠΈλžœμž­μ…˜ 컀밋 } catch (Exception e) { tx.rollback(); // νŠΈλžœμž­μ…˜ λ‘€ } finally { em.close(); } emf.close(); } }
Java
볡사
findMember.id 와 findMember.name 이 찍힐 λ•Œ DB 에 쿼리가 μ•ˆ 날라간 것을 확인할 수 μžˆλ‹€.
즉, μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 1μ°¨ μΊμ‹œμ—μ„œ 값을 κ°€μ Έμ™”λ‹€λŠ” λœ»μ΄λ‹€.
ν•˜μ§€λ§Œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλŠ” 보톡 νŠΈλžœμž­μ…˜ λ‹¨μœ„λ‘œ μƒμ„±λ˜μ—ˆλ‹€κ°€ λ‹€μ‹œ μ΄ˆκΈ°ν™”λ˜κΈ° λ•Œλ¬Έμ— 1μ°¨ μΊμ‹œμ˜ μ„±λŠ₯ 상 큰 이점은 μ—†κΈ΄ν•˜λ‹€.

μ˜μ† μ—”ν‹°ν‹°μ˜ 동일성 보μž₯

같은 νŠΈλžœμž­μ…˜ λ‹¨μœ„(μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ λ‚΄)μ—μ„œμ˜ 같은 μ—”ν‹°ν‹°μ˜ 동일성을 보μž₯ν•΄μ€€λ‹€.
Member a = em.find(Member.class, "member1"); Member b = em.find(Member.class, "member1"); System.out.println(a == b); //동일성 비ꡐ true
Java
볡사

μ“°κΈ° 지연

μ“°κΈ°λ₯Ό μ§€μ—°ν•˜λ‹€κ°€ νŠΈλžœμž­μ…˜ 컀밋 μ‹œμ— DB에 쿼리λ₯Ό λ‚ λ¦°λ‹€.
SQL 을 DB에 날리지 μ•Šκ³  μƒμ„±ν•œ SQL 을 μ“°κΈ° 지연 SQL μ €μž₯μ†Œμ— μ €μž₯ν•΄λ†“λŠ”λ‹€.
κ·Έ ν›„, commit() λ©”μ„œλ“œ μ‹œμ μ— μ“°κΈ° 지연 SQL μ €μž₯μ†Œμ˜ SQL 듀이 flush κ°€ λ˜λ©΄μ„œ μ‹€μ œ DB 에 νŠΈλžœμž­μ…˜μ΄ μ»€λ°‹λœλ‹€. (sql μ‹€ν–‰)
EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); //μ—”ν‹°ν‹° λ§€λ‹ˆμ €λŠ” 데이터 λ³€κ²½μ‹œ νŠΈλžœμž­μ…˜μ„ μ‹œμž‘ν•΄μ•Ό ν•œλ‹€. transaction.begin(); // [νŠΈλžœμž­μ…˜] μ‹œμž‘ em.persist(memberA); em.persist(memberB); //μ—¬κΈ°κΉŒμ§€ INSERT SQL을 λ°μ΄ν„°λ² μ΄μŠ€μ— 보내지 μ•ŠλŠ”λ‹€. //μ»€λ°‹ν•˜λŠ” μˆœκ°„ λ°μ΄ν„°λ² μ΄μŠ€μ— INSERT SQL을 보낸닀. transaction.commit(); // [νŠΈλžœμž­μ…˜] 컀밋
Java
볡사
persistence.xml νŒŒμΌμ—μ„œ jcbc 배치 μ‚¬μ΄μ¦ˆ μ˜΅μ…˜μ„ 톡해 ν•œ λ²ˆμ— flush ν•˜λŠ” sql 개수λ₯Ό μ„€μ •ν•  수 μžˆλ‹€.
<property name="hibernate.jdbc.batch_size" value="10"/>
XML
볡사

λ³€κ²½ 감지(Dirty Checking)

JPA 둜 뢈러온 값을 마치 μžλ°” collection 을 닀루듯이 λ³€κ²½ν•˜κΈ°λ§Œ ν•˜λ©΄, JPA κ°€ 감지λ₯Ό ν•΄μ„œ DB에 쿼리λ₯Ό λ‚ λ €μ€€λ‹€.
EntityManager em = emf.createEntityManager(); EntityTransaction transaction = em.getTransaction(); transaction.begin(); // [νŠΈλžœμž­μ…˜] μ‹œμž‘ // μ˜μ† μ—”ν‹°ν‹° 쑰회 Member memberA = em.find(Member.class, "memberA"); // μ˜μ† μ—”ν‹°ν‹° 데이터 μˆ˜μ • memberA.setUsername("hi"); memberA.setAge(10); //em.update(member) 이런 μ½”λ“œκ°€ μžˆμ–΄μ•Ό ν•˜μ§€ μ•Šμ„κΉŒ? => 없어도 λœλ‹€. transaction.commit(); // [νŠΈλžœμž­μ…˜] 컀밋
Java
볡사
내뢀원리
κΈ°μ‘΄ μ—”ν‹°ν‹°μ˜ 값이 μŠ€λƒ…μƒ·μœΌλ‘œ μ €μž₯λΌμžˆλŠ”λ°, JPA κ°€ μΊμ‹±λœ Entity 와 μŠ€λƒ…μƒ·μ„ λΉ„κ΅ν•˜μ—¬ λ³€κ²½λœ 값에 λŒ€ν•΄ UPDATE SQL 을 생성해쀀닀.

ν”ŒλŸ¬μ‹œ (flush)

ν”ŒλŸ¬μ‹œλž€?

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ³€κ²½λ‚΄μš©μ„ μ‹€μ œ DB 에 λ°˜μ˜ν•˜λŠ” 것(== 쿼리λ₯Ό λ‚ λ¦¬λŠ” 것)

ν”ŒλŸ¬μ‹œ λ°œμƒ μ‹œ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ λ‚΄λΆ€ λ™μž‘

1.
flush() λ°œμƒ
2.
λ³€κ²½λœ JPA 객체에 λŒ€ν•΄, JPAκ°€ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 변경을 감지함
3.
μˆ˜μ •λœ 엔티티에 κ΄€ν•œ SQL(쿼리)을 μ“°κΈ° 지연 SQL μ €μž₯μ†Œμ— 등둝함
4.
μ“°κΈ° 지연 SQL μ €μž₯μ†Œ 의 쿼리λ₯Ό DB 에 λ‚ λ¦Ό

μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό ν”ŒλŸ¬μ‹œ ν•˜λŠ” 방법

1.
em.flush() : 직접 호좜
2.
νŠΈλžœμž­μ…˜ commit() : ν”ŒλŸ¬μ‹œ μžλ™ 호좜
3.
JPQL 쿼리 μ‹€ν–‰ μ‹œ : ν”ŒλŸ¬μ‹œ μžλ™ 호좜

em.flush()

// λΉ„μ˜μ† Member member1 = new Member(32L, "memA"); Member member2 = new Member(33L, "memB"); // μ˜μ†ν™” em.persist(member1); // 1μ°¨ μΊμ‹œμ— μ €μž₯ em.persist(member2); // 1μ°¨ μΊμ‹œμ— μ €μž₯ (but, μΏΌλ¦¬λŠ” μ•ˆ λ‚˜κ°€κ³  SQL μ €μž₯μ†Œμ— 버퍼링 μƒνƒœ) em.flush(); // ν”ŒλŸ¬μ‹œ 직접 호좜 μ‹œμΌœμ„œ 컀밋 전에 DB 에 쿼리 λ‚ λ¦Ό System.out.println("============"); tx.commit(); // νŠΈλžœμž­μ…˜ 컀밋
Java
볡사
Β em.flush() λ₯Ό 톡해 ν”ŒλŸ¬μ‹œλ₯Ό 직접 ν˜ΈμΆœν•΄μ„œ tx.commit() 전에 DB 에 쿼리λ₯Ό λ‚ λ¦°λ‹€.
System.out.println("============") 전에 쿼리가 λ‚˜κ°μ„ 확인할 수 μžˆλ‹€.

JPQL 쿼리 μ‹€ν–‰ μ‹œ ν”ŒλŸ¬μ‹œ μžλ™ν˜ΈμΆœ

ν”„λ‘œκ·Έλž˜λ¨Έμ˜ 편의λ₯Ό μœ„ν•΄ JPQL 쿼리 μ‹€ν–‰ μ‹œ, JPA κ°€ μžλ™μœΌλ‘œ ν”ŒλŸ¬μ‹œλ₯Ό μžλ™ν˜ΈμΆœν•΄μ„œ ν˜„μž¬μ˜ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό DB에 λ°˜μ˜ν•œλ‹€.
단, ν”ŒλŸ¬μ‹œκ°€ λ˜λ”λΌλ„ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 1μ°¨ μΊμ‹œλŠ” κ·ΈλŒ€λ‘œ μœ μ§€λœλ‹€.
1μ°¨ μΊμ‹œλŠ” μ—”ν‹°ν‹° λ§€λ‹ˆμ €κ°€ 리셋될 λ•Œ 같이 λ¦¬μ…‹λœλ‹€.
em.persist(memberA); em.persist(memberB); em.persist(memberC); // 쀑간에 JPQL μ‹€ν–‰ // μžλ™μœΌλ‘œ flush μ‹€ν–‰λΌμ„œ memberA, memberB, memberC 생성쿼리가 DB에 날라간닀. query = em.createQuery("select m from Member m", Member.class); List<Member> members= query.getResultList();
Java
볡사

ν”ŒλŸ¬μ‹œ μ˜΅μ…˜

em.setFlushMode(FlushModeType.COMMIT)
1.
FlushModeType.AUTO (κΈ°λ³Έκ°’)
νŠΈλžœμž­μ…˜ 컀밋 κ³Ό JPQL 쿼리λ₯Ό μ‹€ν–‰ν•  λ•Œ ν”ŒλŸ¬μ‹œκ°€ ν˜ΈμΆœλœλ‹€.
2.
FlushModeType.COMMIT
νŠΈλžœμž­μ…˜ 컀밋 μ‹œμ—λ§Œ ν”ŒλŸ¬μ‹œκ°€ ν˜ΈμΆœλœλ‹€. (JPQL 쿼리 μ‹€ν–‰ μ‹œμ— ν”ŒλŸ¬μ‹œ μžλ™ν˜ΈμΆœ X)
일반적으둜 AUTO λ₯Ό 거의 μ“΄λ‹€.

μ€€μ˜μ† μƒνƒœ

μ˜μ† μƒνƒœλ‘œ λ§Œλ“œλŠ” 방법

1.
em.persist()
2.
em.find() λ“± ν˜ΈμΆœμ‹œμ— 1μ°¨μΊμ‹œμ— μ—”ν‹°ν‹°κ°€ μ—†μ–΄μ„œ DB μ ‘κ·ΌμœΌλ‘œ 1μ°¨μΊμ‹œμ— μ—”ν‹°ν‹°λ₯Ό 등둝할 λ•Œ

μ€€μ˜μ† μƒνƒœλž€?

μ˜μ† μƒνƒœμ˜ μ—”ν‹°ν‹°λ₯Ό μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ—μ„œ 뢄리(detach)ν•˜λŠ” 것
즉, 더 이상 μ˜μ†μ„± μ»¨ν…μŠ€νŠΈμ˜ 관리λ₯Ό 받지 μ•ŠκΈ° λ•Œλ¬Έμ— λ³€κ²½ 감지 λ“±μ˜ μ˜μ†μ„± μ»¨ν…μŠ€νŠΈκ°€ μ œκ³΅ν•˜λŠ” κΈ°λŠ₯을 μ‚¬μš©ν•˜μ§€ λͺ»ν•œλ‹€.

μ€€μ˜μ† μƒνƒœλ‘œ λ§Œλ“œλŠ” 방법

1.
em.detach(entity)
νŠΉμ • μ—”ν‹°ν‹°λ§Œ μ€€μ˜μ† μƒνƒœλ‘œ μ „ν™˜
2.
em.clear()
μ˜μ†μ„± μ»¨ν…μŠ€νŠΈ 자체λ₯Ό μ™„μ „νžˆ μ΄ˆκΈ°ν™”
3.
em.close()
μ˜μ†μ„± μ»¨ν…μŠ€νŠΈλ₯Ό μ’…λ£Œ