본문 바로가기

스프링

[JPA] ORM 정의

1.     Com.springsource.javax.activation : Spring java-mail

2.     Com.springsource.javax.mail : Spring java-mail

3.     Com.springsource.javax.persistence : spring JPA 지원을 위한 클래스 로더 필요

4.     Com.springsource.org.aopalliance : Spring ProxyFactoryBean

5.     Com.springsource.org.aspectj.tools : AspectJExpressionPointcut 포인트컷 표현식 지원

6.     Com.springsource.org.castor : OXM castor Framework

7.     Com.springsource.org.hsqldb : spring 내장형 DataBase

8.     Com.springsource.junit : junit

9.     Commons-logging : Spring-context가 사용

10.   Eclipselink.jar : 이클립스 JPA 구현제품 사용시 사용시

11.   Javax.persistence-api : 줄여서 JPA. JavaSE, EE를 사용하는 응용프로그램에서 관계형 데이터베이스의 관리를 표현하는 자바API

12.   Mail : java-mail

13.   Mockito : 목 프레임워크 중 Mockito

14.   Mysql-connector : Mysql JDBC

15.   Org.springframework.context.support : Spring java-mail

16.   Org.springframework.orm : spring orm

17.   Org.springframework.oxm : Spring OXM(Object XML Mapping) Jaxb .

18.   Spring-aop : 스프링 기능 자체의 aop, Spring ProxyFactoryBean

19.   Spring-bean : 스프링 코어와 함께 의존성 주입 제공 (Core Container)

20.   Spring-context : 스프링 코어, BeanFactory를 확장한 어플리케이션 컨텍스트 구현, 리소스 로드 및 국제화 지원(Core Container)

21.   Spring-core : 다른 스프링 모듈이 사용하는 유틸리티(Core Container)

22.   Spring-expression : EL 확장 Bean속성(배열, 컬렉션 포함).(Core Container)

23.   Spring-jdbc : 스프링이 지원하는 jdbc.

24.   Spring-test.jar

-       @RunWith : Junit 프레임워크의 테스트 실행방법을 확장시 사용.

-       SpringJUnit4ClassRunner : 어플리케이션컨텍스트를 만들고 관리하는 확장 클래스

-       @ContextConfiguration(경로) : 자동으로 만들어줄 어플리케이션 컨텍스트 설정파일

25.   Spring-tx : DuplicateKeyException.class 파일 존재 및 스프링 트랜잭션

 

* 관계형 DB는 성능이 매우 뛰어난 기술임에는 분명하지만 오브젝트를 중심으로 개발하는 자바 오브젝트와 텍스트 문장인 SQL을 직접 작성하고 파라미터를 바인딩해야 하는 부분이 번거롭다.

* ORM이란 이렇게 오브젝트를 RDB에 적절한 형태로 변환해주거나 RDB에 저장되어 있는 정보를 자바오브젝트가 다루기 쉬운 형태로 변환해주는 기술이다.

> JDO(Java Data Objects) : 엔티티빈(데이터를 객체화하여 재사용이 가능한 컴포넌트)처럼 특정 컨테이너에서 동작하는 오브젝트를 사용하는 대신 POJO를 사용하는 ORM 기술.

# 컴포넌트 : 자동차 엔진이 고장나면 엔진만 갈면된다. 소프트웨어에서도 한부분(ex:jdbc.jar)을 이야기함/

# POJO : 객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 오브젝트

> TopLink : 상용 ORM 프레임워크

> 하이버네이트 : 오픈소스 ORM으로 큰 인기를 끌던 제품.

> JPA : ORM전문가들이 대거 참여한 EJB3.0 스펙작업에서 기존 엔티티빈(DB에 데이터를 담는 그릇VO)JPA라는 이름으로 바꾸고 영속성 관리와 ORM기능을 제공하는 범용 ORM 기술로 발전시켰다. JavaEE, JavaSE 환경에서도 사용이 가능하며, 표준 기술이기 때문에 이를 준수하는 다양한 상용/오픈소스 제품이 등장하여 환경에 독립적인 ORM 프로그래밍이 가능해졌다. Java5부터. JavaEE,SE에서도 사용 가능한 ORM 표준 인터페이스라고 보면 됨

#영속성(Persistence) : 데이터를 생성한 프로그램이 종료되더라도 사라지지 않는 데이터, 메모리에 상주했다가 사라지는 데이터와 반대개념. 한마디로 파일

#많이 사용되는 대표적인 JPA : JBoss의 하이버네이트, 아파치의 OpenJPA, 이클립스의 EclipseLink, 오라클의 TopLink Essentials 등이 있다.

 

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

