Showing posts with label mybatis. Show all posts
Showing posts with label mybatis. Show all posts

Thursday, July 11, 2013

Cunning <mybatis:scan/> elemnt

In version 1.2.0 of Mybatis-Spring a possibility of scanning mapper interfaces via XML element <mybatis:scan/> has appeared. As a doc says  the <mybatis:scan/> XML element will search for mappers in a very similar way than the Spring built-in element <context:component-scan/>searches for beans. Here is an example:
<beans xmlns="http://www.springframework.org/schema/beans" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xmlns:mybatis="http://mybatis.org/schema/mybatis-spring" 
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
               http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
               http://mybatis.org/schema/mybatis-spring 
               http://mybatis.org/schema/mybatis-spring.xsd"> 
     <mybatis:scan base-package="org.mybatis.spring.sample.mapper" /> 
</beans>

The base-package attribute lets you set the base package for your mapper interface files. You can set more than one package by using a semicolon or comma as a separator. Mappers will be searched for recursively starting in the specified package(s). 
Also, if we have only either one sqlSessionFactory in application context or one sqlSessionTemplate(but not both) we may not specify them in <mybatis:scan/>, but if we have more than one of them - we should. Also attribute is factory-ref or template-ref, but actually it's not a ref. Mybatis-Spring treats to the value pointed in that attribute as to bean name. So, in my opinion the correct meanning of that attributes are factoryBeanName and templateBeanName as known properties in MapperScannerConfigurer.

<mybatis:scan/> element is handled by org.mybatis.spring.config.NamespaceHandler which is pointed as handler in spring.handlers file under META-INF directory of mybatis-spring.jar.This handler just overrides init method and registers the beanDefinitionParser MapperScannerBeanDefinitionParser. This parser scan the packages specified in base-package attribute and creates bean definitions for MapperFactoryBeans. The default behavior involves beans created using BeanDefinition to have autowire-candidate property to be set to true. Then, if we haven't specified sqlSessionFactory-ref or sqlTemplate-ref in <mybatis:scan/> tag, the subclass of ClasspathBeanDefinitionScanner which is used in MapperScannerBeanDefinitionParser sets autowireMode on created bean to be AUTOWIRE_BY_TYPE. Thus, it produces the behavior similiar to @Autowire annotation on setSqlSessionFactory method of MapperFactoryBean

Sunday, July 7, 2013

Issue with MapperScannerConfigurer in mybatis-spring module

    Recantly I have encountered with interesting feature in mybatis-spring module. I have been developing persistance module for one project and need to write tests for it. Ofcourse I decided to use spring-test and also tried dbUnit. I wrote a base class for persistance test case which simply utilize dbUnit functionality(applying datasets). Also I've annotated that class with standart spring-test annotation:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
@TransactionConfiguration(defaultRollback = true, transactionManager = "txManager")
@Transactional
which 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?