Developer: J2EE
  DOWNLOAD
Oracle TopLink
Oracle JDeveloper 10g (10.1.3)
Spring Framework
Sample Code
  TAGS
opensource, java, All

Spring 프레임워크와 함께 Oracle TopLink 사용하기


저자 Lonneke Dikmans, Oracle Fusion Middleware Regional Director

Oracle JDeveloper를 이용하여 Spring 1.2.8과 Oracle TopLink 기반의 샘플 애플리케이션을 개발하는 방법을 단계별로 설명합니다.

게시일: 2006년 9월

경량형 J2EE 애플리케이션의 개발을 위한 인기 있는 프레임워크인 Spring은, Aspect Oriented Programming(AOP)와 종속성 삽입(dependency injection)의 개념을 코어에 구현하고 있습니다. Spring은 Oracle TopLink, JBoss Hibernate등의 오브젝트-관계형 매핑(ORM) 툴을 포함하는 다양한 프레임워크를 지원합니다. 본 문서는 Spring Framework의 TopLink 지원 기능을 활용하는 방법을 설명하고 있습니다. (OTN의 오라클/Spring 페이지에 방문하시려면 이곳을 누르십시오.)

Spring은 매우 다양한 혜택을 제공합니다:

  • DAO 패턴을 사용하여 데이터 액세스 코드를 비즈니스 로직으로부터 추상화합니다.
  • Template 패턴을 사용하여 데이터 접근 방식의 일관성을 구현합니다.
  • 공통 예외 계위(common exception hierarchy)를 구현하고 있습니다.
  • 통합 트랜잭션 관리 기능을 제공합니다.
  • 리소스 관리 기능을 제공합니다.

Spring을 이용하면 기반 ORM 툴을 변경할 필요가 없습니다. 따라서 이전과 마찬가지 방법으로 애플리케이션을 디자인하고 코딩하는 것이 가능합니다.

본 문서를 통해 Spring 1.2.8, TopLink, Oracle JDeveloper 10.1.3을 이용한 샘플 애플리케이션을 구현해 보겠습니다. 첫 번째로, 워크스페이스를 셋업하고 샘플 애플리케이션을 살펴 봅니다. 그런 다음, 데이터 액세스 코드와 Spring 설정에 대해 설명합니다. 코드의 테스트가 완료되면 애플리케이션을 Oracle Containers for Java (OC4J) 런타임에 적용하실 수 있을 것입니다.

워크스페이스의 설정

애플리케이션을 생성하려면, 먼저, TopLink, EJB, JavaServer Faces(JSF) 등을 이용하여 웹 애플리케이션 템플릿을 기반으로 작업을 시작해야 합니다. 먼저 샘플 애플리케이션을 다운로드하여 압축을 풀고 JDeveloper에 임포트합니다 (그림 1 참고). :

  1. 미리 생성한 폴더에 zip 파일의 압축을 풉니다.
  2. 초록색 플러스 기호를 클릭하여 기존 애플리케이션을 임포트합니다.
  3. zip 파일의 압축을 푼 폴더로 이동합니다.
  4. HockeyCoachAssistant.jws 파일을 더블클릭 합니다.

Figure 1
그림 1. 기존 애플리케이션의 임포트.

애플리케이션의 임포트 작업을 완료했다면, Spring Framework의 사용에 필요한 라이브러리를 추가하고 애플리케이션을 테스트합니다. 위의 예에서는 웹 애플리케이션 템플릿을 사용하고 있기 때문에, TopLink와 JSF를 위한 라이브러리가 이미 Model, ViewController 프로젝트에 추가되었습니다. 표 1은 추가해야 할 라이브러리를 정리하고 있습니다.

표 1. 각 프로젝트별로 필요한 라이브러리.

Project

Library Name

Jar Location

Remark

Model

spring128

class:[springhome]\dist\spring-dao.jar, spring.jar, spring-orm.jar

src: [springhome]\src

doc: [springhome]\docs\api

www.springframework.org

TestModel

Spring128Test

class:[springhome]\dist\spring-mock.jar

src: [springhome]\src

doc: [springhome]\docs\api

www.springframework.org

TestModel

Spring128

Model 프로젝트 참고

