记录一个Invalid bound statement (not found)问题

SpringBoot+MyBatisPlus项目,非常简单,没有任何业务逻辑:

1. pom文件



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.7.12
         
    
    org.yh.mall
    mall-server
    0.0.1-SNAPSHOT
    mall-server
    mall-server
    
        1.8
    
    
        
            org.springframework.boot
            spring-boot-starter-data-redis
        
        
            org.springframework.boot
            spring-boot-starter-groovy-templates
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        
        
            com.mysql
            mysql-connector-j
            runtime
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            org.springframework.boot
            spring-boot-starter-validation
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            3.5.3.1
        

        
        
            com.alibaba
            druid-spring-boot-starter
            1.2.18
        

        
        
            cn.hutool
            hutool-all
            5.8.19
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        

        
            
            
                src/main/resources
            
        
    


2. 启动类

package gov.yh.mall;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan(basePackages = {"gov.yh.mall"})
public class MallServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(MallServerApplication.class, args);
    }

}

3. Controller

package gov.yh.mall.controller;

import gov.yh.mall.common.api.ApiResp;
import gov.yh.mall.service.CategoryService;
import gov.yh.mall.vo.CategoryTreeVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/category")
@RestController
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    @GetMapping("/tree")
    public ApiResp tree() {
        return ApiResp.ok(categoryService.listTree());
    }
}

4. Service接口

package gov.yh.mall.service;

import com.baomidou.mybatisplus.extension.service.IService;
import gov.yh.mall.domain.Category;
import gov.yh.mall.vo.CategoryTreeVo;

public interface CategoryService extends IService {
    CategoryTreeVo listTree();
}

5. Service实现

package gov.yh.mall.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import gov.yh.mall.domain.Category;
import gov.yh.mall.mapper.CategoryMapper;
import gov.yh.mall.service.CategoryService;
import gov.yh.mall.vo.CategoryTreeVo;
import org.springframework.stereotype.Service;

@Service
public class CategoryServiceImpl extends ServiceImpl implements CategoryService {

    @Override
    public CategoryTreeVo listTree() {
        return null;
    }
}

6. 数据库模型

package gov.yh.mall.domain;

import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public abstract class BaseDomain implements Serializable {
    private static final long serialVersionUID = -4103484210920857803L;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime updateTime;
    private String delFlag;
    private Boolean status;
}
package gov.yh.mall.domain;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import gov.yh.mall.common.utils.GenericListJsonTypeHandler;
import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.List;

@EqualsAndHashCode(callSuper = true)
@Data
@TableName("mall_category")
public class Category extends BaseDomain implements Serializable {
    private static final long serialVersionUID = 8072944985574611728L;

    @TableId
    private Integer categoryId;
    private Integer parentId;
    @TableField(typeHandler = GenericListJsonTypeHandler.class)
    private List categoryPath;
    private String categoryName;
}

7. DAO接口

package gov.yh.mall.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import gov.yh.mall.domain.Category;
import org.springframework.stereotype.Repository;

@Repository
public interface CategoryMapper extends BaseMapper {
}

与 mapper对应的xml放在resources/mapper目录中






 8. 数据库表