1) 컨터네이너가 관리하는 EntityManager

> JavaEE 환경과 서버 필요

2) 애플리케이션이 관리하는 EntityManager

> JavaEE, JavaSE 모두 사용 가능.

* 어떤 방식을 사용하든 EntityManagerFactory를 빈으로 등록해야한다.

 

LocalEntityManagerFactoryBean

> 자동 감지 기능을 이용해 프로바이더(제공자:하이버네이트 등)를 찾고 META/persistence.xml에 담긴 퍼시스턴스 유닛의 정보를 활용해서 EntityManagerFacotory를 생성한다.

> 이 방식은 JPA만을 사용하는 단순한 환경에 적용한다. 가장 큰 단점은 스프링의 빈으로 등록한 DataSource를 사용할 수 없으며, 스프링이 제공하는 바이트코드 위빙 기법도 적용할 수 없다.

> 굳이 사용하려면 스프링 기반의 독립형 애플리케이션이나 통합테스트 정도에서 사용할 수 있지만, 통합테스트에서도 굳이 제한이 많은 이 방법을 사용할 이유가 없다. 실전X

 

14. localContainerEntityManagerFactoryBean

> JPAJavaSE환경보다는 JavaEE에서 서버가 제공하는 JPA 프로바이더를 사용하는 것이 일반적이다. 위 빈은 스프링이 직접 제공하는 컨테이너 관리 EntityManagerFactory를 만들어준다. 이 방법을 사용하면 JavaEE 서버에 배치하지 않아도 컨테이너에서 동작하는 JPA의 기능을 활용할 수 있을 뿐만 아니라, 스프링이 제공하는 일관성 있는 데이터 엑세스 기술의 접근 방법을 적용할 수 있고 스프링의 JPA 확장 기능도 활용할 수 있다.

> 이 빈은 META-INF/persistence.xml을 참고해서 퍼시스턴스 유닛(연동할 데이터베이스당 1개의 세부정보를 그룹핑하여 설정을 정의한 단위)과 이를 활용하는 EntityManagerFactory를 만든다. 이 때 DB연결정보는 persistence.xml에 등록할 필요없이, 스프링에 등록된 DataSourceJPA에서 사용할 수 있다.

> 해당 빈은 디폴트 위치인 META-INF/persistence.xml 파일을 찾는다. 따라서 파일의 위치나 이름을 지정하지 않아도 된다. 하지만 일부 WAS에서는 META-INF/persistence.xml 파일을 자동으로 인식해서 서버가 관리하는 EntityManagerFactory를 만들어버린다. 이런 경우 스프링의 빈으로 등록되는 EntityManagerFactory와 충돌이 발생하므로 디폴트경로에 넣는 것은 피하는 것이 좋다. 따라서 persistenceXmlLocation을 사용한다.

16) persistence.xmlUnitName과 매핑

17) persistenceXmlLocation : 일부 WAS에서 자동생성으로 인한 충돌회피.

18) jpaProperties, jpaPropertyMap : EntityManagerFactory를 위한 프로퍼티 지정시 사용. jpaPropertiessms <props>를 사용하고, jpaPropertyMap<map>을 이용해 프로퍼티 정보를 넣을 수 있다. 이 프로퍼티 정보는 persistence.xml<properties> 태그로 정의한 프로퍼티와 함께 EntityManagerFactory를 생성할 때 사용된다. 위에서는 바이트코드 위빙을 사용하지 않겠다고 지정함.

21. JPA 참조구현으로 만들어진 EclipseLink 2.0 사용. jpaVendorAdapter

> JPA는 비슷한 기능이지만 구현 벤더별로 다른 설정 프로퍼티로 인해 jpaVendorAdapter를 이용하면 스프링이 정의한 표준 프로퍼티를 이용해 지정할 수 있다. JPA 벤더별로 만들어진 전용 어댑터를 쓰기만 하면된다.(표준 JPA 스펙이 모든 상세한 설정 방식과 프로퍼티 이름을 지정하고 있지 않기 때문에). 스프링은 현재 EclipseLink, Hibernate, OpenJpa, TopLink라는 네 가지 벤더를 위한 JpaVendorAdapter를 지원하고 있다.

24) 스프링의 LocalContainer~~FactoryBean에서는 showSql 프로퍼티 true를 통해 로그에 SQL을 출력할 수 있다. Eclipselink.logging.level=FINE hibernate.show_sql=true 프로퍼티 등 각 벤더에 따라 다르게 지정해야함. 그 외에 database, DB플랫폼 정보를 지정할 수 있는데, JpaVendorAdapter API를 참조할 것