Model 프로젝트 참고

TestModel

JUnit Runtime

JDeveloper를 위한 익스텐션 다운로드

JDeveloper 익스텐션 센터의 익스텐션 사용

TestModel

dbunit

Class: [dbunithome]\dbunit2.1.jar

www.dbunit.org

ViewController

Spring128

Model 프로젝트 참고

Model 프로젝트 참고

이제 Model 프로젝트를 위한 라이브러리 추가 작업부터 시작해 보겠습니다:

  1. Spring을 라이브러리로 추가합니다.
    • Spring Framework 1.2.8을 다운로드합니다.
    • Spring128이라는 이름의 사용자 정의 라이브러리를 생성합니다.
    • 클래스, 소스, 문서 위치를 추가합니다.

이제 Model 프로젝트가 그림 2와 같이 구성되었습니다.

Figure 2
그림 2. Model 프로젝트의 컴파일/실행에 필요한 라이브러리.

다음으로 TestModel 프로젝트에 라이브러리를 추가합니다:

  1. Spring128 라이브러리를 추가합니다.
  2. spring-mock 라이브러리를 추가합니다.
    • Spring128Test라는 이름의 사용자 정의 라이브러리를 생성합니다.
    • spring-mock-jar를 클래스에 추가합니다.
  3. JUnit 라이브러리를 추가합니다.
    • Help 메뉴에서 Check for Update를 사용하여 JUnit 런타임을 설치합니다.
    • JUnit Runtime 라이브러리를 프로젝트에 추가합니다.
  4. DbUnit 라이브러리를 추가합니다.
    • www.dbunit.org에서 DbUnit을 다운로드합니다.
    • dbunit라는 이름의 사용자 정의 라이브러리를 생성합니다.
    • 클래스 경로에 dbunit2.1.jar 파일을 추가합니다.
    • 추가적으로, 소스를 다운로드하여 라이브러리에 추가할 수도 있습니다.

이제 TestModel 프로젝트가 그림 3과 같이 구성되었습니다.

Figure 3
그림 3. TestModel 프로젝트의 컴파일/실행에 필요한 라이브러리.

마지막으로, ViewController 프로젝트에 필요한 라이브러리를 추가합니다:

  1. 모델 프로젝트를 위해 생성한 spring128 라이브러리를 추가합니다.

변경된 ViewController 프로젝트가 그림 4와 같습니다:

Figure 4
그림 4. Libraries needed to compile and run the ViewController project.

샘플 애플리케이션

지금까지 워크스페이스를 설정해 보았습니다. 이제 HockeyCoachAssistant 애플리케이션에 대해 본격적으로 작업할 수 있게 되었습닏. 이 프로그램은 필드 하키 경기의 팀 기록을 관리하는 간단한 프로그램입니다. 사용자는 경기에 참여하는 팀을 추가, 삭제, 편집할 수 있습니다.

사용자 인터페이스는 JSP(Java Server Pages)와 ADF Faces 컴포넌트로 구성된 웹 애플리케이션으로 구현됩니다. 시작 페이지는 teams.jsp입니다(그림 5 참고).

Figure 5
그림 5. faces-config.xml에 정의된 애플리케이션 내비게이션.

애플리케이션은 모델, 데이터 액세스(DAO), 서비스, 뷰의 4 개 계층으로 구성되어 있습니다(그림 6 참고). 본 문서에서는 TopLink를 위한 Spring 지원 기능이 사용되는 DAO 계층에 초점을 맞추기로 합니다.

Figure 6
그림 6. HockeyCoachAssistant 애플리케이션의 논리적 뷰.

애플리케이션의 설치

