5. [JPA] 엔티티 매니저
What is the EntityManager?
The EntityManager API is used to access a database in a particular unit of work.
It is used to create and remove persistent entity instances,
to find entities by their primary key identity, and to query over all entities.
This interface is similar to the Session in Hibernate.
엔티티 매니저는 특정 작업을 위해 데이터베이스에 액세스 하는 역할을 가진 녀석이다.
또한 엔티티를 데이터베이스에 등록, 수정, 삭제, 조회할 수 있다.
엔티티와 관련된 모든 일을 처리하기에 이름 그대로 엔티티를 관리하는 관리자다.
내용을 풀이해 보자.
엔티티 매니저를 이용하여 엔티티를 저장하게 되면 DB에 access하여 insert문을 전송한다. (중간 단계로 영속성 컨텍스트는 거치게 된다.)
엔티티 매니저를 사용함에 있어 주의할 점은 여러 쓰레드가 동시에 접근하면 동시성 문제가 발생하므로 쓰레드 간에는 무조건 공유하면 안 된다.
만약 아래와 같이 AccountService의 속성으로 EntityManager가 있다면 문제가 되지 않을까?
일반적으로 스프링은 싱글톤 기반으로 동작하기 때문에 속성값은 모든 쓰레드가 공유하게 된다. 즉, 아래 코드의 private EntityManager em; 은 쓰레드 safe하지 않을 것이다. 헌데 다른 사람들이 작성한 테스트 코드들 대부분이 다음처럼 속성에 엔티티 매니저를 선언했다.
뭔가 이상하다고 느꼈지만 다 이유가 있을 것이라 생각되어 구글 검색을 하게 되었다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @Service(value = "entityManagerAccountService") class AccountService { @Getter @PersistenceContext private EntityManager em; @org.springframework.transaction.annotation.Transactional(propagation = Propagation.REQUIRES_NEW) public void updateAccount(int id) { Account account = em.find(Account.class, id); account.setName("changedName"); } @org.springframework.transaction.annotation.Transactional(propagation = Propagation.REQUIRES_NEW) public void testData() { Account account = new Account(); account.setId(2); account.setName("nklee"); em.persist(account); } } | cs |
결론을 말하자면 위의 엔티티매니저는 동시성 문제가 발생하지 않는다.
Spring 컨테이너가 초기화되면서 @PersistenceContext 애노테이션으로 주입받은 EntityManager를 Proxy로 감싼다.
이후 EntityManager 호출 시 마다 Proxy를 통해 EntityManager를 생성 하여 Thread-Safety를 보장 한다.
참고 URL : https://doanduyhai.wordpress.com/2011/11/21/spring-persistencecontext-explained/
정말로 proxy로 감싸져 있는지 테스트 코드를 통해서 확인해 보자.
더불어 엔티티 매니저를 생성했을 때의 인스턴스 비교도 함께 테스트 해보겠다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | @PersistenceUnit private EntityManagerFactory emf; @PersistenceContext private EntityManager em; @Test public void 엔티티매니저_호출시마다_새로운인스턴스리턴() { EntityManager em1 = emf.createEntityManager(); EntityManager em2 = emf.createEntityManager(); EntityManager em3 = emf.createEntityManager(); System.out.println(em1); System.out.println(em2); System.out.println(em3); System.out.println(em); assertThat(em1, is(not(sameInstance(em2)))); assertThat(em1, is(not(sameInstance(em3)))); assertThat(em, is(not(sameInstance(em1)))); assertThat(em, is(not(sameInstance(em2)))); assertThat(em, is(not(sameInstance(em3)))); } | cs |
로그를 확인해 보면 @PersistenceContext 로 주입받은 엔티티 매니저는 프록시 객체로 감싸져 있는 것을 확인할 수 있고
엔티티 매니터 팩토리를 통해 엔티티 매니저를 생성한 객체들은 프록시로 감싸져 있지 않다.
1 2 3 4 | org.hibernate.jpa.internal.EntityManagerImpl@739831a4 org.hibernate.jpa.internal.EntityManagerImpl@7e3236d org.hibernate.jpa.internal.EntityManagerImpl@38a4e2b0 Shared EntityManager proxy for target factory [org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean@14c141c0] | cs |
'프로그래밍' 카테고리의 다른 글
9. [JPA] @Enumerated (0) | 2017.08.23 |
---|---|
8. [JPA] Attribute Converter (0) | 2017.08.23 |
7. [JPA] fetch type - 로딩 기법 (0) | 2017.08.21 |
6. [JPA] 영속성 전이 - Cascade (0) | 2017.08.17 |
4. [JPA] 엔티티 매니저 팩토리 (0) | 2017.08.10 |
3. [JPA] 영속성 컨텍스트란? (0) | 2017.08.01 |
2. [JPA] 테스트 환경 (0) | 2017.07.21 |
1. [JPA] 사용 경험 (2) | 2017.07.21 |