CREATE TABLE `mall_category` (
  `category_id` int(20) NOT NULL COMMENT 'ID',
  `parent_id` int(20) DEFAULT NULL COMMENT '父级ID',
  `category_path` json DEFAULT NULL COMMENT '类别全路径',
  `category_name` varchar(30) COLLATE utf8mb4_bin NOT NULL COMMENT '类别名称',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `del_flag` char(1) CHARACTER SET utf8mb4 DEFAULT '0' COMMENT '是否删除',
  `status` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否启用的状态',
  PRIMARY KEY (`category_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='商品类目';

9. 视图层模型

package gov.yh.mall.vo;

import lombok.Data;

import java.io.Serializable;

@Data
public class CategoryItem implements Serializable {
    private static final long serialVersionUID = -251944196849206041L;
    private Integer categoryId;
    private Integer parentId;
    private String categoryName;
}
package gov.yh.mall.vo;

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.io.Serializable;
import java.util.List;

@EqualsAndHashCode(callSuper = true)
@Data
public class CategoryTreeVo extends CategoryItem implements Serializable {
    private static final long serialVersionUID = -5372664435944643984L;

    private List children;
}

10. 工具类

package gov.yh.mall.common.utils;

import cn.hutool.json.JSONUtil;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

/**
 * JSON字段处理器,列表型数据
 *
 */
public class GenericListJsonTypeHandler extends BaseTypeHandler> {
    private final Class     clazz;

    public GenericListJsonTypeHandler(Class clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.clazz = clazz;
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, List parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, JSONUtil.toJsonStr(parameter));
    }

    @Override
    public List getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return this.toObject(rs.getObject(columnName), clazz);
    }

    @Override
    public List getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return this.toObject(rs.getObject(columnIndex), clazz);
    }

    @Override
    public List getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return this.toObject(cs.getObject(columnIndex), clazz);
    }

    private List toObject(Object content, Class clazz) {
        if (content == null) {
            return null;
        }
        return JSONUtil.toList(content.toString(), clazz);

    }
}

11. 配置文件

11.1 application.yml

server:
  servlet:
    encoding:
      charset: UTF-8
      enabled: true

spring:
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher
  aop:
    proxy-target-class: true
    auto: true
  # 时间戳统一转换
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

mybatis-plus:
  mapper-locations: classpath:/mapper/**/*.xml
  type-aliases-package: com.wasu.mall
  configuration:
    # 驼峰转换 从数据库列名到Java属性驼峰命名的类似映射
    map-underscore-to-camel-case: true

11.2 application-dev.yml

server:
  port: 10900

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/san-zhi?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true&rewriteBatchedStatements=true
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # druid监控全局配置
      filter:
        stat:
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config.multi-statement-allow: true
      min-evictable-idle-time-millis: 300000
      test-on-borrow: false
      test-on-return: false
      initial-size: 10
      min-idle: 10
      max-wait: 60000
      pool-prepared-statements: true
      test-while-idle: true
      validation-query: select 1
      time-between-eviction-runs-millis: 60000
      max-pool-prepared-statement-per-connection-size: 20
      max-active: 100
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
  # redis 配置
  redis:
    host: 47.108.114.216
    port: 6379
    database: 1
    password: fB5LX@NyFI
    lettuce:
      pool:
        max-active: 8
        max-idle: 8
        min-idle: 0
        max-wait: 100ms

# 配置MyBatis日志
mybatis-plus:
  configuration:
    # 打印sql
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

12. 单元测试

package gov.yh.mall.mapper;

import cn.hutool.json.JSONUtil;
import gov.yh.mall.domain.Category;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;

import java.util.List;

@SpringBootTest
@ActiveProfiles({"dev"})
public class CategoryMapperTest {
    @Autowired
    private CategoryMapper categoryMapper;

    @Test
    public void testCategory() {
        List categories = categoryMapper.selectList(null);
        System.out.println(JSONUtil.toJsonPrettyStr(categories));
    }
}

 13. 项目结构

记录一个Invalid bound statement (not found)问题_第1张图片

 

14. 问题表现

执行时,激活dev配置文件

        请求接口/category/1,会调用CategoryController的getById方法,不调用CategoryService,调用成功

        执行单元测试CategoryMapperTest,不调用CategoryService,直接调CategoryMapper,调用成功,没有任何错误

        请求接口/category/tree,会调用CategoryController的tree方法,进而调用categoryService.listTree(),就会报错

2023-06-07 14:38:10.189 ERROR 34416 --- [io-10900-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): gov.yh.mall.service.CategoryService.listTree] with root cause

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): gov.yh.mall.service.CategoryService.listTree
	at org.apache.ibatis.binding.MapperMethod$SqlCommand.(MapperMethod.java:235) ~[mybatis-3.5.10.jar:3.5.10]
	at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.(MybatisMapperMethod.java:50) ~[mybatis-plus-core-3.5.3.1.jar:3.5.3.1]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.lambda$cachedInvoker$0(MybatisMapperProxy.java:111) ~[mybatis-plus-core-3.5.3.1.jar:3.5.3.1]
	at java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1660) ~[na:1.8.0_302]
	at com.baomidou.mybatisplus.core.toolkit.CollectionUtils.computeIfAbsent(CollectionUtils.java:115) ~[mybatis-plus-core-3.5.3.1.jar:3.5.3.1]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.cachedInvoker(MybatisMapperProxy.java:98) ~[mybatis-plus-core-3.5.3.1.jar:3.5.3.1]
	at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89) ~[mybatis-plus-core-3.5.3.1.jar:3.5.3.1]
	at com.sun.proxy.$Proxy77.listTree(Unknown Source) ~[na:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_302]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_302]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_302]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_302]
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.3.27.jar:5.3.27]
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) ~[spring-aop-5.3.27.jar:5.3.27]
	at com.sun.proxy.$Proxy79.listTree(Unknown Source) ~[na:na]
	at gov.yh.mall.controller.CategoryController.tree(CategoryController.java:29) ~[classes/:na]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_302]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_302]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_302]
	at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_302]
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) ~[spring-web-5.3.27.jar:5.3.27]
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150) ~[spring-web-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1072) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:965) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:529) ~[tomcat-embed-core-9.0.75.jar:4.0.FR]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) ~[spring-webmvc-5.3.27.jar:5.3.27]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:623) ~[tomcat-embed-core-9.0.75.jar:4.0.FR]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:209) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) ~[tomcat-embed-websocket-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) ~[spring-web-5.3.27.jar:5.3.27]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) ~[spring-web-5.3.27.jar:5.3.27]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) ~[spring-web-5.3.27.jar:5.3.27]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) ~[spring-web-5.3.27.jar:5.3.27]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:178) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:153) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) ~[tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:481) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:130) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:390) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:926) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1791) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.75.jar:9.0.75]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_302]

15. 强调一下

  1. SpringBoot2+MyBatisPlus+Druid的一个常规工程,没有任何config配置(参见项目结构截图)
  2. 网上各种Invalid bound statement (not found)的错误都查过了,什么改pom中的依赖、build选项、改mapper扫描位置和mapper-locations(见主启动类和配置文件)等等的,都试过了,没用
  3. 工程启动没问题,只要不调用CategoryService,就能成功,一调用这个CategoryService,就失败了,我反复对比了半天这个CategoryService,也没发现问题出在哪里。

总之,不知道哪里不对了,就一直有这个Invalid bound statement (not found),请大神给指点一下,到底哪里出问题了。

代码已经上传到Gitee了,地址:mall-check: 一个有问题的代码仓库

你可能感兴趣的:(IT--软件开发,spring,boot,mybatis,java)