HockeyCoachAssistant 애플리케이션에서 TopLink 지원 기능이 어떻게 활용되는지 확인하기 위해, 데이터베이스 연결과 스키마, 테이블을 생성해 보겠습니다.

  1. Model 프로젝트의 Database 폴더에 위치한 SQL 스크립트를 실행하여 데이터베이스 스키마를 생성합니다.
    • SQLPlus, SQLDeveloper와 같은 툴을 이용하여 compUser.sql 파일을 실행하여 “comp”라는 이름의 사용자를 생성합니다. System, 또는 계정 생성 권한을 가진 다른 사용자로 데이터베이스에 로그인 했는지 확인합니다.
    • “comp” 사용자로 로그인해서 createTable.sql 스크립트를 실행합니다.
  2. JDeveloper의 Connection 탭을 사용해서 데이터베이스 연결을 생성합니다.
    • Competition이라는 이름의 새로운 데이터베이스 연결을 생성합니다.
    • 사용자이름(“comp”)과 패스워드(“comp”)를 입력합니다.
    • 데이터베이스 연결을 위한 SID 값을 입력합니다.
  3. sessions.xml의 연결 설정을 수정합니다(그림 7 참고).
    • Model 프로젝트의 src\META-INF 폴더에서 sessions.xml 파일을 엽니다.
    • Structure 창에서 localSession이라는 이름의 세션을 더블클릭합니다.
    • Login을 클릭합니다.
    • 데이터베이스에 연결하기 위한 JDBC 연결 URL을 업데이트합니다.
    • 필요한 경우, 데이터베이스 플랫폼을 업데이트합니다.

    Figure 7
    그림 7. localSession을 위한 URL의 수정.

  4. competitionMap의 연결 설정을 수정합니다(그림 7 참고).
    • TopLink 맵을 클릭합니다.
    • connection URL을 편집합니다.
    • 필요한 경우, 데이터베이스 플랫폼을 업데이트합니다.

    Figure 8
    그림 8. competitionMap의 수정.

  5. TestModel 프로젝트의 test-toplink-context.xml 파일을 편집합니다.
    • 파일을 더블클릭합니다.
    • 데이터베이스 세팅을 로컬 설치 환경에 맞게 변경합니다(그림 9 참조).

    Figure 9
    그림 9. test-context.xml의 JDBC 연결을 위한 URL 수정.

  6. 설치 환경을 점검합니다.
    • 워크스페이스를 리빌드 합니다.
    • TopLink Mapping Status Report를 생성합니다.
    • CompetitionDaoTopLinkTest에서 테스트를 수행하고 모두 org.springframework.beans.factory.BeanCreationException 익셉션이 발생하는지 확인합니다(그림 10 참고).

    Figure 10
    그림 10. CompetitionDao 인터페이스를 구현하기 전에 확인되는 에러 메시지.

    이제 CompetitionDao 클래스를 구현할 준비가 완료되었습니다.

데이터 액세스 계층

Spring의 데이터 액세스 계층 지원은 크게 두 부분으로 나뉘어집니다:

  • J2EE 애플리케이션의 데이터 액세스를 위한 공통 엘리먼트(트랜잭션 관리, 리소스 관리, 익셉션 처리 등)를 처리하는 Template 클래스
  • 특정 애플리케이션에 관련한 세부 사항을 구현하기 위한 콜백(callback)

위 기능은 TopLink, Hibernate, JDBC, IBATIS, JDO등을 포함하는 모든 Spring 데이터 액세스 지원 환경에서 공통적으로 적용됩니다. TopLinkTemplate 클래스를 사용하는 방법에도 두 가지가 있습니다. Application Context를 이용하여 애플리케이션의 모든 DAO 클래스에 클래스를 삽입(inject)하는 방법이 그 하나이고, 또 다른 방법으로 지원 클래스를 확장할 수 있습니다.

본 문서의 예제에서는 TopLinkDaoSupport 클래스를 확장하는 방법을 사용합니다. 그 결과로 생성된 데이터 액세스 컴포넌트의 구조가 그림 11과 같습니다:

Figure 11
그림 11. 애플리케이션의 데이터 액세스 계층 구조.

TopLinkCompetitionDao(그림 11)는 서비스 계층을 통해 노출되는 CompetitionDao 인터페이스를 구현합니다. 이와 같은 방법으로 데이터 액세스 구현의 세부 사항을 애플리케이션의 다른 부분으로부터 숨길 수 있습니다.

  1. TopLinkCompetitionDao 클래스를 편집합니다(그림 12 참조):
    • TopLinkDaoSupport 클래스를 확장합니다.
    • springframework.orm.toplink.support.TopLinkDaoSupport 클래스를 임포트합니다.

Figure 12
그림 12. TopLinkCompetitionDao 클래스.

