Spring Integration

Spring에서는 Hibernate 기반에서 DAO 클래스를 쉽게 구현할 수 있도록 하기 위해 HibernateTemplate을 제공하고 있다. (※ Spring 2.5 부터는 Hibernate 3 버전을 지원한다.) 또한, Anyframe에서는 Veloticy 문법을 이용하여 Dynamic HQL문을 처리하기 위해서 DynamicHibernateService를 제공한다. Hibernate을 이용하여 데이터 액세스 처리를 수행하는 경우 하나의 비즈니스 서비스를 구성하는 요소들은 일반적으로 다음과 같이 구성될 수 있다.
Spring 기반에서 Hibernate을 통해 데이터 액세스 처리를 수행하기 위해서는 다음과 같은 절차에 따라 비즈니스 서비스를 개발할 수 있다.

Hibernate 속성 정의 파일 작성

Hibernate을 Spring과 연계하기 위해서는 SessionFactory 설정이 필요하다. 또한, Dynamic HQL 실행을 위해서는 Anyframe에서 제공하는 DynamicHibernateService에 대한 설정도 필요하다.

Session Factory 속성 정의

Spring에서 제공하는 HibernateDaoSupport는 내부적으로 Hibernate 연계를 위해 HibernateTemplate을 생성하는데 이 클래스는 SessionFactory를 필요로 한다. 이를 위해 HibernateDaoSupport를 상속받은 클래스들은 SessionFactory를 필요로 하며, SessionFactory는 다음과 같은 속성 정보를 가질 수 있다. 다음은 SessionFactory의 속성을 정의한 context-hibernate.xml 파일의 일부이다.
<bean id="sessionFactory"
	class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<!-- SessionFactory에서 사용할 dataSource 정의 -->
	<property name="dataSource" ref="dataSource" />
	<!-- Mapping XML의 위치 지정 -->
	<property name="mappingLocations">
		<list>
			<value>classpath:anyframe/sample/model/bidirection/Category.hbm.xml</value>
			<value>classpath:anyframe/sample/model/bidirection/Country.hbm.xml</value>
			<value>classpath:anyframe/sample/model/bidirectionMovie.hbm.xml</value>
		</list>
	</property>
	<!-- Hibernate Property에 대한 속성 정의 -->
	<property name="hibernateProperties">
		<props>
			<prop key="hibernate.hbm2ddl.auto">create</prop>
			<!-- DBMS에 따른 dialect 설정-->
			<prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
			<!-- hibernate을 이용한 sql문을 보여줄지 여부-->
			<prop key="hibernate.show_sql">false</prop>
			<prop key="hibernate.format_sql">true</prop>
		</props>
	</property>
</bean>

Dynamic HQL 실행을 위한 DynamicHibernateService 속성 정의

조건에 따라 HQL문을 dynamic하게 생성해 주기 위해 Anyframe에서는 DynamicHibernateService를 제공한다. 이러한 기능을 사용하기 위해서는 다음과 같이 DynamicHibernateService 클래스에 대한 속성을 정의하고 특정 DAO 클래스 정의시 DynamicHibernateService를 참조하도록 할 수 있다. 다음은 dynamicHibernateService bean이 정의된 context-hibernate.xml파일의 일부이다.
<bean id="dynamicHibernateService"
	class="anyframe.core.hibernate.impl.DynamicHibernateService">
	<!-- SessionFactory 지정  -->
	<property name="sessionFactory" ref="sessionFactory" />
	<!-- Velocity 문법이 적용된 dynamic한 HQL을 정의한 XML파일의 경로 지정 -->
	<property name="fileNames">
		<list>
			<value>classpath:anyframe/core/hibernate/spring/dynamic-hibernate.xml</value>
		</list>
	</property>
</bean>
위와 같이 정의할 경우 dynamicHibernateService bean은 sessionFactory bean을 SessionFactory로 가지며 fileNames에 정의된 XML들에서 해당되는 HQL을 찾게 될것이다.

Mapping XML 파일 작성

특정 비즈니스 서비스에서 사용할 객체와 테이블간의 매핑 정보를 Mapping XML 파일에 작성한다. 또한 Mapping XML 파일의 위치를 앞서 언급한 SessionFactory 속성 정의 파일에 아래와 같이 정의해 줘야한다.
<bean id="sessionFactory"
	class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
	<property name="dataSource" ref="dataSource" />
	<!-- Mapping XML의 위치 지정 -->
	<property name="mappingLocations">
		<list>
			<value>classpath:anyframe/sample/model/bidirection/Movie.hbm.xml</value>
		</list>
	</property>
	<!-- Hibernate Property에 대한 속성 정의 -->
