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' } }
两者都可以,哪种简单就用哪种吧。
综上所述,搭建整个项目的过程,最好用其默认的方法结构,会减少很多不必要的麻烦,若你不喜欢用默认的结构名称,那记得加入正确的配置即可。