데이터의 인출. 첫 번째 페이지에 데이터를 채우기 위해, findAllTeams 메소드를 이용하여 데이터베이스에 저장된 모든 팀에 대한 정보를 가져옵니다. Spring을 이용하지 않고 코드를 구현하였다면(예를 들어 “stateless” 세션 빈 또는 “plain” Java 클래스를 이용한 경우), 코드는 Listing 1과 같이 구현됩니다:

Listing 1. Spring을 이용하지 않는 환경의 데이터 인출(세션 관리 기능이 코드에 포함되어 있음)
 public List<Team> findAllTeams() {
    List<Team> result = null;
    
    Session session = getSessionFactory().acquireSession();
    result = (List<Team>)session.executeQuery("findAllTeam", Team.class);
      session.release();      

    return result;
  }

Spring 환경에서는 Spring 템플릿이 리소스 관리를 담당하기 때문에, TopLinkCompetitionDao 클래스에서는 executeQuer에 대한 호출 부분만 구현해 주면 됩니다. 이 세션을 요청하고 해제하는 부분은 Spring이 알아서 수행합니다. (Listing 2 참고)

Listing 2. Spring을 이용한 데이터 인출.리소스는 Spring에 의해 관리됨.
 public List<Team> findAllTeams() {
    return (List<Team>)getTopLinkTemplate().execute(
      new TopLinkCallback(){
        public Object doInTopLink(Session session) {
           return session.executeQuery("findAllTeam", Team.class);
             }
         });
    }

물론 TopLink 맵에 정의된 매개변수나 네임드 쿼리를 추가하는 것도 가능합니다.

어차피 여기서는 모든 팀의 정보를 가져와야 하므로, Template에서 편의적으로 제공하는 메소드를 그냥 사용하기로 합니다(Listing 3 참고).

Listing 3. Spring이 제공하는 메소드를 이용한 대안.
public List findAllTeams() {
     return getTopLinkTemplate().readAll(Team.class);
}
  1. TopLinkCompetitionDao 클래스를 편집하여 findAllTeams를 구현합니다.
    • “return null” 구문을 삭제합니다.
    • TopLinkTemplate.readAll(…) 메소드를 사용하는 코드를 추가합니다.
  2. CompetitionDaoTopLinkTest 클래스를 실행하고 testGetAllTeams 테스트가 이제 에러 없이 성공적으로 실행되는지 확인합니다.

findAllClubs에도 동일한 방법이 적용됩니다(Listing 4 참고):

Listing 4. Spring이용한 FindAllClubs() 구현.
public List<Club> findAllClubs(){
   return getTopLinkTemplate().readAll(Club.class);
}

다음으로,

  1. TopLinkCompetitionDao 클래스를 편집하여 findAllClubs를 구현합니다(Listing 4 참고).
    • “return null” 구문을 삭제합니다.
    • TopLinkTemplate.readAll(…) 메소드를 사용하는 코드를 추가합니다.
  2. CompetitionDaoTopLinkTest 클래스를 실행하고 testFindAllClubs 테스트가 이제 에러 없이 성공적으로 실행되는지 확인합니다.

데이터의 삽입.사용자가 새로운 팀을 클럽에 추가할 수 있도록 하기 위해, saveTeam(Team)을 구현해 봅시다. Spring이 사용되지 않는 환경에서 구현되는 코드가 Listing 5와 같습니다:

Listing 5. Spring을 사용하지 않은 saveTeam 구현.
 public void saveTeam(Team entity) {
    UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
    uow.registerNewObject(entity);
    uow.commit();

  }

위의 예에서는, 리소스를 확보하고 트랜잭션을 관리하기 위한 프로세스가 코드 안에 임베드되어 있습니다.

Spring에서는 AOP를 이용하여 애플리케이션 컨텍스트 내에서 트랜잭션을 선언합니다. 그 방법이 Listing 6과 같습니다:

Listing 6. Spring이용한 saveTeam 구현.
 public void saveTeam(Team team) {
    getTopLinkTemplate().merge(team);
 }

  1. TopLinkCompetitionDao 클래스를 편집기에서 열고 TopLinkTemplate의 merge(..) 메소드를 이용하여 saveTeam 메소드를 구현합니다.
  2. CompetitionDaoTopLinkTest 클래스를 실행하고 testSaveTeam 테스트가 성공적으로 실행되는지 확인합니다.