</bean>
자세한 Mapping File 작성은 본 매뉴얼 >> Hibernate >> Mapping File을 참고하도록 한다.

DAO 클래스 생성

Spring에서는 Hibernate을 보다 쉽게 연계하기 위해 HibernateDaoSupport 클래스를 제공하며 각 DAO 생성시 HibernateDaoSupport 클래스를 상속받아 구현할 수 있다. 각 DAO 클래스는 getHibernateTemplate()메소드를 호출함으로써 HibernateDaoSupport 클래스에서 제공하는 HibernateTemplate을 이용하여 기본 입력/수정/삭제/조회 작업을 수행할 수 있다. 또한, Dynamic HQL 처리를 위해 dynamicHibernateService를 사용해야 할 경우에는 위에서 언급한 바와 같이 dynamicHibernateService에 대한 참조가 필요하다.

DAO 속성 정의 파일 작성

DAO 클래스에 대한 속성 정의 파일을 작성한다. SessionFactory와 DynamicHibernateService를 참조하는 MovieDAOHibernateImpl 클래스에 대한 속성은 다음과 같이 정의할 수 있다.
<bean id="movieService" class="anyframe.sample.service.movie.impl.MovieServiceImpl">
	<property name="movieDAO">
		<bean class="anyframe.sample.service.movie.impl.MovieDAOHibernateImpl">
			<!-- Hibernate Template을 이용하기 위한 SessionFactory 정의 -->
			<property name="sessionFactory" ref="sessionFactory"/>
			<!-- Dynamic HQL문 지원을 위한 dynamicHibernateService 정의 
			(dynamicHibernateService를 사용할 때만 정의) -->
			<property name="dynamicHibernateService" ref="dynamicHibernateService"/>
		</bean>
	</property>
</bean>
위 코드는 context-sample.xml에서 확인할 수 있다.

DAO 클래스 개발

Spring에서 제공하는 HibnernateDaoSupport를 상속받아 DAO 클래스를 정의한다. 이 때, getHibernateTemplate() 메소드를 사용하여 HibernateTemplate을 이용한 데이터 입력/수정/삭제/조회가 가능하다.
public class MovieDAOHibernateImpl extends HibernateDaoSupportimplements
		MovieDAO{

	private DynamicHibernateService dynamicHibernateService;

	//dynamicHibernateService Setter Injection
	public void setDynamicHibernateService(
			DynamicHibernateService dynamicHibernateService) {
		this.dynamicHibernateService = dynamicHibernateService;
	}

	public void createMovie(Movie movie) throws Exception {
		this.getHibernateTemplate().save(movie);
	}
	public Movie findMovie(String movieId) throws Exception {
		return (Movie) this.getHibernateTemplate().get(Movie.class, movieId);
	}

	public List findMovieList(int conditionType, String condition)
			throws Exception {
		Object[] args = new Object[3];
		if (conditionType == 0) {
			args[0] = "director=%" + condition + "%";
			args[1] = "sortColumn=movie.director";
		} else {
			args[0] = "title=%" + condition + "%";
			args[1] = "sortColumn=movie.title";
		}
		args[2] = "sortDirection=ASC";
		
		return dynamicHibernateService.findList("findMovieListAll", args);
	}

	public List findMovieListAll() throws Exception {
		return this.getHibernateTemplate().find(
				"FROM Movie movie ORDER BY movie.title");
	}

	public void removeMovie(Movie movie) throws Exception {
		this.getHibernateTemplate().delete(movie);
	}

	public void updateMovie(Movie movie) throws Exception {
		this.getHibernateTemplate().update(movie);
	}
	
	public void updateMovieByBulk(Movie movie) throws Exception {
		StringBuffer hqlBuf = new StringBuffer();
		hqlBuf.append("UPDATE Movie movie ");
		hqlBuf.append("SET movie.director = ? ");
		hqlBuf.append("WHERE movie.movieId = ? ");
		
		//HQL문을 이용한 CUD를 할 경우에는 getHibernateTemplate().bulkUpdate() 메소드를 사용한다.
		this.getHibernateTemplate().bulkUpdate(hqlBuf.toString(),
				new Object[] { movie.getDirector(), movie.getMovieId() });
	}

	public void createCategory(Category category) throws Exception {
		this.getHibernateTemplate().save(category);
	}

	public void createCountry(Country country) throws Exception {
		this.getHibernateTemplate().save(country);
	}
}
위의 코드는 MovieDAOHibernateImpl.java에서 확인할 수 있다.

