2

I'm trying to use Spring Data JDBC with MyBatis. I'm interested in using the out-of-the-box CRUD methods that CrudRepository provides and in writing ADDITIONAL custom queries defined in MyBatis XML files.

From reading the instructions from jdbc.mybatis it sounds like all I need to do is create a Mapper that implements the method I'd like (adhering the the rule "The name of the statement is constructed by concatenating the fully qualified name of the entity type with Mapper") and add a method signature to my CrudRepository. I've done that but Spring Data never seems to find my method and gives this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fooApplication': Invocation of init method failed; nested exception is org.springframework.dao.InvalidDataAccessApiUsageException: No query specified on findByChangeOwnerId; nested exception is java.lang.IllegalStateException: No query specified on findByChangeOwnerId
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1778) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:849) ~[spring-beans-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:877) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:549) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142) ~[spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.3.RELEASE.jar:2.1.3.RELEASE]
    at com.xxx.fooApplication.main(fooApplication.java:42) [classes/:na]
Caused by: org.springframework.dao.InvalidDataAccessApiUsageException: No query specified on findByChangeOwnerId; nested exception is java.lang.IllegalStateException: No query specified on findByChangeOwnerId

I've also followed the example from mybatis-spring-boot-sample but there's not a lot for me to go on with what's in there.

Questions are:

  1. Can I add additonal methods to the CrudRepository that will call out to MyBatis? If so can explain what I'm missing below?
  2. I assume for any additonal methods I can pass in whatever arguments I want as opposed to always having to pass in the MyBatisContext?

My CrudRepository

package com.xxx.repository.jdbc;

import com.xxx.model.Foo;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface FooBmtRepository extends CrudRepository<Foo, String> {  
  List<Foo> findByChangeOwnerId(@Param("id") String id);
}

src/main/resources/application.yml

mybatis:
  mapper-locations: classpath:/mapper/**/*.xml
  configuration:
    map-underscore-to-camel-case: true

Mapper XML

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xxx.model.ItsmMapper">
    <!--<select id="findByChangeOwnerId" resultType="com.xxx.model.Itsm" parameterType="org.springframework.data.jdbc.mybatis.MyBatisContext">-->
    <!--where change_owner_id=#{changeOwnerId}-->
    <select id="findByChangeOwnerId" resultMap="itsmResultMap" parameterType="String">
        select *
        from [myschema].[tbl_itsms_bmt]
        where change_owner_id=#{id}
    </select>

    <resultMap id="itsmResultMap" type="com.xxx.model.Itsm">
        <result property="changeNumber" column="change_number"/>
        <result property="itsmUrl" column="itsm_url"/>
        <result property="changeTitle" column="change_title"/>
        <result property="category" column="category"/>
        <result property="status" column="status"/>
        <result property="createdDate" column="created_date"/>
    </resultMap>

</mapper>

Dependencies (most are imported using the starters, showing all spelled out since I had trouble getting mvn dependency:tree to properly display)

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-jdbc</artifactId>
    <version>1.0.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-commons</artifactId>
    <version>2.1.5.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.1.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.1.6.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot</artifactId>
    <version>2.1.3.RELEASE</version>
    <scope>compile</scope>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-autoconfigure</artifactId>
    <version>2.0.0</version>
</dependency>
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.0</version>
</dependency>
Tony Falabella
  • 335
  • 1
  • 6
  • 17

1 Answers1

2

The beginning of the section you linked to reads:

For each operation in CrudRepository, Spring Data JDBC runs multiple statements. If there is a SqlSessionFactory in the application context, Spring Data checks, for each step, whether the SessionFactory offers a statement. If one is found, that statement (including its configured mapping to an entity) is used.

This works only for the methods in the CrudRepository. Additional methods don't work like this.

You could implement a full blown custom method implementation calling out to MyBatis.

Alternatively you could create an issue and maybe even a PR for named queries backed by MyBatis.

Jens Schauder
  • 77,657
  • 34
  • 181
  • 348
  • I had read what you've noted but wasn't sure if I interpreted the statement correctly - thanks for the clarification. For now I've opened an enhancement request. https://jira.spring.io/browse/DATAJDBC-362 – Tony Falabella Apr 15 '19 at 13:44
  • Thanks. If you have an idea how to improve the documentation to make this clearer I'd appreciate that as well. – Jens Schauder Apr 15 '19 at 13:47