기본환경:
OS: Window 7 SP1
JVM: Java SE 6 (JDK 1.6.0_31, 64bit)
Maven: 3.0.4
Eclipse: JEE-Indigo-SR2
현상:
Spring 3.1.1.RELEASE, JPA 2.0, Hibernate 3.6.10.Final 로 프로젝트를 진행하고 있었다. 처음에 MySql 5.5로 개발을 진행하다가 Oracle10g로 변경을 했는데 잔잔한 몇가지 경우를 빼고는 별탈없이 단위테스트케이스를 만들면서 개발이 가능했다. 그런데
@Lob 을 테스트하면서 다음과 같은 예외가 발생했다.
java.lang.ClassCastException: java.lang.String cannot be cast to oracle.sql.CLOB
at oracle.jdbc.driver.OraclePreparedStatement.setObjectCritical(OraclePreparedStatement.java:9229) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]
at oracle.jdbc.driver.OraclePreparedStatement.setObjectInternal(OraclePreparedStatement.java:8843) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]
at oracle.jdbc.driver.OraclePreparedStatement.setObject(OraclePreparedStatement.java:9316) ~[ojdbc14-10.2.0.4.0.jar:Oracle JDBC Driver version - "10.2.0.4.0"]
at org.dbunit.dataset.datatype.ClobDataType.setSqlValue(ClobDataType.java:71) ~[dbunit-2.4.9.jar:na]
at org.dbunit.database.statement.SimplePreparedStatement.addValue(SimplePreparedStatement.java:73) ~[dbunit-2.4.9.jar:na]
at org.dbunit.database.statement.AutomaticPreparedBatchStatement.addValue(AutomaticPreparedBatchStatement.java:63) ~[dbunit-2.4.9.jar:na]
at org.dbunit.operation.AbstractBatchOperation.execute(AbstractBatchOperation.java:189) ~[dbunit-2.4.9.jar:na]
at org.dbunit.operation.CompositeOperation.execute(CompositeOperation.java:79) ~[dbunit-2.4.9.jar:na]
at com.github.springtestdbunit.DbUnitRunner.setupOrTeardown(DbUnitRunner.java:159) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.DbUnitRunner.beforeTestMethod(DbUnitRunner.java:70) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.DbUnitTestExecutionListener.beforeTestMethod(DbUnitTestExecutionListener.java:136) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.TestExecutionListenerChain$3.call(TestExecutionListenerChain.java:92) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.TestExecutionListenerChain.runChain(TestExecutionListenerChain.java:125) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.TestExecutionListenerChain.forwards(TestExecutionListenerChain.java:114) ~[spring-test-dbunit-1.0.0.jar:na]
at com.github.springtestdbunit.TestExecutionListenerChain.beforeTestMethod(TestExecutionListenerChain.java:90) ~[spring-test-dbunit-1.0.0.jar:na]
at org.springframework.test.context.TestContextManager.beforeTestMethod(TestContextManager.java:358) ~[spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:73) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47) [junit-4.10.jar:na]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231) [junit-4.10.jar:na]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60) [junit-4.10.jar:na]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229) [junit-4.10.jar:na]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50) [junit-4.10.jar:na]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222) [junit-4.10.jar:na]
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.junit.runners.ParentRunner.run(ParentRunner.java:300) [junit-4.10.jar:na]
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) [spring-test-3.1.1.RELEASE.jar:3.1.1.RELEASE]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197) [.cp/:na]
이 예외의 원인은
java.lang.ClassCastException: java.lang.String cannot be cast to oracle.sql.CLOB
에서 유추할 수 있으나 왜 그런지는 알수가 없다. 예외스택을 좀더 탐색해 보면
oracle.jdbc.driver.OraclePreparedStatement -> org.dbunit.database.statement.SimplePreparedStatement. -> com.github.springtestdbunit.DbUnitRunner.setupOrTeardown
등으로 역추적할 수 있는데 dbUnit을 Annotation 기반으로 테스트 환경을 구축하기 위해서
pom.xml
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
</dependency>
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.4.9</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
DbUnitTestTemplate .java
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;
import org.springframework.test.context.transaction.TransactionConfiguration;
import com.github.springtestdbunit.DbUnitTestExecutionListener;
import com.github.springtestdbunit.TransactionDbUnitTestExecutionListener;
import com.github.springtestdbunit.annotation.DbUnitConfiguration;
/**
* DbUnit에대한 테스트설정을 공유하기 위한 추상클래스
* @author <a href="mailto:byleem@nextree.co.kr">임병인</a>
* @since 2012. 11. 15.
*/
@ContextConfiguration(locations = { "classpath:applicationContext-test.xml" })
// @DbUnitConfiguration(databaseConnection = "test_ASN_DS")
@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class, // needed if using transactions otherwise use TransactionalTestExecutionListener.class
DbUnitTestExecutionListener.class })
@TransactionConfiguration(transactionManager = "testTransactionManager", defaultRollback = true)
public abstract class DbUnitTestTemplate {
}
applicationContext-test.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-1.1.xsd">
<context:component-scan base-package="com.acelife.asn" />
<context:property-placeholder location="classpath:application-test.properties"/>
<bean id="test_ASN_DS" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" />
<property name="url" value="jdbc:oracle:thin:@db.ace.dev:1522:xe" />
<property name="username" value="........" />
<property name="password" value="........" />
</bean>
<bean id="testEMF" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="test_ASN_DS" />
<property name="persistenceUnitName" value="pun_asn" />
<property name="persistenceXmlLocation" value="classpath:META-INF/persistence-test.xml" />
<property name="loadTimeWeaver">
<bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver" />
</property>
</bean>
<bean id="testTransactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="testEMF" />
</bean>
<tx:annotation-driven transaction-manager="testTransactionManager" />
<bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" />
<ehcache:annotation-driven cache-manager="ehCacheManager" />
<!-- dbUnit -->
<bean id="dbUnitDatabaseConnection" class="com.github.springtestdbunit.bean.DatabaseDataSourceConnectionFactoryBean">
<property name="dataSource" ref="test_ASN_DS" />
<property name="databaseConfig">
<bean class="com.github.springtestdbunit.bean.DatabaseConfigBean">
<property name="skipOracleRecyclebinTables" value="true" />
<property name="datatypeFactory">
<bean class="org.dbunit.ext.oracle.Oracle10DataTypeFactory" />
</property>
</bean>
</property>
</bean>
</beans>
와 같이 설정했다.
지면관계상 설정을 해결안까지 같이 설정해 놨는데 위의 오라클 Lob문제는
를 참고하여
<property name="datatypeFactory">
<bean class="org.dbunit.ext.oracle.Oracle10DataTypeFactory" />
</property>
와 같이 datatypeFactory를 설정해주는 것이었다. 그리고
@DbUnitConfiguration(databaseConnection = "dbUnitDatabaseConnection")
와 같이 설정해줌으로써 DataSource 대신 IDatabaseConnection을 DBUnit에 전달해 주는 방법이다.
아마 대다수의 DB에서는 이런 문제가 발생하지 않는다고 하는데... 그건 알수가 없는 노릇이고... 어쨌든 바쁜와중에도 이문제는 꼭 포스팅해야한다는 생각이 들었다. 나를 위해서... ^^;