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:
- Can I add additonal methods to the CrudRepository that will call out to MyBatis? If so can explain what I'm missing below?
- 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>