이제 deepMerge, shallowMerge, mergeWithReferences중의 하나를 선택할 수 있습니다. 어느 것을 선택할 것인가는 관련된 오브젝트를 이용하여 어떤 작업이 수행되는가에 따라 달라집니다. 이 세 가지 메소드는 UnitOfWork API의 메소드들과 동일한 방식으로 동작합니다.

데이터의 업데이트.사용자는 주석(remark)을 추가하거나 변경하는 방식으로 팀을 업데이트할 수 있습니다. Spring이 사용되지 않는 환경에서 구현되는 코드가 Listing 7와 같습니다.:

Listing 7. 팀 업데이트를 위한 코드. Spring을 이용하지 않은 경우.
 public void updateTeam(Team entity) {
   UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
   Object workingCopy = uow.readObject(entity);
   if (workingCopy == null){
      throw new RuntimeException("Couldn뭪 find team to update");
    }
    uow.shallowMergeClone(entity)
    uow.commit();
  }

Spring을 이용하여 구현된 코드가 Listing 8와 같습니다.

Listing 8. Spring을 이용한 팀 업데이트.
public void updateTeam(Team team) {
   getTopLinkTemplate().shallowMerge(team);        
}

변경 작업을 수행하기 전에 먼저 병합된 엔티티를 세션에 등록하는 것이 중요합니다. 그렇게 하지 않는 경우 Listing 8의 메소드 호출 시 익셉션이 발생됩니다. 따라서 사용자가 편집할 팀을 선택하는 시점에, Listing 9와 같이 loadTeam(…)을 호출하여 읽기 작업을 수행해야 합니다.

Listing 9. 세션에 등록하기 위해 팀을 로드.
public Team loadTeam(Long teamId) {
  Team t = null;
  try {
   t = (Team)getTopLinkTemplate().readAndCopy(Team.class, teamId);
   } catch (ObjectRetrievalFailureException e) {
    	if (logger.isDebugEnabled()) {
        logger.debug("team with id: " + teamId + "is not found");
            }
    }
    return t;
}
  1. TopLinkCompetitionDao 클래스를 편집하여 updateTeam 메소드를 구현합니다. TopLinkTemplate의 shallowMerge(..) 메소드를 사용합니다(Listing 8 참고).
  2. TopLinkCompetitionDao 클래스를 편집기에서 열고 TopLinkTemplate의 readAndCopy(..) 메소드를 이용하여 loadTeam 메소드를 구현합니다.
  3. CompetitionDaoTopLinkTest 클래스를 실행하고 testUpdateTeam과 testLoadTeam에 대한 테스트가 성공적으로 실행되는지 확인합니다.

데이터의 삭제.이번에는 팀을 삭제하기 위한 deleteTeam(Team)을 구현해 보겠습니다. Spring을 사용하지 않는 경우의 코드가 Listing 10과 같습니다.

Listing 10. 팀의 삭제. Spring을 사용하지 않은 경우.
  public void deleteTeam(Team entity) {
   UnitOfWork uow = getSessionFactory().acquireUnitOfWork();
   Object workingCopy = uow.readObject(entity);
   if (workingCopy == null){
     throw new RuntimeException("Could not find team to delete");
   }
   uow.deleteObject(workingCopy);
   uow.commit();
  }

TopLinkTemplate을 이용하여 구현한 코드가 Listing 11과 같습니다:

Listing 11. TopLinkTemplate를 이용하여 팀을 삭제
public void deleteTeam(Team team) {
   getTopLinkTemplate().delete(team);
}
  1. TopLinkCompetitionDao 클래스를 편집기에서 열고 TopLinkTemplate의 delete(..) 메소드를 이용하여 deleteTeam 메소드를 구현합니다.
  2. CompetitionDaoTopLinkTest 클래스를 실행하고 testDeleteTeam 테스트가 성공적으로 실행되는지 확인합니다.

