使用springboot+gradle+mybatis-plus搭建项目xml扫描问题

1、走一个正常的项目流程:如下图

entity,mapper,service 你可以使用mybatis-plus自动工具生成,当然需要的实体类比较少的话,愿意自己写也不费劲。下面是gradle配置。


plugins {
    id 'org.springframework.boot' version '2.3.12.RELEASE'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'com.kk'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'



repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    // swagger
//    implementation 'io.springfox:springfox-swagger2:3.0.0'
    // swagger ui
//    implementation 'io.springfox:springfox-swagger-ui:3.0.0'

//    implementation 'io.springfox:springfox-boot-starter:3.0.0'
    //swagger-bootstrap-ui
//    implementation 'com.github.xiaoymin:swagger-bootstrap-ui:1.9.6'
    //
    // knife4j-spring-boot-starter
//    implementation 'com.github.xiaoymin:knife4j-spring-boot-starter:3.0.3'
    implementation 'com.github.xiaoymin:knife4j-openapi2-spring-boot-starter:4.5.0'

    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok'

    implementation 'mysql:mysql-connector-java:8.0.33'
    implementation 'com.baomidou:mybatis-plus-boot-starter:3.5.7'

}

tasks.named('test') {
    useJUnitPlatform()
}

application.yml配置:

server:
  port: 8090 # 服务端口

spring:
  application:
    name: payment-api # 应用名称
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

  datasource:
    url: jdbc:mysql://127.0.0.1:3306/test_demo?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
    username: root
    password: 123456

# mybatis-plus
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


# knife4j
knife4j:
  # 是否开启
  enable: true

加入配置config->MybatisPlusConfig ,这里主要是配置mybatis-plus的包自动扫描,当然你也可以在启动类里加这个@MapperScan("com.kk.paymentapi.mapper"),com.kk.....就是你的mapper的包路径。

@Configuration
@MapperScan("com.kk.paymentapi.mapper")
@EnableTransactionManagement //启用事务管理
public class MybatisPlusConfig {
}

写一个controller测试类:

@RestController
@RequestMapping("/api/product")
@Api(tags = "商品管理")
public class ProductController {

    @Resource
    private ProductService productService;

    @Resource
    private TestMapper testMapper;

    @Resource
    ProductMapper productMapper;

    @RequestMapping("list")
    @ApiOperation(value = "查询商品列表")
    public R queryProductList(){
        List<Product> list=productService.list();
        return R.ok().data("productList", list);
    }
    @RequestMapping("{id}")
    @ApiOperation(value = "通过id查询某个商品信息")
    public R queryById(@PathVariable Long id){
        Product product=productMapper.findById(id);
        return R.ok().data("product",product);
    }

    @RequestMapping("ext/{id}")
    @ApiOperation(value = "通过id查询某个商品信息")
    public R queryTestExtById(@PathVariable Long id){
        Product product=testMapper.findById(id);
        return R.ok().data("product",product);
    }
}

TestMapper是自定义的sql,又在ProductMapper里自定义了一个sql:

package com.kk.paymentapi.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kk.paymentapi.entity.Product;
public interface ProductMapper extends BaseMapper<Product> {

    Product findById(Long id);
}

<?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.kk.paymentapi.mapper.ProductMapper">

    <select id="findById" parameterType="Long" resultType="com.kk.paymentapi.entity.Product">
        select * from t_product where id = #{id}
    </select>
</mapper>
package com.kk.paymentapi.mapper.ext;

import com.kk.paymentapi.entity.Product;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;

@Repository
public interface TestMapper {


    Product findById(@Param("id") Long id);

}
<?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.kk.paymentapi.mapper.ext.TestMapper">

        <select id="findById"  resultType="com.kk.paymentapi.entity.Product">
            select * from t_product where id = #{id}
        </select>
</mapper>

2、 启动项目,一切应该都很正常:http://localhost:8090/doc.html

 上面便是正常搭建项目的过程。下面来看看不正常的过程。

3、将resources目录下的mapper改个别的名称试试:

