Spring JPA EntityManagerFactory 등록

이혜지·2024년 2월 13일

EntityManagerFactory 등록


JPA 퍼시스턴스 컨텍스(JPA Persistence Context)에 접근하고 엔티티 인스턴스를 관리하려면 JPA의 핵심 인터페이스인 EntityManager를 구현한 오브젝트가 필요하다.

EntityManager는 JPA에서 두가지 방식으로 관리된다.

  1. 애플리케이션이 관리하는 EntityManager
    1. 애플리케이션이 관리하는 EntityManager는 JavaEE와 JavaSE에서 모두 사용가능하다.
  2. 컨테이너가 관리하는 EntityManager
    1. 컨테이너가 관리하는 EntityManager를 위해서는 JavaEE 환경과 서버가 필요하다.

어떤 방식을 사용하든 반드시 EntityManagerFactory를 빈으로 등록해야한다. 스프링에서는 세 가지 방법을 이용해 EntityManagerFactory타입의 빈을 등록할 수 있다.

1. LocalEntityManagerFactoryBean


LocalEntityManagerFactoryBean은 JPA 스펙의 JavaSE 기동 방식을 이용해 EntityManagerFactory를 생성해준다.

<bean id="emf" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
</bean>

이 빈은 PersistenceProvider 자동 감지 기능을 이용해서 프로바이더를 찾고 META-INF/persistence.xml 에 담긴 퍼시스턴스 유닛의 정보를 활용해서 EntityManagerFactory를 생성한다.

  • 단점
    • JPA를 사용하는 단순한 환경에서는 괜찮지만, 스프링에서 본격적으로 사용하기에는 제약사항이 많다.
    • 스프링 빈으로 등록한 DataSource를 사용할 수 없다.
    • 스프링이 제공하는 바이트코드 위빙 기법도 적용할 수 없다.
    • JPA프레임워크가 직접 제공하는 확장 기능을 사용할 수는 있지만 스프링에서 제어할 수 없다.
    • 그러므로 굳이 사용할 이유가없음

2. JavaEE 5 서버가 제공하는 EntityManagerFactory


JPA는 JavaSE 환경보다는 JavaEE에서 서버가 제공하는 JPA 프로바이더를 통해 사용하는 것이 일반적.

스프링 애플리케이션에서는 JNDI를 통해 서버가 제공하는 EntityManager와 EntityManagerFactory를 제공받을 수 있다. 또 서버의 JTA를 이용해 트랜잭션 관리 기능을 활용할 수 있다.

*스프링에서는 특별히 관여할수잇는 설정은 없고, 이 방식은 JPA를 지원하는 JavaEE 5 이상의 서버에 배치하고 JPA 프로바이더와 서버가 요구하는 설정을 해뒀다고 전제한다.

모든 JPA의 기능은 서버와 JPA의 퍼시스턴스 유닛 설정(META-INF/persistence.xml)에 따른다.

스프링에서는 DAO를 사용할 수 있도록 EntityManagerFactory를 다음과 같이 JNDI 검색을 통한 빈 등록 기능을 이용해 넣으면 된다.

<jee:jndi-lookup id="emf" jndi-name="persistence/myPersistenceUnit"/>

jndi-name은 JNDI로 등록된 퍼시스턴스 유닛의 이름을 지정해주면 된다.

  • 장점
    • 기존에 JavaEE서버에서 사용되도록 개발된 JPA모듈을 그대로 활용할 수 있다는 것이다.
    • 서버가 제공해주는 JPA 기능을 최대한 이용할 수 있는 방법이기도 함

하지만 단지 컨테이너가 관리하는 EntityManager 방식을 사용하기 위해서라면 굳이 사용할 필요 없다.

스프링이 직접 제공해주는 컨테이너 관리 EntityManager를 사용할 수 있기 때문이다.

3. LocalContainerEntityManagerFactoryBean


LocalContainerEntityManagerFactoryBean은 스프링이 직접 제공하는 컨테이너 관리 EntityManager를 위한 EntityManagerFactory를 만들어준다.

  • 이 방법을 이용하면 JavaEE서버에 배치하지 않아도 컨테이너에서 동작하는 JPA의 기능을 활용할 수 있을 뿐만 아니라,
  • 스프링이 제공하는 일관성 있는 데이터 액세스 기술의 접근 방법을 적용할 수 있고 스프링의 JPA 확장 기능도 활용할 수 있다.

