@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) @TransactionConfiguration(defaultRollback = true, transactionManager = "txManager") @Transactionalwhich points to applicationContext and also uses transactionManager to roolback transactions(with dbUnit i clear the real table in test method, insert there test dataset, perform test and then rollback a transaction).
All my persistance tests are focusing on testing mybatis mappers, so for that reason I used MapperScannerConfigurer to dynamically discover all mybatis mapper interfaces and create for each of them MapperFactoryBean instances which produce real instances of mapper interfaces. So, I configured a bean for MapperScannerConfigurer in applicationContext:
<bean id="mappeScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.mypackage" p:sqlSessionFactory-ref="sqlSessionFactory" />As it needs sqlSessionFactory I declare it also. SqlSessionFactory needs a datasource to fetch connections from it, so i declare a datasource like this:
<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource" p:username="${jdbc.username}" p:password="${jdbc.password}" p:url="${jdbc.url}" p:driverClassName="${jdbc.driverclass}" p:validationQuery="SELECT sysdate FROM dual" />Ofcourse I need some property-placeholder to resolve references to jdbc stuff and I declare it:
<context:property-placeholder location="classpath:props/datasource.properties" />It seemed to me that it's enough, but when I try to run the test I got next error:
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverclass}'
It means that placeholder hasn't been resolved. but why?
I've started to investigate the problem and discovered that MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor. Javadoc for that class says:
Extension to the standard BeanFactoryPostProcessor SPI, allowing for the registration of further bean definitions before regular BeanFactoryPostProcessor detection kicks inSo MapperScannerConfigurer instantiates before the PropertyPlaceHolderConfigurer and, as it depends on sqlSessionFactory which depends on datasource, datasource instance try to be instantiated with property holders in its properties.
We can fix that in several ways:
- By removing p:sqlSessionFactory-ref="sqlSessionFactory" from the MapperScannerConfigurer definition will not trigger creation sqlSessionFactory object during creation of MapperScannerConfigurer. But sqlSessionfactory will then be autowired into MapperFactoryBean which extends SqlSessionDaoSupport(NOTE: this works only for versions of mybatis-spring prior to 1.2.0, starting from 1.2.0 version @Autowired annotation was removed from setSqlSessionFactory and setSqlSessionTemplate methods)
- By specifying p:sqlSessionFactoryName="sqlSessionFactory" instead. But this only works for versions higher than 1.0.2. And if you are using version higher than 1.2.0 it is the only way to configure MapperScannerConfigurer(however, there is a tag <mybatis:scan/> which can be used instead of MapperScannerConfigurer)
So, my result configuration was:
<bean id="mappeScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:basePackage="com.mypackage" p:sqlSessionFactoryName="sqlSessionFactory" />
and I switched to use version 1.2.0. In this case I wrote a class that extends standart SqlSessionDaoSupport and overrides method setSqlSessionFactory with @Autowired annotation. It allowed me not to declare all my dao classes in xml, but do the component-scan on them
This comment has been removed by the author.
ReplyDeletePlease correct the typo: mybatis-spring:scan to mybatis:scan
ReplyDeleteFixed, thanks for attentiveness
ReplyDelete