[Spring] JPA ์์์ฑ ์ปจํ ์คํธ ๊ตฌ์กฐ๋ก ๋ณด๋ ์ด์
์ง๋ ํฌ์คํธ์์ JPA์ ์์์ฑ ์ปจํ ์คํธ์ ์๋ช ์ฃผ๊ธฐ์ ๋ํด ์์๋ดค์ต๋๋ค. ๊ฐ๋จํ๊ฒ ์์์ฑ ์ปจํ ์คํธ์ ์๋ช ์ฃผ๊ธฐ์ ๊ด๊ณ๋ฅผ ํตํด์ Java๋ก ๊ตฌํ๋ ๊ฐ์ฒด๊ฐ ์ด๋ป๊ฒ DB๋ก ์ ์ฌ๋๊ณ ์ญ์ ๋๋์ง๋ฅผ ์ ์ ์์์ต๋๋ค.
๊ทธ๋ฐ๋ฐ, ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ ์ฌ์ฉํ๋ ๊ฒ์ผ๊น์?
์ฒ์์ ์ ๋ ์ด๋ ๊ฒ ์๊ฐํ์ต๋๋ค. JDBC๋ฅผ ์ฌ์ฉํ๋ ค๋ฉด ์ด์ฐจํผ Connection์ด ์์ฑ๋์ด์ผ ํ๊ณ , ๊ทธ์ ๋ฐ๋ฅธ Statement๊ฐ ๋ง๋ค์ด์ ธ์ผ ํ๋๋ฐ, ์๋ฒ ์ ํ๋ฆฌ์ผ์ด์ ํน์ฑ์ ๋ค์ํ ์ฌ์ฉ์์ ์์ฒญ์ ํ ๋ฒ์ ์ฒ๋ฆฌํด์ผ ํ๋ฏ๋ก, Connection์ด ์ฌ๋ฌ๊ฐ ์๊ธฐ๊ฒ ๋ฉ๋๋ค. ์ด ๋๋ฌธ์ Connection Pool์ด๋ผ๋ ๊ฒ์ด ์๊ณ , ๋ํ์ ์ผ๋ก HikariCP๊ฐ ์ฌ์ฉ๋๊ณ ์์ฃ . ๊ทธ ๋ค์์๋ ์ฟผ๋ฆฌ๋ฌธ์ ์์ฑํด์ผ ํ๋๋ฐ, ์ด ๋ถ๋ถ๊ณผ ํจ๊ป ์ปค๋ฐ๊น์ง ํด์ฃผ๋ ๋ ์์ด ์์์ฑ ์ปจํ ์คํธ๋ผ๊ณ ์๊ฐํ์์ต๋๋ค.
๋์ถฉ ๊ทธ๋ด์ธํ์ง๋ง ์ง์ง ๋ชฉ์ ์ ๋ฐ๋ก ์์์ต๋๋ค..
์์์ฑ ์ปจํ ์คํธ์ ์ด์ 1: Cache
๋จผ์ ์์์ฑ ์ปจํ ์คํธ๋ Entity๋ฅผ ์๊ตฌ์ ์ผ๋ก ์ ์ฅํ๋ ํ๊ฒฝ์ด๋ผ๊ณ ํ์์ต๋๋ค. ์ฆ, DB์ ์ ์ฅํ๋ ๊ฒ์ด ์๋ EntityManager ๋ฅผ ์ด์ฉํด์ Entity๋ฅผ ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅํ๋ ๊ฒ์ ๋๋ค.
์ด๋ฌํ ์์์ฑ ์ปจํ ์คํธ์๋ 1์ฐจ ์บ์๋ผ๋ ๊ฒ์ด ์์ต๋๋ค. Entity๊ฐ DB์ ์ ์ฅ๋๊ธฐ ์ ์ ์ฌ์ฉ๋๋ ๊ณต๊ฐ์ธ๋ฐ, ๋ฐ๋๋ก DB๋ฅผ ์กฐํํ๊ณ ๋ ๋์๋ ์ ์ฅํ๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ผ์ ํ ๋ฒ ๋ ๊ฐ์ Entity๋ฅผ ์ฝ๊ณ ์ ํ ๋ ๋น ๋ฅธ ์ฝ๊ธฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๊ณ ๋ถํ๋ฅผ ์ค์ฌ์ค๋๋ค.
Cafe americano = new Cafe();
cafe.setName("Americano");
cafe.setPrice(3000);
Cafe espresso = new Cafe();
cafe.setName("Espresso");
cafe.setPrice(3000);
Cafe cafuchino = new Cafe();
cafe.setName("Cafuchino");
cafe.setPrice(4000);
// ์บ์์ ์ ์ฅ๋จ
em.persist(americano);
em.persist(espresso);
em.persist(cafuchino);
// ์บ์์์ ์กฐํ
em.find(Cafe.class, 1);
์์์ฑ ์ปจํ ์คํธ๋ 2์ฐจ ์บ์๊น์ง ์กด์ฌํ์ง๋ง ๋ ๋ฆฝ์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ฒ์ 1์ฐจ ์บ์์ด๋ฉฐ 2์ฐจ ์บ์์ ๊ฒฝ์ฐ, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ฒด Entity์ ์ฌ์ฉํ๋ ์บ์์ ๋๋ค. ๋ฐ๋ผ์ ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ข ๋ฃ๋ ๋๊น์ง 2์ฐจ ์บ์๋ ์กด์ฌํ์ง๋ง Entity์ Transaction Thread๊ฐ ์ข ๋ฃ๋๋ ๊ฒฝ์ฐ, 1์ฐจ ์บ์๋ ์ญ์ ๋ฉ๋๋ค. ์ฆ, ํธ๋์ญ์ ๋ฒ์ ์์์๋ง ์ฌ์ฉํ๋ ์งง์ ์บ์์ ๋๋ค.
DB์์ ์กฐํํ ๋
- 1์ฐจ ์บ์๋ฅผ ๋จผ์ ์กฐํํ ํ, ์์ผ๋ฉด 1์ฐจ ์บ์์ ๋ด์ฉ์ ๋ถ๋ฌ์จ๋ค.
- 1์ฐจ ์บ์์์ Entity๋ฅผ ์ฐพ์ง ๋ชปํ์ ๊ฒฝ์ฐ, 2์ฐจ ์บ์์์ ์กฐํํ๋ฉฐ ์ฌ๊ธฐ์๋ ์๋ค๋ฉด, DB์์ ์กฐํํ๋ค.
- DB์์ ์กฐํํ Entity๋ฅผ 1์ฐจ ์บ์์ 2์ฐจ ์บ์์ ์ ์ฅํ๋ค.
- ์กฐํํ Entity๋ฅผ ๋ฐํํ๋ค.
DB์์ ์กฐํํ์ ๋๋ง์ผ๋ก๋ 1์ฐจ ์บ์์ ์ ์ฅ๋๋ ๊ฒ์ ์ ์ ์์ผ๋ฉฐ ์ฌ์ฉ์๊ฐ ๋ค์๊ธ ์ด ์์ฒญ์ ์งํํ์ ๋ ์บ์์์ ๋น ๋ฅด๊ฒ ๋ถ๋ฌ์ฌ ์ ์๋๋ก ํ๋ ๊ฒ์ด ์ฒซ ๋ฒ์งธ ์ด์ ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
DB์ ์ ์ฅํ ๋
- ๊ฐ์ฒด๋ก ๋ง๋ Entity๋ฅผ ์์ ์ํ๋ก ์ ํํ๋ฉด 1์ฐจ ์บ์์ 2์ฐจ ์บ์์ ์ ์ฅ๋๋ค. (DB์ ์์)
- 1์ฐจ ์บ์์ ์ ์ฅ๋ Entity๋ ํธ๋์ญ์ ์ปค๋ฐ ๋ฉ์๋๋ฅผ ํตํด DB๋ก ์ ์ฅ๋๋ค.
- ์์ ์ด ๋๋ฌ์ผ๋ฉด ์ค์์ ์ํ๋ก ์ ํํ๋ค.
๋ฒ๊ฑฐ๋ก์ด ๋ฉด์ ์์ง๋ง 1์ฐจ ์บ์์ ์ ์ฅ๋จ์ผ๋ก์จ ์ค๊ฐ์ ์์ ํ ์ฌํญ์ด ์๋ค๋ฉด UPDATE ์ฟผ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๊ณ ๋ INSERT ์ฟผ๋ฆฌ๋ง์ผ๋ก ๋ฐ๋ก ์ ์ฅํ ์ ์๋ค๋ ์ ๋ ๋๋ฆ์ ์ด์ ์ด๋ผ๊ณ ํ ์ ์์ต๋๋ค.
์ด๋ฐ์์ผ๋ก 1์ฐจ ์บ์๋ Entity๋ง๋ค ๋ ๋ฆฝ๋ ํํ๋ก ์ฌ์ฉํ๋ฉฐ ๋ง์ฝ, 1,000๋ช ์ ๋์ ์ฌ์ฉ์๊ฐ DB ์์ฒญ์ด ์ค๋ฉด EntityManager๊ฐ 100๊ฐ ์์ฑ๋๋ฉฐ Thread๊ฐ ์ข ๋ฃ๋ ๋ค์๋ ๋ชจ๋ ์๋ฉธ๋ฉ๋๋ค.
๋ฐ๋๋ก 2์ฐจ ์บ์๋ Application ์ ์ฒด์์ ์ฌ์ฉํ๋ ์บ์๋ก ์ ํ๋ฆฌ์ผ์ด์ ์ด ์ข ๋ฃ๋ ๋๊น์ง ๊ณ์ ์ ์ง๋๋ ์บ์์ ๋๋ค. JPA 2.0์์๋ถํฐ ํ์ค์ด ๋์์ผ๋ฉฐ Hibernate์ 2์ฐจ ์บ์์ ๋์ผํ๊ฒ ๋์ํ๋๋ฐ, ๊ตฌ์ฒด์ ์ธ ๋ด์ฉ์ Hibernate 2์ฐจ ์บ์๋ฅผ ๋ค๋ฃจ๋ ๋ฐฉ๋ฒ์ผ๋ก ์ฐจํ์ ํฌ์คํธํด๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์์์ฑ ์ปจํ ์คํธ์ ์ด์ 2: ๋์ผ์ฑ ๋ณด์ฅ
์บ์์์ ํ ๋ฒ ์ค๋ช ์ ๋๋ ธ์ง๋ง, Entity๋ฅผ DB์ ์ ์ฅํ ๋, ์บ์์ ํ ๋ฒ ๋ณด๊ด์ ํ๋ค๊ณ ํ์์ต๋๋ค. ์ด ๋ง์ ์บ์์ ์๋ ๊ฒ๊ณผ ์ค์ DB์ ์กด์ฌํ๋ ๊ฒ์ด ๊ฐ์ผ๋ฉด์ ๋ ํผ๋ฐ์ค ๊ด๊ณ๊ฐ ๋๋ค๋ ๊ฒ์ธ๋ฐ์. ์ฆ, ์บ์์ ๋ณด๊ดํ๊ณ ์๋ ๋ฐ์ดํฐ์ DB์ ์๋ ๋ฐ์ดํฐ๊ฐ 100% ๋์ผํ๋ค๋ ๊ฒ์ ๋ณด์ฅ๋ฐ์ ์ ์๊ฒ ๋ฉ๋๋ค.
ํธ๋์ญ์ ๋ด๋ถ์์ persist ๋ฉ์๋๊ฐ ํธ์ถ๋๋ฉด, Entity๋ค์ 1์ฐจ ์บ์์ ์์ฑํ๊ณ , ๋ ผ๋ฆฌ์ ์ผ๋ก ๊ตฌํ๋์ด ์๋ Write-behind SQL ์ ์ฅ์์ INSERT ์ฟผ๋ฆฌ ๋ฑ์ DML ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ์ฌ ์์๋์ต๋๋ค.
์ต์ข ์ ์ผ๋ก commit() ๋ฉ์๋๊ฐ ํธ์ถ๋ ๋ ์ ์ ์ฅ์์ ์๋ ๋ชจ๋ ์ฟผ๋ฆฌ๊ฐ DB๋ก ์ ๋ฌ๋๋ฉฐ ์ด ๋๋ถํฐ DB์ ๋ด์ฉ์ด ๋ฐ์๋๋ ๊ฒ์ ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์ฌ๊ธฐ์์ ํจ์ ์ commit() ๋ฉ์๋ ๋ด์๋ flush() ๋ฉ์๋๋ฅผ ๊ฐ์ด ํธ์ถํฉ๋๋ค. flush() ๋ฉ์๋๋ Write-behind SQL ์ ์ฅ์์ ์๋ ๋ชจ๋ ์ฟผ๋ฆฌ๋ฅผ DB์ ์ ์กํ๋ ๋ฉ์๋์ด๊ณ , ๊ทธ ๋ด์ฉ์ ๋ฐ์ํ๋ ๊ฒ์ด commit() ๋ฉ์๋์ธ๋ฐ, ์ค์ ํธ๋์ญ์ ๋ด์ commit์ ์ด 2๊ฐ์ง ๊ธฐ๋ฅ์ ๋ชจ๋ ์ํํ๋ค๋ ์ ์ ์ธ์งํ์ ์ผ ํฉ๋๋ค.
EntityManager em = emf.createEntityManager();
EntityTransaction transaction = em.getTransaction();
// ์ํฐํฐ ๋งค๋์ ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์
์ ์์ํด์ผ ํ๋ค.
transaction.begin(); // ํธ๋์ญ์
์์
em.persist(americano);
// ์ปค๋ฐํ๋ ์๊ฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ INSERT SQL์ ๋ณด๋ธ๋ค.
// ์ด ๋ flush()์ commit()์ด ๊ฐ์ด ์คํ๋จ
transaction.commit();
๊ทธ๋ ๋ค๋ฉด Write-behind SQL ์ ์ฅ์์ SQL ์ฟผ๋ฆฌ๊ฐ 100๊ฐ ์์ด๋ฉด 100๊ฐ๊ฐ ํ๊บผ๋ฒ์ ์ ์ก๋๋ ๊ฑธ๊น? SQL ์ ์ฅ์์ ์ต๋๋ก ์์ผ ์ ์๋ SQL ๊ฐฏ์๋ 30๊ฐ์ด๋ฉฐ application.yml ํ์ผ ๋ฑ์ ํตํด์ hibernate batch size๋ฅผ ์กฐ์ ํ ์ ์์ต๋๋ค.
spring.jpa.properties.hibernate.jdbc.batch_size=20
์์์ฑ ์ปจํ ์คํธ ์ด์ 3: ์ํฐํฐ ์์ ์ ์๋ํ๋ Dirty Checking
Entity๋ฅผ ์์ ํด์ผํ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ํ ์ ์์๊น? ๊ทธ๋ฅ ์ฐ๋ฆฌ๊ฐ JPA๋ฅผ ์ฌ์ฉํ๋ค๊ณ ์๊ฐํ๊ณ , ์ฝ๋๋ฅผ ์ง๋ณธ๋ค๋ฉด ์๋์ ๊ฐ์ด ์งค ์ ์์ต๋๋ค.
// EntityManager ์์ฑ
EntityManager em = emf.createEntityManager();
// ์์ Entity ์กฐํ
Cafe cafe = em.find(Cafe.class, 1);
// ์์ Entity ์์
cafe.setName("iceAmericano");
์ด๋ ๊ฒ ์ง๊ณ commit()๋ง ์ํํ๋ฉด ์์์ ๋ ๊น? ๊ทธ๋ฐ๋ฐ, ์ฐ๋ฆฌ๋ ์์์ฑ ์ปจํ ์คํธ์ ์๋ Entity๋ฅผ transaction beginํ๊ณ commitํ๋ฉด INSERT ์ฟผ๋ฆฌ๊ฐ ๋๊ฐ๋ ๊ฒ์ผ๋ก ์๊ณ ์๋๋ฐ, ์ด๋ ๊ฒ ํ๋ฉด ์๋ก์ด Row๊ฐ ์์ฑ๋๋ ๊ฒ์ด ์๋๊น?
// EntityManager ์์ฑ
EntityManager em = emf.createEntityManager();
// ์์ Entity ์กฐํ
Cafe cafe = em.find(Cafe.class, 1);
// ์์ Entity ์์
cafe.setName("iceAmericano");
// ํธ๋์ญ์
์์ฑ ๋ฐ ์ปค๋ฐ
EntityTransaction transaction = em.getTransaction();
transaction.begin();
transaction.commit();
์ ๋ต์ INSERT๊ฐ ์๋ UPDATE ์ฟผ๋ฆฌ๊ฐ ์ ์คํ๋๋ค ์ ๋๋ค. ์ด๋ป๊ฒ ๋ณ๊ฒฝ๋์๋์ง๋ฅผ ๊ฐ์งํ๋ ๊ฑธ๊น์? ๊ทธ๋ฅ ์์์ฑ ์ปจํ ์คํธ ์บ์์ Entity๋ฅผ ๋ถ๋ฌ์์ ๋ฟ์ด๊ณ , ์ฐ๋ฆฌ๊ฐ ์๋๋๋ก๋ผ๋ฉด INSERT ์ฟผ๋ฆฌ๊ฐ ์์ฑ๋์ด DB์ ๋ณด๋ด์ ธ์ผํ ํ ๋ฐ ๋ง์ด์ฃ .
๊ทธ ๋น๋ฐ์ JPA ์บ์์ ์๋ ์ค๋ ์ท ๋๋ฌธ์ ๋๋ค. 1์ฐจ ์บ์์ ์ ์ฅํ ๋ ID์ Entity, ๊ทธ๋ฆฌ๊ณ Snapshot์ด ์ ์ฅ๋๋๋ฐ, commit() ๋๋ flush()๊ฐ ๋ฐ์ํ์ ๋ Snapshot์ ๋น๊ตํ์ฌ ๋ณ๊ฒฝ ์ฌํญ์ผ ๊ฒฝ์ฐ์๋ UPDARE ์ฟผ๋ฆฌ๋ฅผ ๋ง๋ค์ด์ฃผ๊ฒ ๋ฉ๋๋ค.
์ด๋ฌํ ๋ณ๊ฒฝ ์ฌํญ์ ๊ฐ์งํ๋ ๊ธฐ์ ์ ์ฐ๋ฆฌ๋ Dirty Checking์ด๋ผ๊ณ ํฉ๋๋ค.
๊ทธ๋ฐ๋ฐ, ์ฐ๋ฆฌ๊ฐ ๋จ์ํ Setter ๋ฉ์๋๋ฅผ ์ด์ฉํด์ ์ด๋ ๊ฒ ๋ณ๊ฒฝํ๋ฉด UPDATE ์ฟผ๋ฆฌ๋ฅผ ์๋ํ๋ ๊ฒ์ ๋ง์ง๋ง ๋ชจ๋ ํ๋์ ๋ํด์ ๋ณ๊ฒฝ ์ฟผ๋ฆฌ๋ฅผ ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค. ๋ง์ฝ Entity์ ํ๋๊ฐ 20๊ฐ ์ด์์ด๋ผ๋ฉด 20๊ฐ ์ด์์ ๋ชจ๋ ๋ฐ์ํ๋ ๊ฒ์ด์ฃ .
์ด๋ ๊ฒ ์ฒ๋ฆฌํ๋ฉด ์ฟผ๋ฆฌ๊ฐ ๊ธธ์ด์ง๊ณ , ๋๋ฒ๊น ์ด ์ด๋ ต๊ธฐ ๋๋ฌธ์ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด์๋ง UPDATE ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋๋ก ์ค์ ํ ์ ์๋ ๋ฐฉ๋ฒ์๋ @DynamicUpdate ์ด๋ ธํ ์ด์ ์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ด ์์ต๋๋ค.
์ Annotation์ ์ฌ์ฉํจ์ผ๋ก์จ ์ํ๋ ํ๋์ ๋ํด์๋ง UPDATE ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ๋๋ก ํ ์ ์์ต๋๋ค.
๋ง์น๋ฉฐ...
์ฌ๊ธฐ๊น์ง ์์์ฑ ์ปจํ ์คํธ ๊ตฌ์กฐ๋ก ๋ณด๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๋ ์ด์ ์ ๋ํด ์์๋ดค์ต๋๋ค. ์์์ฑ ์ปจํ ์คํธ์์ ์ ๊ณตํ๋ ์บ์๋ฅผ ์ด์ฉํด Dirty Checking์ ์ด์ฉํ๋ ๋ฐฉ๋ฒ์ Java ์ ์ธ ๋ฐฉ๋ฒ์ ์ธ์ฉํ๋ ๋ฐฉ์์ด๋ผ ์์ฃผ ๊ด์ฐฎ๋ค๋ ์ด๋ฏธ์ง๋ฅผ ๋ง์ด ๊ฐ์ง๊ฒ ๋์์ต๋๋ค.
๊ทธ๋ฌ๋ UPDATE ์ฟผ๋ฆฌ๋ฅผ ์์ฑํ ๋, ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด์๋ง ์ฒ๋ฆฌํ๋ ๊ฒ ๊ธฐ๋ณธ๊ฐ์ด๋ผ๋ฉด ์ข์์ํ ๋ฐ, ๊ทธ๋ ์ง ์์ ์ด์ ๋ ์๋ง ์ฝ๋๊ฐ ๋ณต์กํด์ง๋ ๋ฐ ๋ฌธ์ ๊ฐ ์์๊น ์์ํด๋ด ๋๋ค.
'Programming > Spring' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Spring boot] Spring boot Security๋ก ์์ํด๋ณด๋ ์ธ์ฆ (3) | 2020.08.07 |
---|---|
[Spring] JPA์ ํ๋ฌ์(flush) (0) | 2020.07.02 |
[Spring] JPA์ ์์์ฑ ์ปจํ ์คํธ์ ์๋ช ์ฃผ๊ธฐ (0) | 2020.06.23 |
[Spring boot] ์ ํ๋ฆฌ์ผ์ด์ ์ Docker ์ด๋ฏธ์ง๋ก ๋ง๋ค๊ธฐ Old & New (0) | 2020.06.04 |
[Spring boot] DAO์ DTO (6) | 2020.05.27 |