LocalContainerEntityManagerFactoryBean은 기본적으로 아래와 같이 등록해주면 된다.

필수 프로퍼티는 빈으로 등록된 dataSource를 지정해주는 것

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
	<property name="dataSource" ref="dataSource"/>
</bean>

이 빈은 META-INF/persistence.xml을 참고해서 퍼시스턴스 유닛과 이를 활용하는 EntityManagerFactory를 만든다. 이 때 DB연결정보는 persistence.xml에 프로퍼티로 등록하지 않아도 된다.

  • 대신 스프링에 빈으로 등록된 DataSource를 JPA에서 사용할 수 있다.
  • persistence.xml 파일은 퍼시스턴스 유닛 정보를 태그 안에 담고있다.
  • 퍼시스턴스 유닛에는 EntityManagerFactory와 EntityManager에 대한 설정과 JPA과 관리하는 엔티티 클래스 그리고 매핑정보를 넣을 수 있다.

META-INF/persistence.xml 파일의 예시

<?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="default">
				<class>springbook.learningtest.spring.jpa.Member</class> <!--RDB와 매핑 될 오브젝트인 엔티티 클래스들을 직접 지정가능-->
				<exclude-unlisted-classes /> <!--엔티티클래스를 자동스캔않도록 지정. 이 태그 제거시 클래스에서 @Entity 애노테이션이 붙은 모든 클래스를 자동 등록한다.-->
				<properties>
						<property name="eclipselink.waving" value="false" /> <!--바이트코드위빙 사용 않도록 설정 -->
				</properties>				
		</persistence-unit>
</persistence>

일부 프로퍼티 설정은 JPA 구현 제품마다 다름을 주의

자바오브젝트와 RDB 테이블 사이의 매핑과 변환을 위한 정보는 orm.xml과 같은 매핑 정보를 담은 파일에 정의하거나 애노테이션을 이용해 클래스 안에 정의 가능

@Entity
public class Member {
		@Id
		int id;

		@Column(length=100)
		String name;

		@Column(nullable=false)
		double point;

		// 수정자(setter), 접근자(getter)
		...
}

LocalContainerEntityManagerFactoryBean에서 사용할 수 있는 Property


persistenceUnitName

  • persistence.xml에는 하나 이상의 퍼시스턴스 유닛이 정의될 수 있음
  • persistence.xml에 다음과 같이 퍼시스턴스 유닛이 정의되어 있고, 이 설정을 따라 EntityManagerFactory 빈을 정의해야한다면 아래와 같이 추가
<!--persistence.xml-->
<persistence-unit name="subPersistenceUnit">

<!-- LocalContainerEntityManagerFactoryBean -->
<property name="persistenceUnitName" value="subPersistenceUnit" />

persistenceXmlLocation

  • LocalContainerEntityManagerFactoryBean은 디폴트 위치인 META-INF에서 persistence.xml 파일을 찾음
  • 일부 WAS에서는 META-INF/persistence.xml 파일을 자동으로 인식해서 서버가 관리하는 EntityManagerFactory와 충돌이 발생
  • 따라서 스프링이 제공하는 컨테이너 관리 EntityManager를 적용하고 동시에 JPA 프로바이더를 제공하는 WAS에 배포할 경우 디폴트 위치에 persistence.xml을 넣는건 피해야 함
  • persistenceXmlLocation의 프로퍼티에 파일 위치를 지정하여 변경
  • 가장 간단한 방법은 파일의 이름을 spring-persistence.xml과 같이 변경하는 방법
<property name="persistenceXmlLocation" value="META-INF/spring-persistence.xml" />

jpaProperties, jpaPropertyMap

  • EntityManagerFactory를 위한 프로퍼티를 지정할 때 사용
  • jpaProperties는 를 이용, jpaPropertyMap은 을 이용해 정보를 입력
<!--persistence.xml-->
<persistence-unit name="default">
		...
		<properties>
				<property name="eclipselink.waving" value="false" />
		<properties>
</persistence-unit>
  • persistence.xml 파일 대신 스프링의 LocalContainerEntityManagerFactoryBean 빈에 정의