모든 테스트가 성공함으로써 TopLinkCompetitionDao 클래스의 구현 작업을 완료하였습니다. 이제 CompetitionDaoTopLinkTest 클래스의 구현 방법을 살펴 보기로 합시다.

리소스와 트랜잭션의 설정.지금까지 DAO 클래스에 존재하는 리소스 관리, 트랜잭션 관리를 위한 코드를 모두 제거하였습니다. 다음으로 Spring 설정 파일을 수정해 보겠습니다. 편집할 파일은 ApplicationContext.xml입니다.

  1. applicationContext.xml을 엽니다. 이 파일은 WEB-INF의 ViewController 프로젝트에 위치하고 있습니다(그림 13 참고).

    Figure 13
    그림 13. ViewController 프로젝트의 applicationContext.

    위의 예에서, 컨테이너는 트랜잭션과 리소스를 처리합니다. 이제 Spring 컨텍스트가 JtaTransactionManager를 사용하도록 설정합니다. 이와 같이 설정하는 경우 TopLink 세션 역시 그에 맞추어 설정을 변경해 주어야 합니다(그림 14 참고). TopLink 세션의 데이터 소스를 변경하고 외부 트랜잭션 컨트롤러를 사용하도록 변경해 주어야 합니다(OTN의 이전 아티클 “What Is the Spring TopLink Integration?”(영문)”을 참고하시기 바랍니다).

  2. (Model 프로젝트에 있는) sessions.xml의 competitionSession을 점검하여 세션이 External Transaction Controller를 사용하도록 설정되어 있는지 확인합니다.

    Figure 14
    그림 14. JTA 트랜잭션을 사용하도록 설정을 변경..

  3. applicationContext.xml에 Listing 12의 코드를 추가하여 JtaTransactionManager를 추가합니다: Listing 12. applicationContext.xml에서 트랜잭션 매니저를 정의하는 코드 .
    <!-- JTA transaction manager for J2EE environments --> 
    <bean name="myTransactionManager" 
    class="org.springframework.transaction.jta.JtaTransactionManager"/>
                

    데이터 소스는 JNDI를 사용하는 컨테이너로부터 가져 옵니다. 이를 위해 JndiObjectFactoryBean클래스의 빈(bean)이 정의됩니다.

  4. 데이터 소스를 applicationContext.xml에 추가합니다(Listing 13 참고): Listing 13. JNDI 네임을 이용하여 데이터소스 룩업을 수행하는 코드.
    <!-- JNDI DataSource for J2EE environments -->
    <bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
      <property name="jndiName" value="jdbc/competitionDS"/>
    </bean>
    

    데이터 소스와 세션이 모두 LocalSessionFactoryBean에 정의되었습니다.

  5. applicationContext.xml에 applicationContext.xml을 추가합니다l: Listing 14. Sessionfactory의 설정.
    <!-- the sessionfactory -->
    <bean name="sessionFactory" 
    class="org.springframework.orm.toplink.LocalSessionFactoryBean">
     <property name="configLocation">
    <value>sessions.xml</value>
      </property>
      <property name="sessionName">
    <value>competitionSession</value>
      </property>
      <property name="dataSource" ref="dataSource"/>
      <property name="sessionLog">
      	<bean class=
    "org.springframework.orm.toplink.support.CommonsLoggingSessionLog"/>
      </property>
    </bean>
    

    세션 팩토리(session factory)가 TopLinkCompetitionDao에 삽입(inject)되었습니다.

  6. Listing 15의 코드를 추가하여competionDao 빈을 정의하고 세션 팩토리를 설정합니다: Listing 15. DAO 클래스의 설정.
    <!--data access class with session factory -->
    <bean name="competitionDao" 
    class="com.xebia.demo.hockey.dao.toplink.TopLinkCompetitionDao">
          <property name="sessionFactory">
    		<ref bean="sessionFactory"/>
    	</property>
    </bean>
    

    Competition 서비스가 컨텍스트 안에 함께 정의되었습니다. Listing 15에서 정의한 competitionDao는 세터 삽입(setter injection)을 이용하여 서비스에 삽입됩니다

  7. 아래 정의를 applicationContext.xml에 추가하여 competitionService를 정의합니다: Listing 16. competition 서비스의 정의.
    <!-- the service class that is injected when 
    the JSF pages call competitionService -->
    <bean name="competitionServiceTarget" 
          class="com.xebia.demo.hockey.service.impl.CompetitionServiceImpl" >
      <property name="competitionDao">
    <ref bean="competitionDao"/>
      </property>
    </bean>
    

    AOP를 이용하여 CompetitionService의 모든 메소드에 대해 트랜잭션이 정의됩니다. 이때 CompetitionService를 가로채고 와일드카드 “*”를 이용하여 각 메소드에 대해 새로운 트랜잭션을 적용하게 됩니다(Listing 17 참고).

  8. 아래 코드를 applicationContext.xml에 추가하여 인터셉터(interceptor)를 정의합니다: Listing 17. AOP를 이용한 트랜잭션의 정의.
    <!-- the bean that intercepts the competitionService to wrap a transaction 
    around the methods -->
    <bean id="competitionService" 
    class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        	<property name="transactionManager">
              <ref bean="myTransactionManager"/>
        	</property>
        	<property name="target">
              <ref bean="competitionServiceTarget"/>
        	</property>
        	<property name="transactionAttributes">
          	<props>
                   <prop key="*">PROPAGATION_REQUIRED</prop>
          	</props>
        	</property>
     </bean>
    

    완성된 설정 파일이 그림 15와 같습니다.

    Figure 15
    그림 15. 완성된 applicationContext.xml파일.

