티스토리 뷰

JPA

[JPA] EntityManagerFactory & EntityManager

on1ystar 2023. 5. 7. 20:03
728x90
반응형

보통 Spring에서 JPA를 사용할 때, 엔티티 매니저(EntityManager) 객체를 Spring 컨테이너로부터 주입 받아 사용한다. 하지만 사실, 우리가 Java 어플리케이션에서 JPA를 사용하기 위해서는 가장 먼저 엔티티 매니저 팩토리(EntityManagerFactory)라는 인터페이스 객체를 먼저 생성해야 한다.

지금까지는 Spring 컨테이너의 도움을 받았기 때문에 이 엔티티 매니저 팩토리의 존재를 몰라도 됐을 것이다. 본 포스팅에서는 컨테이너의 도움 없이, Java SE에서 JPA를 사용하기 위해서는 어떤 과정이 필요하며, 엔티티 매니저 팩토리와 엔티티 매니저에 대해 정리해 보려고 한다.

Goals

  • 엔티티 매니저 팩토리 ? 엔티티 매니저 ?
  • 엔티티 매니저가 제공하는 주요 메서드
  • 스프링 프레임워크에서 사용하기

 

엔티티 매니저 팩토리 ? 엔티티 매니저 ?


우리가 Java 어플리케이션에서 JPA를 사용하기 위해서는 가장 먼저 엔티티 매니저 팩토리라(EntityManagerFactory)는 객체를 생성해야 한다고 했다. 이 객체가 있어야 우리가 사용하던 엔티티 매니저(EntityManager)를 생성할 수 있기 때문이다. 즉, 엔티티 매니저 팩토리는 이름 그대로 엔티티 매니저를 찍어내는 공장같은 것이다.

EntityManagerFactory는 JPA에서 EntityManager를 생성하기 위한 인터페이스

 

엔티티 매니저 팩토리 생성

EntityManagerFactory emf = Persistence.createManagerFactory("example");

Persistence 클래스는 JPA에서 엔티티 매니저 팩토리를 생성하는 가장 간단한 방법을 제공한다.

  • createEntityManagerFactory(String persistenceUnitName) : 지정한 이름의 persistence-unit(영속성 유닛)을 사용하여 엔티티 매니저 팩토리를 생성

이 메서드는 persistence.xml 파일에서 정의한 설정 정보를 기반으로 엔티티 매니저 팩토리를 생성한다. persistence.xml은 JPA에서 데이터베이스와의 연결 및 매핑 정보를 정의하는 파일이다.

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
             http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
             version="2.0">

    <persistence-unit name="example">
        <properties>
            <property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/example"/>
            <property name="javax.persistence.jdbc.user" value="root"/>
            <property name="javax.persistence.jdbc.password" value="password"/>
            <property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQL5Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true"/>
        </properties>
    </persistence-unit>

</persistence>

JPA 설정은 영속성 유닛(persistence-unit)이라는 것부터 시작하는데 일반적으로 연결할 데이터베이스당 하나의 영속성 유닛을 등록한다. 위 예시 파일에서 persistence-unit 요소를 보면 고유한 이름을 부여하기 위해 name 속성 값으로 example을 넣어줬다. 이러면 일므이 example인 영속성 유닛을 찾아 엔티티 매니저 팩토리를 생성한다.

주의할 점은 엔티티 매니저 팩토리는 어플리케이션 전체에서 딱 한 번만 생성하고 공유해서 사용해야 한다. 엔티티 매니저 팩토리는 JPA를 동작시키기 위한 기반 객체를 만들고, 커넥션 풀도 생성하는 등 생성 비용이 매우 크기 때문이다. 따라서 보통 애플리케이션 시작 시점에서 엔티티 매니저 팩토리를 생성하고, 애플리케이션 종료 시점에서 close()를 호출해 닫는다.

 

엔티티 매니저 생성

엔티티 매니저 팩토리를 생성했으면 엔티티 매니저를 생성할 수 있다.

EntityManagerFactory emf = Persistence.createManagerFactory("example");
EntityManager em = emf.createEntityManager();

JPA의 기능 대부분은 이 엔티티 매니저가 제공한다. 대표적으로 엔티티 매니저를 사용해서 엔티티를 데이터베이스에 등록/수정/삭제/조회할 수 있다. 엔티티 매니저는 내부에 데이터베이스 커넥션을 유지하면서 데이터베이스와 통신한다.

EntityManager는 JPA에서 객체와 데이터베이스 테이블 간의 매핑을 관리하는 객체. 개발자는 EntityManager를 가상의 데이터베이스라고 생각하면 된다.

 

엔티티 매니저는 내부적으로 데이터베이스와의 커넥션을 관리하며, 트랜잭션 내에서 엔티티를 영속화하고 업데이트하며, 쿼리를 실행한다. 또한, 엔티티의 상태를 추적하고 관리하므로, 엔티티를 영속화할 때 자동으로 데이터베이스에 INSERT 쿼리를 실행하거나, 업데이트할 때 자동으로 UPDATE 쿼리를 실행하고, 삭제할 때 자동으로 DELETE 쿼리를 실행한다.