25) generateDdl : DDL(create, alter, drop )을 자동생성해주는 옵션. 테이블을 @Enitity VO대로 삭제했다 새로 생성함. 운영환경에서는 절대 true로 하면 안됨. 기본값 false

39. transactionManager

> 컨테이너가 관리하는 EntityManager 방식에는 컨테이너가 제공하는 트랜잭션 매니저가 반드시 필요하다. 스프링 JDBC는 트랜잭션 매니저가 없어도 동작한다. JDBC자체가 자동 트랜잭션 모드를 갖고 있기 때문이다. 반면에 JPA는 반드시 트랜잭션 안에서 동작하도록 설계되어 있다.

> JPA DAOJDBC DAO를 하나의 트랜잭션 안에서 사용가능(공유가능)

> JPAJTA 트랜잭션을 이용하는 경우라면 JtaTransaction을 사용해야한다.

 

* 지연된 로딩 : 호출되지 않은 오브젝트가 호출시 조작되어 로딩

* 런타임 : 프로그램이 기동/실행되고 있을 때 존재하는 곳

 

 

 

7. persistenceUnitName : 퍼시스턴스 유닛의 이름을 지정하여 LocalContainer~FactoryBean에 프로퍼티를 추가하면 해당 유닛 설정을 읽는다. 단일 퍼시스턴스 유닛이 정의되어 있는 경우 지정하지 않거나 default를 사용한다.

 

Getter/setter

 

위 처럼 자바오브젝트와 RDB 사이의 매핑과 변환을 위한 정보를 xml에 정의할 수 있고, 애노테이션을 이용해 클래스안에 정의할 수도 있다.

 

JdbcTemplate이나 SqlMapClientTemplate와 동일하게 템플릿 방식으로 JPA 코드를 작성할 수 있게 해준다. 반복작업을 줄여주고, 예외변환 같은 편리한 기능을 제공해주는 장점이 있는 반면, 데이터 액세스 기술이 직접 제공하는 API를 사용하는 대신 템플릿의 메소드와 콜백을 사용하게 된다는 단점이 있다.

> JpaTemplate은 자주 사용되지 않는다. 스프링의 템플릿 방식이 편하게 느껴진다면 사용한다.

140. persist : insert역할

142. flush : 데이터베이스에 반영

 

54. doInJpa() : 해당 메소드는 파라미터로 EntityManager를 전달해준다. EntityManagerJPA의 핵심 인터페이스이므로 이를 통해 모든 JPA 기능을 사용할 수 있다.

55. SQL처럼 보이겠지만 SQL이 아니다. Member 엔티티를 사용해서 만든 오브젝트 쿼리다.

61,62. JpaTemplate는 콜백 오브젝트 없이도 대부분의 기능을 사용하게 해준다.

* 자세한 사항은 JpaTemplate API문서를 참조하자.

 

* JpaDaoSupport 클래스를 상속해서 DAO를 만들면 JpaTemplate를 생성하는 코드는 생략할 수 있다. jpaTemplate가 필요한 경우 getJapTemplate() 메소드를 이용하면 된다.

* 컨텍스트란? 어떤 루틴이 실행될 때의 변수값들

> 아래서 설명하겠지만, JpaTemplate를 사용하는 것이 JPA API를 직접 사용하는 방법보다 나은점은 데이터 액세스 예외 추상화 클래스 계층인 DataAccessException의 예외로 변환해 준다는 점이다.

> 반대로 아래에서 배울 JPA API 역시 런타임 예외를 발생시키기 때문에 불필요한 try/catchthrows 선언이 필요 없다.

 

컨테이너 대신 애플리케이션 코드가 관리하는 EntityManager 이용

이 방법은 JavaEE 환경과 JavaSE 환경에서 모두 사용 가능.

107. PersistenceContext : 가장 대표적인 방법. 컨테이너가 제공하는 EntityManager를 직접 제공받아서 사용. EntityManager는 스프링의 빈으로 등록되지 않는다. 빈으로 등록한 것은 EntityManagerFacoty 타입의 빈을 생성하는 LocalContainerEntityManagerFactoryBean이지 EntityManager 타입의 빈은 존재하지 않는다. 따라서 @Autowired와 같은 스프링의 DI 방법으로는 주입받을 수 없다.(createEntityManager 메소드를 통해 생성해야함) 하지만 스프링에서는 JPA 스펙에 나오는 EntityManager를 주입받는 방법을 스프링에서 해당 애노테이션 적용시 주입받을 수 있게 해놓았다.

* 이상한점