코드의 테스트

Now it’s time to examine the test code. 그림 16은 테스트의 클래스 메소드를 보여 주고 있습니다. TopLinkCompetitionDao 클래스의 모든 메소드를 위한 테스트 메소드가 존재함을 확인할 수 있습니다. 테스트는 오라클 데이터베이스를 대상으로 실행됩니다. 아래 예에서, 필자는 XML 파일의 데이터를 초기화하기 위해 DbUnit을 사용하였습니다. 또 테스트 케이스를 작성하고 결과를 검증하기 위해 JUnit을 사용하였습니다. AbstractTransactionalDataSourceSpringContextTests 클래스는 테스트가 트랜잭션 모드로 실행되고 완료된 후 롤백 처리됨을 보장합니다. 이 테스트에서, 필자는 jdbcTemplate을 이용하여 데이터와 로우 카운트를 확인하고 있습니다.

Figure 16
그림 16. 테스트 코드를 위한 Class 모델.

테스트 환경에서는 Spring이 조금 다르게 설정됩니다. AbstractDaoTest는 getConfigLocations() 메소드의 어느 위치에 Spring 설정이 저장되어 있는지 정의하고 있습니다. 그 위치는 test-config.xml파일입니다. applicationContext와 비교했을 때 가장 큰 차이점이 아래와 같습니다:

  • org.springframework.jdbc.datasource.DriverManagerDataSource를 사용하고 있습니다.
  • JtaTransactionManager대신 TopLinkTransactionManager를 사용하고 있습니다.
  • AbstractTransactionalDataSourceSpringContextTests 클래스에 의해 트랜잭션이 처리되고 있기 때문에 별도로 트랜잭션 경계가 정의되지 않았습니다.

이러한 접근법을 사용하여 테스트 코드를 매우 간단하게 구현할 수 있습니다. testGetAllTeams()의 구현 예가 Listing 18과 같습니다:

Listing 18. CompetitionDaoTopLinkTest의 테스트 메소드 예제.
/**
* Tests the method that gets all teams that belong to a competition.
* Fields that are fetched include the club and the name
*/
public void testGetAllTeams(){
  List<Team> teams = competitionDao.findAllTeams();
        
  assertNotNull(teams);
  assertEquals(2, teams.size());
        
  Team teamOne = teams.get(0);
  assertNotNull(teamOne.getClub());
  assertEquals("Kampong", teamOne.getClub().getName());
  assertNotNull(teamOne.getName());
 }

AbstractXXXSpringContextTests 클래스는 몇 가지 편리한 메소드를 제공하고 있습니다. 표 2는 테스트에서 사용된 메소드를 정리해서 보여 주고 있습니다.

표 2. 테스트 클래스에서 사용된 메소드.

메소드 이름

설명

사용된 메소드

deleteFromTables(String[] tables)