<!--LocalContainerEntityManagerFactoryBean-->
<property name="jpaProperties">
		<props>
				<prop key="eclipselink.waving">false</prop>
		</props>
</property>

jpaVendorAdapter

  • JPA는 비슷한 기능이지만 벤더별로 다른 설정 프로퍼티를 사용해야 하는 경우가 많음
  • 구현 벤더별로 다르게 지정되는 프로퍼티나 설정을 JpaVendorAdapter를 이용하면 스프링이 정의한 표준 프로퍼티를 이용해 지정 가능
  • JPA는 코드상에서 데이터 액세스 로직을 작성할 때 자바오브젝트와 전용 쿼리 언어, Criteria 등을 이용
  • 이런 API와 오브젝트 쿼리 등을 DB가 이해할 수 있는 SQL 언어로 전환해서 처리
  • ORM 계층의 언어와 API가 아닌 DB SQL을 출력하고 싶을때 아래와 같이 옵션 설정
<property name="jpaVendorAdapter">
		<bean class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
				<property name="showSql" value="true" />
		</bean>
</property>
  • showSql이라는 프로퍼티는 true, false로 지정 가능
  • 벤더 어댑터에 맞는 설정으로 변환 (EclipseLink: eclipselink.logging.level=FINE, 등)
  • 나는 여기서 HibernateJpaVendorAdaptor로 변경했따.
  • 스프링 3.1에선 EclipseLink. Hibernate, OpenJpa, TopLink 네 가지 벤더를 위한 JpaVendorAdapter를 지원
  • JpaVendorAdapter를 이용하면 showSql뿐 아니라 매핑 DDL을 자동생성해주는 generatedDdl과 DB 종류인 database, DB 플랫폼 정보인 databasePlatform 등을 지정
  • 이 외에도 각 벤더의 JPA 구현에 따라 달라지는 다양한 정보를 EntityManagerFactory 생성을 위해 제공

loadtimeWeaver

  • JPA는 그래서 단순한 자바 코드로 만들어진 엔티티 클래스의 바이트코드를 직접 조작해서 확장된 기능을 추가하는 방식을 이용
    • 엔티티 오브젝트 사이에 지연된 로딩이 가능
    • 엔티티 값의 변화를 추적 가능
    • 최적화와 그룹 페칭(fetching) 등의 고급 기능을 적용 가능
  • 이미 컴파일된 클래스 바이트 코드를 조작해서 새로운 기능을 추가하는 것을 바이트코드 향상 기법이라고 함
        

트랜잭션 매니저

  • 컨테이너가 관리하는 EntityManager 방식에는 컨테이너가 제공하는 트랜잭션 매니저가 반드시 필요
  • 스프링의 EntityManager를 사용하려면 적절한 트랜잭션 매니저 등록이 필요
  • 스프링 JDBC는 트랜잭션 매니저가 없어도 동작, JDBC 자체가 자동 트랜잭션 모드를 갖고 있기 때문에 명시적으로 트랜잭션 관리를 안해도 됌
  • 반면 JPA는 반드시 트랜잭션 안에서 동작하도록 설계
  • LocalContainerEntityManagerFactoryBean을 통해 컨테이너 관리 EntityManger를 사용할 때는 트랜잭션 매니저를 다음과 같이 추가
<bean id="emf" class=
			"org.springframework.orm.jpa.localContainerEntityManagerFactoryBean">
		...
</bean>

<bean id="transactionManager"
			class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="emf" />
</bean>
  • JPA를 이용하는 DAO 코드는 스프링이 관리하는 트랜잭션 관리 기능을 이용 가능
  • @Transactional이나 트랜잭션 AOP를 이용해서 트랜잭션 경계설정을 해주면 자동으로 JPA 트랜잭션을 시작하고 커밋하도록 만듬
  • JpaTransactionManager를 사용하면 DataSource를 공유하는 JDBC DAO와 트랜잭션 공유 가능 (서비스 계층의 코드가 JPA DAO와 JDBC DAO를 하나의 트랜잭션 안에서 사용 가능)
  • JTA 트랜잭션을 이용하는 경우라면 JtaTransactionManager를 사용
profile
공유 문화를 지향하는 개발자입니다.

0개의 댓글