- EntityManager가 등록된 빈이 아니지만 PersistenceContext라는 특별한 애노테이션을 통해 제공 받는 것 까지는 이해하겠지만, E인스턴스 변수에 한번 주입받아서 계속 사용하는 것이 아니다. EntityManager는 멀티스레드에서 공유해서 사용할 수 없다.

> Connection을 하나 가져와서 DAO에서 계속 재사용할 수 없는 것과 마찬가지다. 스레드별로 독립적인 EntityManager가 만들어져 사용돼야 한다. 정확히는 JDBC DAO에서 트랜잭션마다 하나의 Connection이 만들어져서 트랜잭션 범위 안에서는 재사용되지만 해당 트랜잭션 밖에서는 다른 Connection이 만들어지는 것처럼, EntityManager도 트랜잭션마다 하나씩 만들어져서 사용되고 트랜잭션이 종료되면 함께 제거돼야 한다.

- 어떻게 인스턴스 변수에 한번 DI받아놓고 같은 오브젝트를 여러 스레드가 동시에 사용하게 하는 것일까?

> @PersistenceContext로 주입받는 EntityManager는 실제 EntityManager가 아니라 현재 진행 중인 트랜잭션에 연결되는 퍼시스턴스 컨텍스트를 갖는 일종의 프록시 이기 때문이다. 마치 하나의 오브젝트를 공유하는 듯하지만 사실은 각 스레드가 자신의 컨텍스트에 따라서 만들어진 독립적인 오브젝트를 사용하도록 연결해주는 기능을 가진 프록시를 이용하는 것이다.

106. TRANSACTION : EntityManager가 존재하는 프록시가 트랜잭션 범위이기 때문에 type 엘리먼트는 디폴트 값인 TRANSACTION이 적용된다.

> 이렇게하면 매번 EntityManager를 매번 생성하지도 않고, 스프링이 제공해주는 템플릿/콜백 방식 없이 코드가 간결해지면서 트랜잭션 경계설정과 동기화 기법을 적용한 것과 같은 혜택은 그대로 얻을 수 있다.

> JPA 스펙에 따르면 JTA를 통해 트랜잭션을 관리하게 되어있다. 하지만 스프링은 이를 스프링이 직접 관리하는 JpaTransactionManager를 통해 트랜잭션을 관리해주기 때문에 JavaEE 서버의 트랜잭션 매니저와 JTA가 없어도 JPA의 장점인 @PersistenceContext를 통한 EntityManager 주입 방식을 그대로 적용할 수 있다.

107. EXTENDED : 이렇게 하면 트랜잭션 스코프 대신 확장된 스코프를 갖는 EntityManager가 만들어진다. (JPA에서 이 확장된 퍼시스턴스 컨텍스트는 상태유지 세션빈에 바인딩 되는 것을 말한다.) 따라서 이 방식은 스프링의 싱글톤 빈에는 사용할 수 없다. 상태를 가진 세션빈이나 장기간 지속되는 스코프 빈에만 사용될 수 있다. @PersistenceContext에 타입 엘리먼트를 지정하면 확장된 퍼시스턴스 컨텍스트를 갖는 EntityManager를 주입해준다. EntityManager는 트랜잭션 스코프의 퍼시스턴스 컨텍스트에서 사용되는 EntityManager처럼 멀티스레드에서 안전하지 않은 실제 EntityManager. 확장된 퍼시스턴스 컨텍스트는 싱글톤 빈에 적용하면 안된다. 상태를 가진 세션빈은 하나 이상의 오브젝트로 만들어지기 때문에 각각 독립적으로 EntityManager를 가질 수 있기 때문에 스코프 프록시를 사용할 필요가 없다.

* 상태유지 세션빈 : 사용자별로 독립적이며 장기간 보존되는 오브젝트.

109. PersistenceUnit : 스프링의 DI방식 대신 JPA 표준 스펙에 나온 방식.(EJB와 비슷). 이 애노테이션을 사용하면 EntityManagerFactory를 주입받는 코드를 만들 수 있다. 이렇게 하면 스프링프레임워크에 대한 의존도가 전혀 없는 순수한 JPA 코드가 된다. (아래 스샷참조)

111. AutowiredResource 또는 일반적인 스프링의 DI 방식으로 DAO로 가져와 사용가능. 이것도 PersistenceUnit과 마찬가지로 스프링의 의존도가 전혀 없는 순수한 JPA코드

 

@PersistenceUnit 을 통해 EntityManagerFactory가 주입되려면 애노테이션을 이용한 의존관계설정이 가능한 컨테이너이거나, 스프링일 경우 context:annotation-config 설정이 반드시 포함되어 있어야 한다.