String 어레이의 테이블에서 데이터를 삭제.

testFindAllClubs

setComplete()

트랜잭션의 롤백을 방지.

testSaveTeam

testDeleteTeam

endTransaction()

setComplete()이 호출된 경우 롤백 없이 트랜잭션을 종료.

testSaveTeam

testDeleteTeam

startNewTransaction()

메소드 내에서 새로운 트랜잭션을 시작. 이러한 방법으로 생성/종료할 수 있는 트랜잭션의 수에는 제한이 없음. 테스트 케이스가 실패하는 경우 최종 트랜잭션은 자동으로 롤백 처리됨.

testSaveTeam

testDeleteTeam

코드를 여러 가지 방법으로 수정하면서 테스트해 보시기 바랍니다. testSaveTeam 또는 testDeleteTeam 메소드에서 setComplete()과 the endTransaction()의 커멘트를 제거하면 어떻게 되는지 확인해 보십시오. 이렇게 하면 테스트는 실패할 것입니다. jdbcTemplate이 TopLink 세션을 사용하지 않고 트랜잭션이 여전히 실행 중인 상태로 남아 버리기 때문입니다.

애플리케이션의 적용(Deploy)

이렇게 해서 애플리케이션의 테스트를 끝냈습니다. 이제 디플로이먼트 프로파일(deployment profile), Ant, 또는 Maven을 이용해서 애플리케이션을 적용(deploy)해 봅시다(그림 17 참고). 본 문서에서는 디플로이먼트 프로파일을 사용하기로 합니다.

  1. webapp.deploy 프로파일을 편집합니다.
    • Spring128 라이브러리를 선택합니다.

    Figure 17
    그림 17. 애플리케이션과 함께 deploy되어야 하는 라이브러리.

  2. 스탠드얼론 OC4J를 시작합니다. OC4J가 JDK1.5와 함께 실행 중인지 확인합니다.
  3. JDeveloper의 Connections 탭을 이용하여 OC4J에 새로운 연결을 설정합니다.
  4. 디플로이먼트 프로파일을 이용하여 스탠드얼론 OC4J에 애플리케이션을 deploy합니다.
  5. 애플리케이션을 테스트합니다.
    • 웹 브라우저를 엽니다.
    • http://localhost:8888/competition/faces/teams.jsp를 입력합니다.
    • 팀 데이터를 편집, 추가, 삭제해 봅니다(그림 18 참고).

    Figure 18
    그림 18. 애플리케이션 시작 페이지.

향후 전망

현재 Spring Framework는 TopLink, Hibernate, JDO및 기타 데이터 액세스 프레임워크를 지원하고 있습니다. Spring JPA를 포함하게 될 Spring 2.0은 Java Persistence API에 대해 이와 유사한 지원을 제공하게 될 것입니다. 또 TopLink의 오픈 소스 버전인 TopLink Essentials가 새로이 포함될 예정입니다.

결론

본 문서를 통해 JDeveloper 환경에서 Spring과 TopLink를 활용하는 방법을 배워 보았습니다. 워크스페이스를 셋업하는 과정은 매우 간단하며, 기존에 사용해오던 것과 동일한 방법으로 TopLink 애플리케이션을 설계하고 코딩할 수 있습니다. 리소스와 트랜잭션 관리는 Spring 설정 파일에서 선언적으로 정의될 수 있으므로, 실제 코드와의 완전한 분리가 가능합니다. 이러한 기능 덕분에 코드의 관리와 테스트가 한층 쉬워졌을 뿐 아니라 EJB 3.0과 JPA 환경으로 마이그레이션하는 것도 한층 용이해졌습니다.


Lonneke Dikmans는 OFM Regional Director이자 네덜란드 Xebia Software Development의 선임 컨설턴트입니다. 로네케는 Java 2 플랫폼에 대한 Sun Certified Java Developer 인증을 획득하였으며 Oracle Fusion Middleware Regional Director로 활동하고 있습니다. 로네케는 2000 년부터 JDeveloper를 사용해 왔으며, J2EEE 애플리케이션의 설계, 개발, 구축에 관련한 풍부한 경험을 보유하고 있습니다.
E-mail this page
Printer View Printer View