์์์ฑ ๊ด๋ฆฌ์์ ๊ฐ์ฅ ์ค์ํ ๊ฒ์ 2๊ฐ์ง์ด๋ค.
- ๊ฐ์ฒด์ ๊ด๊ณํ ๋ฐ์ดํฐ๋ฒ ์ด์ค๊ฐ์ ๋งคํ
- ์์์ฑ ์ปจํ ์คํธ ( JPA ๋ด๋ถ ๋์ )
EntityManagerFactory์ EntityManager
ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ์ฌ๋ ๋ง๋ค ( ์ฆ, thread๊ฐ ํ๋์ฉ ์์ฑ๋ ๋๋ง๋ค ) EntityManager๋ฅผ ์์ฑํ๋ค.
EntityManager๋ ๋ด๋ถ์ ์ผ๋ก DB ์ปค๋ฅ์ ํ์ ์ฌ์ฉํด์ DB์ ์ ๊ทผํ๋ค.
EntityManagerFactory
JPA๋ EntityManagerFactory๋ฅผ ๋ง๋ค์ด์ผ ํ๋ค.
์ ํ๋ฆฌ์ผ์ด์ ์ด ๋ก๋ฉ๋๋ ์์ ์ DB๋น ๋ฑ ํ๋๋ง ์์ฑํด์ผ ํ๋ค.
EntityManagerFactory emf = Persistence.createEntityManagerFactory("emf");
๊ทธ๋ฆฌ๊ณ WAS๊ฐ ์ข ๋ฃ๋๋ ์์ ์ EntityManagerFactory๋ฅผ ๋ซ๋๋ค.
EntityManager
์ค์ ํธ๋์ญ์ ๋จ์๊ฐ ์ํ๋ ๋๋ง๋ค ์์ฑ๋๋ค.
ํด๋ผ์ด์ธํธ์ ์์ฒญ์ด ๋ค์ด์ฌ ๋ ์์ฑํ๋ค๊ฐ ์์ฒญ์ด ๋๋๋ฉด ๋ซ๋๋ค.
thread๊ฐ์ ๊ณต์ ๋ฅผ ํ๋ฉด ์๋๊ณ ํธ๋์ญ์ ์ด ์ํ๋ ํ์๋ ๋ฐ๋์ ๋ซ๊ณ DB ์ปค๋ฅ์ ์ ๋ฐํํ๋ค.
public void createMember() {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
Member member = new Member("Id", "pw");
em.persist(member);
tx.commit();
}
EntityTransaction
๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋ ๋ชจ๋ ์์ ์ ๋ฐ๋์ ํธ๋์ญ์ ์์์ ์ด๋ฃจ์ด์ ธ์ผ ํ๋ค. ( ์กฐํ๋ ์๊ด ์์ )
EntityTransaction tx = entityManager.getTransaction();
tx.begin(); // ํธ๋์ญ์
์์
tx.commit(); // ํธ๋์ญ์
์ํ
tx.rollback(); // ์์
์ ๋ฌธ์ ๋ฐ์ ์
๊ด๊ณ๋๋ฅผ ์ดํด๋ณด๋ฉด ์๋ ๊ทธ๋ฆผ๊ณผ ๊ฐ๋ค.
EntityManager ๊ด๊ณ๋ - ์ถ์ฒ : https://iyoungman.github.io/jpa/EntityManagerFactory-EntityManager-PersistenceContext/
EntityManager๋ DB ์ฐ๊ฒฐ์ด ํ์ํ ์์ ์ ์ปค๋ฅ์ ํ์์ ์ปค๋ฅ์ ์ ํ๋ ์ป๋๋ค.
JPA์ ๊ตฌํ์ฒด( ex. hibernate )๋ค์ EntityManagerFactory๋ฅผ ์์ฑํ ๋ ์ปค๋ฅ์ ํ์ ๋ง๋ ๋ค.
์์์ฑ ์ปจํ ์คํธ ( Persistence Context )
Entity๋ฅผ ์๊ตฌ ์ ์ฅํ๋ ํ๊ฒฝ์ ๋ปํ๋ค.
EntityManager๋ก Entity๋ฅผ ์ ์ฅํ๊ฑฐ๋ ์กฐํํ๋ฉด ์์์ฑ ์ปจํ ์คํธ์ Entity๋ฅผ ๋ณด๊ดํ๊ณ ๊ด๋ฆฌํ๋ค.
em.persist(entity);
์ ์ฝ๋๋ ์ค์ ๋ก DB์ ์ ์ฅํ๋ค๋ ๊ฒ์ด ์๋๋ผ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ํตํด์ Entity๋ฅผ ์์ํํ๊ฒ ๋ค๋ ์๋ฏธ๋ค.
์ฆ, Entity๋ฅผ ์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅํ๋ ๊ฒ์ด๋ค.
EntityManager๋ฅผ ํตํด์ ์์์ฑ ์ปจํ ์คํธ์ ์ ๊ทผํ๊ฒ ๋๋ค.
package javax.persistence;
import java.util.List;
import java.util.Map;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate;
import javax.persistence.metamodel.Metamodel;
public interface EntityManager {
void persist(Object var1);
<T> T merge(T var1);
void remove(Object var1);
<T> T find(Class<T> var1, Object var2);
<T> T find(Class<T> var1, Object var2, Map<String, Object> var3);
<T> T find(Class<T> var1, Object var2, LockModeType var3);
<T> T find(Class<T> var1, Object var2, LockModeType var3, Map<String, Object> var4);
<T> T getReference(Class<T> var1, Object var2);
void flush();
...
}
EntityManager๊ฐ ์์ฑ๋๋ฉด 1:1๋ก ์์์ฑ ์ปจํ ์คํธ๊ฐ ์์ฑ๋๋ค.
ํ์ง๋ง ์ปจํ ์ด๋ ํ๊ฒฝ์ JPA์์๋ ์ฌ๋ฌ EntityManager๊ฐ ํ๋์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๊ณต์ ํ๊ฒ ๋๋ค.
์ปจํ ์ด๋ ํ๊ฒฝ์ JPA๋ผ ํจ์ ๋ฌด์์ผ๊น?
์ปจํ ์ด๋๋ฅผ ์ฌ์ฉํ๋ ํ๊ฒฝ (ex. spring)์์๋ ๊ฐ๋ฐ์๊ฐ EntityManager๋ฅผ ์ง์ ์์ฑํ์ง ์๊ณ ์ปจํ ์ด๋์ ์์ํ๋ค.
์ผ๋ฐ์ ์ผ๋ก ์คํ๋ง์ ์ฑ๊ธํค ๊ธฐ๋ฐ์ผ๋ก ๋์ํ๊ธฐ ๋๋ฌธ์ ์์ฑ๊ฐ์ ๋ชจ๋ thread๊ฐ ๊ณต์ ํ๊ฒ ๋๋ค.
๊ทธ๋์ ์ฌ๋ฌ thread๊ฐ ๋์์ ์ ๊ทผํ๋ฉด ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์๋ ์๋ค.
๊ทธ๋ ๋ค๋ฉด ์คํ๋ง์ด ๊ด๋ฆฌํ๋ EntityManager์ Thread-safe๋ฅผ ์ด๋ป๊ฒ ๋ณด์ฅํ ๊น?
EntityManager๋ฅผ Proxy๋ฅผ ํตํด์ ๊ฐ์ธ๊ณ EntityManager ๋ฉ์๋ ํธ์ถ ๋ ๋ง๋ค Proxy๋ฅผ ํตํด์ EntityManager๋ฅผ ์์ฑํ๋ค.
EntityManager๋ฅผ ์ง์ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ์๋ @PersistenceContext๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
- ์ง์ ์์ฑํ EntityManager์ @PersistenceContext๋ฅผ ์ ์ธํ๋ ๊ฒฝ์ฐ
@Service
public class TestService {
@PersistenceContext
private EntityManager entityManager;
...
}
entityManager = {$Proxy84@9656} "Shared EntityManager proxy for target factory [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@4f356b98]"
์คํ๋ง ์ปจํ ์ด๋๊ฐ ์ด๊ธฐํ ๋๋ฉด์ @PersistenceContext ์ด๋ ธํ ์ด์ ์ผ๋ก ์ฃผ์ ๋ฐ์ EntityManager๋ฅผ Proxy๋ก ๊ฐ์ผ๋ค.
SharedEntityManagerCreator์ ์ํด Proxy๋ก ๋ง๋ค์ด์ง๋ค.
- SimpleJpaRepository์ EntityManager
@Repository
@Transactional(readOnly = true)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
private final JpaEntityInformation<T, ?> entityInformation;
private final EntityManager em;
private final PersistenceProvider provider;
private @Nullable CrudMethodMetadata metadata;
private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
...
}
em = {$Proxy84@9748} "Shared EntityManager proxy for target factory [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@4f356b98]"
SharedEntityManagerCreator์ ์ํด Proxy๋ก ๋ง๋ค์ด์ง๋ค.
ํธ๋์ญ์ ๋ฒ์์ ์์์ฑ ์ปจํ ์คํธ
์คํ๋ง ์ปจํ ์ด๋๋ ํธ๋์ญ์ ๋ฒ์์ ์์์ฑ ์ปจํ ์คํธ ์ ๋ต์ ๊ธฐ๋ณธ์ผ๋ก ์ฌ์ฉํ๋ค.
ํธ๋์ญ์ ์ด ์์ํ ๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์์ฑํ๊ณ ํธ๋์ญ์ ์ด ๋๋ ๋ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๋๋ธ๋ค.
์ฆ, ํธ๋์ญ์ ์ด ๊ฐ์ผ๋ฉด ๊ฐ์ ์์์ฑ ์ปจํ ์คํธ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ด๋ค.
ํธ๋์ญ์ ๋ฒ์์ ์์์ฑ ์ปจํ ์คํธ - ์ถ์ฒ : https://iyoungman.github.io/jpa/EntityManagerFactory-EntityManager-PersistenceContext/
์ฌ๋ฌ EntityManager๋ฅผ ์ฌ์ฉํด๋ ํ ํธ๋์ญ์ ์ผ๋ก ๋ฌถ์ด๋ฉด ์์์ฑ ์ปจํ ์คํธ๋ฅผ ๊ณต์ ํ๋ค.
์ถ์ฒ : https://iyoungman.github.io/jpa/EntityManagerFactory-EntityManager-PersistenceContext/
๊ฐ์ EntityManager๋ฅผ ์ฌ์ฉํด๋ ํธ๋์ญ์ ์ ๋ฐ๋ผ ์ ๊ทผํ๋ ์์์ฑ ์ปจํ ์คํธ๊ฐ ๋ค๋ฅด๋ค.
๋ฐ๋ผ์ ๊ฐ์ EntityManager๋ฅผ ํธ์ถํด๋ ์ ๊ทผํ๋ ์์์ฑ ์ปจํ ์คํธ๊ฐ ๋ค๋ฅด๋ฏ๋ก ๋ฉํฐ์ค๋ ๋์ ์์ ํ๋ค.
์ถ์ฒ : https://iyoungman.github.io/jpa/EntityManagerFactory-EntityManager-PersistenceContext/
Entity์ ์์ฑ์ฃผ๊ธฐ ( Entity LifeCycle )
- ๋น์์ ( new / transient )
์์์ฑ ์ปจํ ์คํธ์๋ ์ ํ ๊ด๊ณ๊ฐ ์๋ ์ํ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑ๋ง ํ ์ํ๋ฅผ ๋งํ๋ค.
Member member = new Member();
member.setId("memberID");
member.setPw("password");
- ์์ ( managed )
์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋ ์ํ๋ก Entity๊ฐ ์์์ฑ ์ปจํ ์คํธ์ ์ํด ๊ด๋ฆฌ๋๊ณ ์๋ ์ํ๋ฅผ ๋งํ๋ค.
Member member = new Member();
member.setId("memberID");
member.setPw("password");
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
em.persist(member); // ์์ ์ํ๊ฐ ๋๋ค๊ณ ๋ฐ๋ก ์ฟผ๋ฆฌ๊ฐ ๋ ๋ผ๊ฐ์ง๋ ์์
//tx.commit(); ์ ํด์ฃผ์ด์ผ ์์์ฑ ์ปจํ
์คํธ์ ์๋ ์ ๋ณด๋ค์ด DB์ ์ฟผ๋ฆฌ๋ก ๋ ๋ผ๊ฐ
- ์ค์์ ( detached )
์์์ฑ ์ปจํ ์คํธ์ ์ ์ฅ๋์๋ค๊ฐ ๋ถ๋ฆฌ๋ ์ํ๋ก ์์์ฑ ์ปจํ ์คํธ์์ ์ง์ด ์ํ๋ฅผ ๋งํ๋ค.
em.detach(member);
- ์ญ์ ( removed )
์ค์ DB ์ญ์ ๋ฅผ ์์ฒญํ ์ํ๋ฅผ ๋งํ๋ค.
em.remove(member);
์์์ฑ ์ปจํ ์คํธ๊ฐ ์กด์ฌํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น?
1. 1์ฐจ ์บ์
์์์ฑ ์ปจํ ์คํธ ๋ด๋ถ์๋ 1์ฐจ ์บ์๊ฐ ์กด์ฌํ๋ค. ( 1์ฐจ ์บ์๋ฅผ ์์์ฑ ์ปจํ ์คํธ๋ผ๊ณ ์ดํดํด๋ ๋๋ค. )
Map<Key, Value> ํํ๋ก 1์ฐจ ์บ์์ ์ ์ฅ์ด ๋๋ค. ( key = @Id๋ก ์ ์ธํ PK, value = ํด๋น Entity ์์ฒด )
Member member = new Member();
member.setId("memberID");
member.setPw("password");
/* ์์ ์ํ (Persistence Context ์ ์ํด Entity ๊ฐ ๊ด๋ฆฌ๋๋ ์ํ) */
// DB ์ ์ฅ X, 1์ฐจ ์บ์์ ์ ์ฅ๋จ
em.persist(member);
// 1์ฐจ ์บ์์์ ์กฐํ
Member findMember = em.find(Member.class, "memberID");
1์ฐจ ์บ์์ Entity๊ฐ ์์ ๋ ์ด์ ์ ๋ฌด์์ผ๊น?
em.find()๋ฅผ ํ๋ฉด DB๋ณด๋ค ๋จผ์ 1์ฐจ ์บ์๋ฅผ ์กฐํํ๊ฒ ๋๋ค. ๊ทธ๋ฆฌ๊ณ 1์ฐจ ์บ์์ ํด๋น Entity๊ฐ ์กด์ฌํ๋ค๋ฉด ๋ฐ๋ก ๋ฐํํ ์ ์๋ค.
1์ฐจ ์บ์์ ํด๋น Entity๊ฐ ์๋ค๋ฉด DB๋ฅผ ์ง์ ์กฐํํ๊ณ ์กฐํํ ๊ฒฐ๊ณผ๋ฅผ 1์ฐจ ์บ์์ ์ ์ฅํ ํ Entity๋ฅผ ์ฐพ์์จ๋ค.
EntityManager๋ ํธ๋์ญ์ ๋จ์๋ก ๋ง๋ค๊ณ ํธ๋์ญ์ ์ด ๋๋ ๋ ํจ๊ป ์ข ๋ฃ๊ฐ ๋๋ค.
๊ทธ๋ 1์ฐจ ์บ์๋ ๋ชจ๋ ๋ ๋ผ๊ฐ๊ธฐ ๋๋ฌธ์ ํธ๋์ญ์ ์ด ์ํ๋๋ ๋์์๋ง ์ด๋์ด ์๊ธฐ ๋๋ฌธ์ ํฐ ์ฑ๋ฅ ์ด์ ์ ๊ฐ์ง๊ณ ์์ง๋ ์๋ค.
ํ์ง๋ง ๋น์ฆ๋์ค ๋ก์ง์ด ๊ต์ฅํ ๋ณต์กํ ๊ฒฝ์ฐ์๋ ํจ๊ณผ๋ฅผ ๋ณผ ์ ์๋ค.
2. ๋์ผ์ฑ ๋ณด์ฅ
Member member1 = entityManager.find(Member.class, "member1");
Member member2 = entityManager.find(Member.class, "member1");
System.out.println(member1 == member2); // ๋์ผ์ฑ ๋น๊ต true
์์ Entity์ ๋์ผ์ฑ์ ๋ณด์ฅํ๋ค.
member1์ ํด๋นํ๋ Entity๋ฅผ 2๋ฒ ์กฐํํ๊ฒ ๋๋ฉด 1์ฐจ ์บ์์ ์ํด ๊ฐ์ Reference๋ก ์ธ์์ด ๋๋ค.
๋ฐ๋ผ์ ํ๋์ ํธ๋์ญ์ ๋ด์์ ๊ฐ์ Entity๋ฅผ ๋น๊ตํ๊ฒ ๋๋ฉด ๋์ผ์ฑ์ ๋ณด์ฅ๋๋ค.
1์ฐจ ์บ์๋ก Repeatable read ๋ฑ๊ธ์ isolation level์ DB๊ฐ ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์ฐจ์์์ ์ ๊ณตํ๋ค.
3. Entity ๋ฑ๋ก ์ ํธ๋์ญ์ ์ ์ง์ํ๋ ์ฐ๊ธฐ ์ง์ฐ ( ๋ฒํผ๋ง )
public void createMember() {
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
//EntityManager๋ ํ
์ดํฐ ๋ณ๊ฒฝ์ ํธ๋์ญ์
์ ์์ํด์ผ ํ๋ค.
tx.begin();
Member member1 = new Member("Id1", "pw1");
Member member2 = new Member("Id2", "pw2");
//์ด๋๊น์ง Insert ์ฟผ๋ฆฌ๋ฅผ DB์ ๋ณด๋ด์ง ์๋๋ค.
em.persist(member1);
em.persist(member2);
//commit ํ๋ ์๊ฐ DB์ ์ฟผ๋ฆฌ๋ฅผ ๋ณด๋ธ๋ค.
tx.commit();
}
em.persist()
JPA๊ฐ insert SQL์ ๊ณ์ ์๊ณ ์๋ ์ํ๋ค.
member1์ 1์ฐจ ์บ์์ ์ ์ฅํํ JPA๊ฐ Entity๋ฅผ ๋ถ์ํ์ฌ insert SQL์ ๋ง๋ ๋ค.
๊ทธ๋ ๊ฒ ๋ง๋ค์ด์ง insert SQL์ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์๋ผ๋ ๊ณณ์ ์๊ณ DB์ ๋ฐ๋ก ๋ฃ์ง ์๊ณ ๊ธฐ๋ค๋ฆฐ๋ค.
member2๋ ๊ฐ์ ๊ณผ์ ์ ๋ฐ๋ณตํ๋ค.
tx.commit()
commit ์์ ์ insert SQL์ ๋์์ DB๋ก ๋ณด๋ธ๋ค.
์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ์์ฌ์๋ SQL๋ค์ DB๋ก ๋ ๋ฆฐ๋ค. ( flush() )
flush()๋ 1์ฐจ ์บ์๋ฅผ ์ง์ฐ์ง๋ ์๊ณ ์ฟผ๋ฆฌ๋ค์ DB์ ๋ ๋ ค์ DB์์ ์ฑํฌ๋ฅผ ๋ง์ถ๋ ์ญํ ์ ํ๋ค.
flush() ํ์๋ ์ค์ DB ํธ๋์ญ์ ์ ์ปค๋ฐ์ด ๋๋ค. ( commit() )
4. ๋ํฐ ์ฒดํน ( Dirty Checking )
EntityManager em = emf.createEntityManager();
EntityTransaction tx = em.getTransaction();
tx.begin();
// ์์ ์ํฐํฐ ์กฐํ
Member memberA = em.find(Member.class, "memberA");
// ์์ ์ํฐํฐ ๋ฐ์ดํฐ ์์
memberA.setUsername("hi");
memberA.setAge(10);
//ํธ๋์ญ์
์ปค๋ฐ
tx.commit();
Entity ๋ฐ์ดํฐ๋ฅผ ์์ ํ๊ณ ์ปค๋ฐํ๊ธฐ ์ ์ update()๋ persist()๋ก ์์์ฑ ์ปจํ ์คํธ์ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ ๋ฐ์ดํธ ํด๋ฌ๋ผ๊ณ ์๋ ค์ฃผ์ด์ผ ํ์ง ์์๊น?
๊ทธ๋ด ํ์ ์๋ค.
Entity ๋ฐ์ดํฐ๋ง ์์ ํ๊ณ commit๋ง ํ๋ฉด DB์ ์๋์ผ๋ก ๋ฐ์์ด ๋๋ค.
์ฆ, ๋ฐ์ดํฐ๊ฐ setํ๋ฉด ํด๋น ๋ฐ์ดํฐ ๋ณ๊ฒฝ์ ๊ฐ์งํ์ฌ ์๋์ผ๋ก update SQL์ด ๋๊ฐ๋ ๊ฒ์ด๋ค.
tx.commit()์ ํ๋ฉด flush()๊ฐ ์ผ์ด๋ ๋ Entity์ ์ค๋ ์ท์ ์ผ์ผ์ด ๋น๊ตํ๋ค.
๋ณ๊ฒฝ์ฌํญ์ด ์์ผ๋ฉด update SQL์ ๋ง๋ค๊ณ ์ฐ๊ธฐ ์ง์ฐ SQL ์ ์ฅ์์ ์ ์ฅ์ ํ๋ค.
update SQL์ DB์ ๋ฐ์ํ ํ commit()์ ํ๊ฒ ๋๋ค.
@DynamicUpdate
๋ํฐ ์ฒดํน์ผ๋ก ์์ฑ๋๋ update SQL์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ชจ๋ ํ๋๋ฅผ ์ ๋ฐ์ดํธํ๋ค.
@DynamicUpdate๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณ๊ฒฝ๋ ํ๋๋ง ๋ฐ์๋๋๋ก ํ ์ ์๋ค.
5. Entity ์ญ์
Member memberA = em.find(Member.class, "memberA");
em.remove(memberA);
์์ Entity ์์ ๋งค์ปค๋์ฆ๊ณผ ๋์ผํ๋ค.
ํธ๋์ญ์ ์ commit ์์ ์ delete SQL์ด ๋๊ฐ๋ค.