Quantcast

Problem autowiring mapper in Spring

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Problem autowiring mapper in Spring

Mr. Bill
Hello everyone,

I am trying to set up MyBatis with Spring but encounter an autowire
error during a unit test. I followed the pet store example very
closely but can't get Spring to see my Mapper. Hopefully I am just
doing something boneheaded! Thanks in advance for any help you can
provide.

Configuration:
Spring 3.1.1
mybatis-spring 1.1.0
hsqldb 2.2.4
Java 1.6

====================
My Mapper XML (located in com.bonddesk.bondapi.message.persistence):

<mapper
namespace="com.bonddesk.bondapi.message.persistence.MessageMapper">
  <select id="getMessage" parameterType="long" resultType="Message">
    select
      MSG_ID as messageId,
      USER_ID as userId,
      POSTED_TS as posted,
      EVENT_TYPE as eventType,
      MSG_READ as read,
      MSG_DELETED as deleted,
      ACTION_DONE as actionDone
    from
      USER_EVENT_STORE
    where
      messageId = #{messageId}
  </select>
</mapper>


====================
My Mapper Class (located in com.bonddesk.bondapi.message.persistence):

public interface MessageMapper {
    Message getMessage(long messageId);
}


====================
My Spring Configuration:

  <!-- enable autowire -->
  <context:annotation-config />

  <!-- enable component scanning (beware that this does not enable
mapper scanning!) -->
  <context:component-scan base-package="com.bonddesk.bondapi" />

  <!-- enable transaction demarcation with annotations -->
  <tx:annotation-driven />

  <!-- configure property file locations -->
  <bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:jdbc.properties"/>
  </bean>

  <!-- message data source -->
  <bean id="dataSource" destroy-method="close"
class="com.jolbox.bonecp.BoneCPDataSource">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}${jdbc.schema}$
{jdbc.driverProperties}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>

  <bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="typeAliasesPackage"
value="com.bonddesk.bondapi.message.domain" />
  </bean>

  <!-- scan for mappers and let them be autowired -->
  <bean id="mapperScannerConfigurer"
class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage"
value="com.bonddesk.bondapi.message.persistence" />
  </bean>
</beans>


====================
My Repository (where mapper is being auto wired):

@Repository
public class MyBatisMessageRepository implements MessageRepository {

    @Autowired
    private MessageMapper messageMapper;

    @Override
    public Message getMessage(long messageId) {
        return messageMapper.getMessage(messageId);
    }
}


====================
My Unit Test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class MyBatisMessageRepositoryTest {

    @Autowired
    private MessageRepository messageRepository;

    @Test
    public void getNewOrderMessage() {
        Message message = messageRepository.getMessage(515851L);
        assertThat(message, not(nullValue()));
    }
}


====================
Relevant portions of the log output:

Running
com.bonddesk.bondapi.message.persistence.MyBatisMessageRepositoryTest
2012-03-20 11:14:41,467 [main] DEBUG
org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner - Ignored
because not a concrete top-level class: file [/Users/bill/dev/projects/
bondapi/target/test-classes/com/bonddesk/bondapi/message/persistence/
MyBatisMessageRepositoryTest.class]
2012-03-20 11:14:41,468 [main] DEBUG
org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner - Identified
candidate component class: file [/Users/bill/dev/projects/bondapi/
target/classes/com/bonddesk/bondapi/message/persistence/
MessageMapper.class]
2012-03-20 11:14:41,468 [main] DEBUG
org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner - Ignored
because not a concrete top-level class: file [/Users/bill/dev/projects/
bondapi/target/classes/com/bonddesk/bondapi/message/persistence/
MyBatisMessageRepository.class]
2012-03-20 11:14:41,469 [main] DEBUG
org.mybatis.spring.mapper.MapperScannerConfigurer$Scanner - Creating
MapperFactoryBean with name 'messageMapper' and
'com.bonddesk.bondapi.message.persistence.MessageMapper'
mapperInterface
2012-03-20 11:14:41,602 [main] ERROR
org.springframework.test.context.TestContextManager - Caught exception
while allowing TestExecutionListener
[org.springframework.test.context.support.DependencyInjectionTestExecutionListener@1efd9b97]
to prepare test instance
[com.bonddesk.bondapi.message.persistence.MyBatisMessageRepositoryTest@1bf1e666]


Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
matching bean of type
[com.bonddesk.bondapi.message.persistence.MessageMapper] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations:
{@org.springframework.beans.factory.annotation.Autowired(required=true)}
     at
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:
924)
     at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:
793)
     at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:
707)
     at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:
478)

Tests in error:
 
getNewOrderMessage(com.bonddesk.bondapi.message.persistence.MyBatisMessageRepositoryTest):
Failed to load ApplicationContext
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate
star

Re: Problem autowiring mapper in Spring

Mr. Bill
I tried wiring everything up manually via Spring XML configuration and
it actually worked. While I am happy things are working, I would
rather go back to using annotations and autowire. I don't understand
why the MapperScannerConfigurer isn't working for me.


====================
Here is the Spring config that gets it working:

  <!-- enable autowire -->
  <context:annotation-config />

  <!-- enable component scanning (beware that this does not enable
mapper scanning!) -->
  <context:component-scan base-package="com.bonddesk.bondapi" />

  <!-- enable transaction demarcation with annotations -->
  <tx:annotation-driven />

  <!-- configure property file locations -->
  <bean
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:jdbc.properties"/>
  </bean>

  <!-- message data source -->
  <bean id="dataSource" destroy-method="close"
class="com.jolbox.bonecp.BoneCPDataSource">
    <property name="driverClass" value="${jdbc.driverClassName}"/>
    <property name="jdbcUrl" value="${jdbc.url}${jdbc.schema}$
{jdbc.driverProperties}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
  </bean>

  <bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
  </bean>

  <bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="typeAliasesPackage"
value="com.bonddesk.bondapi.message.domain" />
  </bean>

  <bean id="messageMapper"
class="org.mybatis.spring.mapper.MapperFactoryBean">
    <property name="mapperInterface"
value="com.bonddesk.bondapi.message.persistence.MessageMapper" />
    <property name="sqlSessionFactory" ref="sqlSessionFactory" />
  </bean>

  <bean id="messageRepository"
class="com.bonddesk.bondapi.message.persistence.MyBatisMessageRepository">
    <constructor-arg ref="messageMapper" />
  </bean>


====================
Here is the modified MessageRepository class:

public class MyBatisMessageRepository implements MessageRepository {
    private MessageMapper messageMapper;

    public MyBatisMessageRepository(MessageMapper messageMapper) {
        this.messageMapper = messageMapper;
    }

    @Override
    public Message getMessage(long messageId) {
        return messageMapper.getMessage(messageId);
    }
}
Loading...