※ Dynamic Hibernate에 대한 자세한 사항은 본 매뉴얼 >> Hibernate >> Dynamic Hibernate 를 참고한다.

Test Code 작성

위와 같이 Spring과 Hibernate 연계 작업이 완료되었다면 Test Code를 작성해서 정상 동작 여부를 확인해 보도록 하자. 다음은 Test Code의 예인 HibernateSpringIntegrationTest.java파일의 일부이다.
public class HibernateSpringIntegrationTest extends
		AbstractDependencyInjectionSpringContextTests {
	private MovieService movieService;
	
	//Test 실행에 필요한 비즈니스 서비스 정의 파일의 위치를 지정해준다.
	protected String[] getConfigLocations() {
		return new String[] { "classpath:anyframe/core/hibernate/spring/context-*.xml" };
	}
	
	//MovieService Setter Injection
	public void setMovieService(MovieService movieService) {
		this.movieService = movieService;
	}

	/**
	 * [Flow #-1] Positive Case : Hibernate과 Spring Framework을 연계한 MovieService를
	 * 통해 단건의 Movie 정보를 등록,수정,삭제,조회하여 본다.
	 * 
	 * @throws Exception
	 *             throws exception which is from MovieService
	 */
	public void testMovieService() throws Exception {
		Movie movie = new Movie();
		movie.setMovieId("MV-00001");
		movie.setDirector("Jaeyong Gwak");
		movie.setReleaseDate(DateUtil.string2Date("2001-07-27", "yyyy-MM-dd"));
		movie.setTitle("My Sassy Girl");
		//movie 객체 등록
		movieService.createMovie(movie);

		Movie result = movieService.findMovie("MV-00001");
		assertNotNull("fail to add a new movie.", result);

		movie.setDirector("Update Jaeyong Gwak");
		//movie 객체 수정
		movieService.updateMovie(movie);
		
		//movie 객체 조회
		result = movieService.findMovie("MV-00001");
		assertEquals("fail to update a new movie.", "Update Jaeyong Gwak",
				result.getDirector());
				
		//movie 객체 삭제
		movieService.removeMovie(movie);
		
		//movie 객체 조회
		result = movieService.findMovie("MV-00001");
		assertNull("fail to remove a movie.", result);
	}
}
위와 같은 코드로 MovieService를 통해 입력/수정/삭제/조회 관련 메소드들이 잘 작동되는지 확인할 수 있다.

선언적인 트랜잭션 관리

Hibernate을 사용할 시에도 Spring의 AOP를 이용한 선언적인 트랜잭션 관리가 가능하다. 이는 본 매뉴얼 >> Spring >> AOP >> AOP Sample- Transactons에서 기본적인 내용을 확인할 수 있다. 단, Spring에서는 다음과 같이 Hibernate을 위한 TransactionManager인 org.springframework.orm.hibernate3.HibernateTransactionManager를 제공함으로써 Hibernate에 최적화된 형태로 트랜잭션을 관리할 수 있게 해주며 설정 방법의 예는 context-transaction.xml 의 일부인 다음과 같다.
<bean id="transactionManager"
	class="org.springframework.orm.hibernate3.HibernateTransactionManager">
	<property name="sessionFactory" ref="sessionFactory" />
</bean>

<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<tx:attributes>
		<tx:method name="*" propagation="REQUIRES_NEW"
			rollback-for="Exception" />
	</tx:attributes>
</tx:advice>

<aop:config proxy-target-class="true">
	<aop:pointcut id="executionMethods"
		expression="execution(* anyframe.sample..*Impl.*(..))" />
	<aop:advisor advice-ref="txAdvice"
		pointcut-ref="executionMethods" />
</aop:config>
기타 정의 방법은 기존 Spring TransactionManager를 사용할 때와 동일하다. Hibernate 기반의 트랜잭션 관리에 대한 자세한 내용은 본 매뉴얼 >> Hibernate >> Transaction Management를 참고한다.

Resources

  • 다운로드
  • 샘플 테스트 코드를 포함하고 있는 anyframe-hibernatetest-src.zip 파일을 다운받은 후, 테스트 환경 설정을 참조하여 위에서 제시한 예제 코드 (src/test/java 폴더의 anyframe.core.hibernate.spring 패키지에 속한 *Test.java)를 실행해 볼 수 있다.
    Name
    Download
    anyframe-hibernatetest-src.zip
    Download