I have the following applicationContext.xml file in the root of the pathpath:
<context:annotation-config /> <context:property-placeholder location="classpath:props/datasource.properties" /> <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" /> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="datasource" p:mapperLocations="classpath:mappers/*-mapper.xml" /> <tx:annotation-driven transaction-manager="txManager" /> <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager" p:dataSource-ref="datasource" /> <bean id="mappeScannerConfigurere" class="org.mybatis.spring.mapper.MapperScannerConfigurer" p:sqlSessionFactory-ref="sqlSessionFactory" p:basePackage="com.mypackage" />
props/datasource.properties also exists in the root of the path path with this content:
jdbc.url=myjdbcurl jdbc.driverclass=myClass jdbc.username=myUserName jdbc.password=myPassword
I have a spring managed tag where I declare to use the previously mentioned applicationContext.xml through the following annotations:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
When I call the validation method, I get the following error from spring:
org.apache.commons.dbcp.SQLNestedException: Cannot load JDBC driver class '${jdbc.driverclass}'
As I understand it, sping did not resolve the link to jdbc.driverclass. What did I do wrong?
PS: I am using spring 3.2.3.RELEASE
**
EDIT
**
Perhaps the problem might be in the MapperScannerConfigurer . This is a BeanDefinitionRegistryPostProcessor , and as Javadok says:
Expanding to the standard SPI BeanFactoryPostProcessor, allowing you to register additional bean definitions before the BeanFactoryPostProcessor is regularly detected in
So MapperScannerConfigurer creates an instance of the data source object via sqlSessionFactory using the BeanFacoryPostProcessor (which is responsible for <context:property-placeholder/> ) was not used. So my question translates into a question about how to change the order of BeanFacoryPostProcessor with <context:property-placeholder/> and BeanDefinitionRegistryPostProcessor ( MapperScannerConfigurer )?
Decided
After several hours of research, I found a solution:
As I said, MapperScannerConfigurer is a BeanDefinitionRegistryPostProcessor that fires before the BeanFactoryPostProcessor , which is responsible for <context:property-placeholder/> . Thus, when creating MapperScannerConfigurer links, external properties will not be allowed. In this case, we must defer the creation of the data source until after the application of BeanFactoryPostProcessor . We can do this in several ways:
- remove
p:sqlSessionFactory-ref="sqlSessionFactory" from the MapperScannerConfigurer . In this case, the data source object will not be created before the MapperScannerConfigurer , but after the BeanFactoryPostProcessor , which is responsible for <context:property-placeholder/> . If you have more than one sqlSessionFactory in applicationContext than some problems may be - In versions of the mybatis-spring module above 1.0.2, it is possible to set
sqlSessionFactoryBeanName instead of sqlSessionFactory . This helps to solve the PropertyPlaceHolder problem with a BeanFactoryPostProcessor . This is the recommended way to solve this problem, described in mybatis-spring doc