启动项目试试:启动不报错,似乎运行良好,请求mybatis-plus自动生成的方法,也没问题,然而当你请求自定义sql时出错了,如同

Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@76fcf718]
2024-07-15 13:08:55.474 ERROR 21340 --- [nio-8090-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.kk.paymentapi.mapper.ProductMapper.findById] with root cause

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.kk.paymentapi.mapper.ProductMapper.findById
	at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:229) ~[mybatis-3.5.16.jar:3.5.16]
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.<init>(MybatisMapperMethod.java:50) ~[mybatis-plus-core-3.5.7.jar:3.5.7]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.lambda$cachedInvoker$0(MybatisMapperProxy.java:99) ~[mybatis-plus-core-3.5.7.jar:3.5.7]
	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) ~[na:1.8.0_321]
	at org.apache.ibatis.util.MapUtil.computeIfAbsent(MapUtil.java:36) ~[mybatis-3.5.16.jar:3.5.16]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.cachedInvoker(MybatisMapperProxy.java:97) ~[mybatis-plus-core-3.5.7.jar:3.5.7]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) ~[mybatis-plus-core-3.5.7.jar:3.5.7]
	at com.sun.proxy.$Proxy67.findById(Unknown Source) ~[na:na]
	at com.kk.paymentapi.controller.ProductController.queryById(ProductController.java:48) ~[main/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_321]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_321]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_321]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_321]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:878) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:792) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:626) ~[tomcat-embed-core-9.0.46.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:733) ~[tomcat-embed-core-9.0.46.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53) ~[tomcat-embed-websocket-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119) ~[spring-web-5.2.15.RELEASE.jar:5.2.15.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202) ~[tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:143) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:374) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_321]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_321]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.46.jar:9.0.46]
	at java.lang.Thread.run(Thread.java:750) [na:1.8.0_321]

问题就出在没有自动扫描到自定义的xml文件,怎么解决呢?只需在yml配置文件里引入:mapper-locations: classpath:/mapper1/**/*.xml,即可解决问题。

4、点击mapper-locations源码,你会发现其默认值是:classpath*:/mapper/**/*.xml。至此你应该知道了为何使用mapper文件下的xml能扫描到了的原因了。

5、将resources下的mapper文件移入到main/java/下面的某个包下,试试又会出怎样的问题呢?

将yml中的 mapper-locations: classpath:/mapper1/**/*.xml注释掉,启动项目,试试效果。你会发现自定义的sql出现和上面一样的错误,找不到自定义的sql方法。在yml配置中加入mapper-locations: classpath:com/kk/paymentapi/mapper/mapper/**/*.xml。注意路径是你项目包下的路径。错误依旧。这是为什么呢?加入了扫描路径了呀,还是不能找到自定义sql。查看gradle下的build文件,你会发现,java下的xml没有被build进来,classes下没有,resources下面也没有,因此即使你有了正确的扫描路径,到时xml没有被打包到项目里。怎么办?

maven项目你需要在pom.xml 里的build里加入下面的配置,要将java目录中的xml文件一起打包

<resources>
 <resource>
   <directory>src/main/java</directory>
   <includes>
     <include>**/*.xml</include>
   </includes>
   <filtering>false</filtering>
 </resource>
</resources>

gradle里也是同样的问题,未能将java下的xml文件打包。在gradle里加入如下配置:

sourceSets.main.resources {
    srcDirs = ["src/main/resources","src/main/java"]; excludes ["**/*.java"]
}
jar{
    //指定包含的文件
    from('src/main/java') {
        include '/**/*.xml'
    }
    from('src/main/java') {
        include '/**/*.properties'
    }
}

上面是jar包形式,war包如下

sourceSets.main.resources {
    srcDirs = ["src/main/resources","src/main/java"]; excludes ["**/*.java"]
}
war{
    //指定包含的文件
    from('src/main/java') {
        include '/**/*.xml'
    }
    from('src/main/java') {
        include '/**/*.properties'
    }
}

 或者用此方法更简单:

processResources {
    from('src/main/java') {
        include '**/*.xml'
    }
}

 两者都可以,哪种简单就用哪种吧。

综上所述,搭建整个项目的过程,最好用其默认的方法结构,会减少很多不必要的麻烦,若你不喜欢用默认的结构名称,那记得加入正确的配置即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值