엔티티 매니저 팩토리
엔티티 매니저 팩토리에서 필요할 때마다 여러 개의 엔티티 매니저를 생성해준다.
셍성비용이 크다.
한개만 만들어 프로그램 전체에서 공유하도록 설계되어 있다.
여러 스레드가 동시에 접근해도 안전하게 설계되었다.
엔티티 매니저
생성 비용이 적다.
여러 스레드가 동시에 접근하면 동시성 문제가 발생한다(공유 불가)
DB 커넥션 풀에서 연결이 필요한 시점에 커넥션을 얻어 사용한다.
동시성 문제: 동일한 자원에 대해 여러 스레드가 동시에 접근하면서 발생하는 문제
프로그램들이 성능을 위해서 여러 스레드들이 일을 같이 하게 되어 있는데, 하나의 큰 일을 동시에 처리하려다 보면 동시성 문제가 발생할 수 있다.
엔티티 매니저 팩토리는 Thread-safe 하기에 여러 스레드에서 접근해도 안전하다.
하지만 특징에 적힌 것처럼 생성하는데 많은 비용을 초래한다.
그래서 하나의 팩토리를 생성해서 공유하는 방식을 택한다.
비용이 거의 들지 않는 엔티티 매니저를 요청이 들어올 떄마다 1개씩 생성해준다.
하지만 엔티티 매니저 또한 여러 스레드가 동시에 접근하면 문제가 발생한다.
그렇기에 스레드 간에 공유할 수 없다.
요청이 1개 들어올 떄마다 엔티티 매니저 팩토리에서 요청 1개당 매니저 1개를 붙여준다.
이로 인해 동시성 문제가 발생되지 않는다.
이 엔티티매니저는 내부적으로 DB 와 커넥션을 맺는다.
해당 그림에서 요청1 과 요청2 는 DB 연결이 필요하기 때문에 Connection Pool 과 연결되어 있지만, DB 연결이 필요하지 않다면 꼭 필요한 시점까지 DB 와 connection 을 하지 않는다.
이 connection 은 보통 트랜잭션을 시작할 때 획득한다.
엔티티를 데이터 베이스에 CRUD 하는 역할이며 엔티티와 관련된 일을 수행하는 엔티티 관리자이다.
@PersistenceContext 어노테이션을 통해 주입
abstract class QueryDslSupport {
// EntityManager 필요할 때 @PersistenceContext 이용
@PersistenceContext
protected lateinit var entityManager: EntityManager
// 다른 곳에서도 쓸 수 있게 쿼리 팩토리를 만들어야 한다.
protected val queryFactory: JPAQueryFactory
get() {
return JPAQueryFactory(entityManager)
}
}
Todo Application 에서 infra 패키지 내에 QueryDslSupport 추상 클래스 내 EntityManager 가 생성돼서 담겨 있는데,
각각의 리포지토리 구현체에 담겨 클래스 내에 작성된 쿼리문에 맞게 EntityManager 가 엔티티를 CRUD 해준다.
이 EntityManager 는 싱글톤이기에 동시성 문제가 발생할 수도 있는데 스프링에서는 문제없이 잘 실행됐다.
그 이유가 무엇일까?
스프링 프레임워크는 여기에 실제 EntityManager 를 주입하는 것이 아니라 실제 EntityManager 를 연결해주는 가짜 프록시 EntityManager 를 주입해둔다.
그리고 이 EntityManager 를 호출하면 현재 데이터베이스 트랜잭션과 관련된 실제 EntityManager 를 호출해준다.
이 덕분에 Swagger에서 QueryDsl 을 이용한 api 가 잘 excute 된 것이었다.
참고:
https://velog.io/@gksrywls97/EntityManager-EntityManagerFactory