38. context:annotation-config에 의해 자동등록되는 PersistenceAnnotationBeanPostProcessor 후처리기가 @PersistenceUnit 애노테이션에 대한 DI 작업을 담당한다.

 

 

@PersistenceUnit으로 주입받은 EntityManager의 사용방법. 컨테이너가 관리하지 않는 EntityManager이므로 트랜잭션은 직접 begin()하고 commit()을 해줘야 한다.

76. createEntityManager() 메소드를 통해 EntityManager를 생성해서 사용한다.

* 위와 같은 방법은 실제로는 자주 쓰지 않는다. 매번 코드에 의해 EntityManager가 생성되고 트랜잭션도 코드에 의해 관리되므로 매우 번거롭다.

> EntityManagerFactoryEntityManager를 생성해서 데이터 액세스 로직을 작성하기보다는 EntityManagerFactory 인터페이스에서 제공하는 QueryBuilder, Metamodel, PersistenceUnitUtil을 가져오거나 캐시나 프로퍼티 정보를 참조할 필요가 있을 때 사용하는 것이 바람직하다.

> EntityManagerFactory 오브젝트는 멀티스레드 환경에서 안전하게 공유해서 쓸 수 있다.

 

* JPA 예외변환 AOP PersistenceExceptionTranslationPostProcessor

> JpaTemplate를 사용하지 않고 JPA API를 직접 이용하는 경우에도 JPA 예외를 스프링의 DataAccessException 예외로 전환시킬 수는 있다. 스프링의 AOP를 이용하면 된다.

> 위 빈을 등록하면 @Repository 애노테이션이 붙은 빈을 찾아서 예외 변환 기능을 가진 AOP 어드바이스를 적용해주는 후처리기이다. AOP의 자동 프록시 생성기와 같은 방식이라고 생각하면 된다.

> @Repository 애노테이션을 등록하고 위 빈을 등록하면 JPA API를 사용하는 DAO에서 발생하는 예외가 스프링의 DataAccessException으로 전환돼서 서비스 계층을 던져질 것이다.

 

@Repository

> 먼저 예외 변환이 필요한 DAO 클래스에 @Repository 애노테이션을 부여한다. @Repository@Componenet와 같은 자동인식을 위한 스테레오타입 애노테이션이기도 하다. 빈 스캐너를 사용하지 않은 경우에도 @Repository 애노테이션을 DAO에 사용할 수 있다.

> @Repository가 붙은 DAO 클래스의 메소드는 AOP를 이용한 예외 변환 기능이 부가될 빈으로 선정된다. @Transaction처럼 애노테이션을 이용한 포인트컷을 사용하기 때문이다.

127~128. Persist : 기본키를 동일하게 인서트 시키면 스프링에서 정의한 Exception이 나겠지만 @PersistenceContext를 이용한 JPA API를 사용했기 때문에 스프링에러가 안나지만 스프링 AOP를 적용했으므로 Spring 에러가 난다.

 

그렇다면 JPA에서 발생하는 예외가 JDBC를 사용했을 때처럼 다양한 DataAccessException의 서브클래스로 매핑되어서 던져지리라고 기대할 수 있을까? 안타깝지만 그렇지 못하다.

> JPA의 예외 자체가 분류하기 힘든 예외로 던져지기 때문에 이를 적절히 해석해서 변환하기 어렵다. Jdbc처럼 기본키 중복에러의 경우 DuplicateException을 던지지만 JPA는 한가지인JpaSystemException으로 전환될 뿐이다. JPA 스펙이 정의하는 예외는 제한적이다. 스프링은 이렇게 한가지 예외로 포장된 세부 원인을 분석해 변환해주는 기능까지는 제공하지 못한다.

> 그렇다고 스프링의 DataAccessException 계층으로 변환하는 일이 쓸모없는 것은 아니다. 사실 대부분의 DB예외는 복구가 불가능하다. 반면에 JPA가 정의한 OptimisticLockException은 복구하기 적당한 예외다. 해당 예외는 하이버네이트, JDO에서와 동일하게 예외 변환을 통해 스프링의 OptimisticLockingFailureException으로 변환된다.

> 기술에 독립적인 인터페이스와 예외를 사용해서 DAO 클라이언트를 작성하고, 구체적인 데이터 액세스 기술은 자유롭게 변경해서 사용할 수 있게 하는 것이 스프링이 지향하는 목표다. 따라서 하이버네이트,JDO로 변환할 수 도 있다는 전제하에 스프링의 예외로 변경하는 건 가치 있는 작업이다. 따라서 JPA API를 직접 사용하는 DAO에는 @RepositoryPersis~Processor를 이용한 예외 변환 AOP를 반드시 적용하자.