참고로, 엔티티 매니저 팩토리는 여러 스레드가 동시에 접근해도 안전하므로 서로 다른 스레드 간에 공유해도 되지만, 엔티티 매니저는 데이터베이스 커넥션과 밀접한 관계가 있어 여러 스레드가 동시에 접근하면 동시성 문제가 발생하므로 스레드 간에 절대 공유하면 안 된다.

 

엔티티 매니저가 제공하는 주요 메서드


엔티티 관리 메서드

  • persist(Object entity): 엔티티를 영속화(저장)
  • merge(Object entity): 엔티티를 병합(준영속 상태의 엔티티를 영속 상태로 만들거나, 영속 상태의 엔티티를 업데이트할 때 사용)
  • remove(Object entity): 엔티티를 삭제
  • find(Class<T> entityClass, Object primaryKey): 엔티티를 조회
  • getReference(Class<T> entityClass, Object primaryKey): 엔티티의 참조를 조회

 

쿼리 생성 및 실행 메서드

  • createQuery(String qlString): JPQL(Java Persistence Query Language) 쿼리를 생성
  • createNamedQuery(String name): 이름이 지정된 JPQL 쿼리를 생성
  • createNativeQuery(String sqlString): 네이티브 SQL 쿼리를 생성
  • createNamedStoredProcedureQuery(String name): 이름이 지정된 저장 프로시저 쿼리를 생성

 

트랜잭션 관리 메서드

  • getTransaction(): 현재 트랜잭션을 획득
  • joinTransaction(): 현재 트랜잭션에 참여
  • flush(): 영속성 컨텍스트의 변경 내용을 데이터베이스에 반영

 

스프링 프레임워크에서 사용하기


스프링 프레임워크에서는 엔티티 매니저 팩토리를 빈(Bean)으로 등록하여 관리한다. 따라서 스프링의 IoC 컨테이너에서 생성되고 관리되며, 필요한 곳에서 주입받아 사용할 수 있다.

스프링에서 제공하는 LocalContainerEntityManagerFactoryBean은 JPA를 사용하는 데 필요한 엔티티 매니저 팩토리를 설정하는 데 사용된다.

 

JavaConfig.java 파일로 설정

@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class AppConfig {

    @Bean
    public DataSource dataSource() {
        // 데이터베이스 연결 정보 설정
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        LocalContainerEntityManagerFactoryBean emf = new LocalContainerEntityManagerFactoryBean();
        emf.setDataSource(dataSource());
        emf.setPackagesToScan("com.example.entity");
        emf.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        emf.setJpaProperties(jpaProperties());
        return emf;
    }

    private Properties jpaProperties() {
        Properties props = new Properties();
        props.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL5InnoDBDialect");
        props.setProperty("hibernate.hbm2ddl.auto", "update");
        props.setProperty("hibernate.show_sql", "true");
        return props;
    }
}

 

appConfig.xml 파일로 설정

<!--데이터베이스 연결 정보 설정-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/test" />
    <property name="username" value="root" />
    <property name="password" value="root" />
</bean>

<!--엔티티 매니저 팩토리 설정-->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="dataSource" />
		<!--@Entity 탐색 시작 위치-->
    <property name="packagesToScan" value="com.example.entity" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
		<!--하이버네이트 상세 설정-->
    <property name="jpaProperties">
        <props>
            <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
            <prop key="hibernate.hbm2ddl.auto">update</prop>
            <prop key="hibernate.show_sql">true</prop>
        </props>
    </property>
</bean>

 

@PersistenceContext

javax.persistence.PersistenceContext 어노테이션은 컨테이너가 관리하는 엔티티 매니저를 주입해 준다.

@Repository
public class UserRepositoryImpl implements UserRepository {

    @PersistenceContext
    private EntityManager entityManager;

    // ...
}

 

Spring Boot에서 사용하기

Spring Boot에서는 JPA를 사용할 때 엔티티 매니저 팩토리를 생성하고 관리하기 위해 application.properties 또는 application.yml 파일에서 설정한다.

spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect

위와 같이 설정한 후, 엔티티 매니저를 주입받아 사용한다.

마찬가지로 @PersistenceContext를 사용해도 되고, 생성자로도 주입받을 수 있다.

import jakarta.persistence.EntityManager;

public class JpaMemberRepository implements MemberRepository{

    private final EntityManager em;

    @Autowired  // (생성자가 1개이므로 생략 가능)
    public JpaMemberRepository(EntityManager em) {
        this.em = em;
    }

    @Override
    public Member save(Member member) {
        em.persist(member);
        return member;
    }

    ...

 

참고로 김영한님의 코멘트를 하나 첨부한다.

(https://www.inflearn.com/questions/158967/안녕하세요-entitymanager에-대해-궁금한-점이-있어-질문-남깁니다)

스프링 프레임워크는 여기에 실제 EntityManager를 주입하는 것이 아니라, 사실은 실제 EntityManager를 연결해주는 가짜 EntityManager를 주입해둡니다.

그리고 이 EntityManager를 호출하면, 현재 데이터베이스 트랜잭션과 관련된 실제 EntityManager를 호출해줍니다.

덕분에 개발자는 동시성 이슈에 대한 고민없이, 쉽게 개발할 수 있습니다.

 

References

  • 자바 ORM 표준 JPA 프로그래밍 - 김영한
728x90
반응형
댓글