当前使用版本(必填,否则不予处理)

3.5.3.1

该问题是如何引起的?(确定最新版也有问题再提!!!)

在 Spring 项目与 Mybatis Plus 结合使用并使用纯注解开发时发生 Mapper 无法注入问题

重现步骤(如果有就写完整)

代码过长,可以参考本人 repo 代码 在导入 Spring、Mybatis Plus 相关依赖后,使用 Druid 连接池(也使用过 c3p0)对所有配置使用注解进行配置,配置相关 Scan 注解后依旧无法使用 Mapper 接口进行数据库操作,在使用相关方法时发生 mapper is null 问题

相关代码配置

在 MybatisConfiguration 中设置了相关配置 Bean 以及 MapperScan 注解 UtilTest 中无法通过,并且已经证实在项目中的 Controller 也无法使用,报错是一致的 java.lang.NullPointerException: Cannot invoke "com.mou.springmvcdemo.mapper.AccountMapper.selectByMap(java.util.Map)" because "this.accountMapper" is null 我没有找到关于注解开发在配置完整的情况下时 Mybatis Plus 的 Mapper 发生 NullPointerException 时如何应对,因此只好来发 Issue 希望能得到解答,因为我并不清楚这是 Mybatis Plus 的原因还是我自身配置的问题,并且也未能在文档中找到必要的配置注解(若有请求指明)

项目配置与测试代码: JdbcConfig.java

package com.mou.springmvcdemo.config;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

@PropertySource("classpath:/jdbc.properties")
@Data
@Configuration
public class JdbcConfig {
    @Value("${jdbc.driver}") //com.mysql.cj.jdbc.Driver
    String driver;
    @Value("${jdbc.url}")
    String url;
    @Value("${jdbc.username}")
    String username;
    @Value("${jdbc.password}")
    String password;
}

MybatisConfiguration.java

package com.mou.springmvcdemo.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.commons.dbutils.QueryRunner;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;

@Configuration
@MapperScan(value = "com.mou.springmvcdemo.mapper")
public class MybatisConfiguration extends com.baomidou.mybatisplus.core.MybatisConfiguration {
    public MybatisConfiguration() {
        super();
        this.setMapUnderscoreToCamelCase(true);
    }

    @Bean(name = "dataSource")
    public DataSource createDataSource(JdbcConfig jdbcConfig) {
        try {
            DruidDataSource ds = new DruidDataSource();
            ds.setUsername(jdbcConfig.getUsername());
            ds.setPassword(jdbcConfig.getPassword());
            ds.setDriverClassName(jdbcConfig.getDriver());
            ds.setUrl(jdbcConfig.getUrl());
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Bean(name = "runner")
    public QueryRunner creatQueryRunner(@Qualifier("dataSource") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    @Bean
    public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
        MybatisSqlSessionFactoryBean mybatisPlus  = new MybatisSqlSessionFactoryBean();
        mybatisPlus.setDataSource(dataSource);
        return mybatisPlus;
    }

    @Bean
     public PaginationInnerInterceptor getPaginationInterceptor(){
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor();
        paginationInterceptor.setDbType(DbType.MYSQL);
        return paginationInterceptor;
    }
}

SpringConfiguration.java

package com.mou.springmvcdemo.config;

import io.pebbletemplates.pebble.PebbleEngine;
import io.pebbletemplates.pebble.loader.Servlet5Loader;
import io.pebbletemplates.spring.servlet.PebbleViewResolver;
import jakarta.servlet.ServletContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.*;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@Import(MybatisConfiguration.class)
@ComponentScan("com.mou")
@PropertySource("classpath:jdbc.properties")
@EnableAspectJAutoProxy
@EnableTransactionManagement()
@EnableWebMvc
public class SpringConfiguration {
    @Bean
    WebMvcConfigurer webMvcConfigurer(){
        return new WebMvcConfigurer() {
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/static/**").addResourceLocations("/static/");
            }
        };
    }

    @Bean
    ViewResolver createViewResolver(@Autowired ServletContext servletContext){
        var engine = new PebbleEngine.Builder().autoEscaping(true)
                .cacheActive(false) // no cache
                .loader(new Servlet5Loader(servletContext)) // set servlet 5 loader
                .build();
        var viewResolver = new PebbleViewResolver(engine);
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix("");
        return viewResolver;
    }
}

Account.java

package com.mou.springmvcdemo.domain;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import lombok.Data;
import org.springframework.stereotype.Component;

import java.io.Serializable;

@Data
@Component
@TableName("Account")
public class Account extends Model<Account> implements Serializable {
    @TableId(type = IdType.AUTO)
    private Integer id;
    private String name;
    private String job;

    public Account(Integer id, String name, String job) {
        this.id = id;
        this.name = name;
        this.job = job;
    }

    public Account(){}

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }
}

AccountMapper.java

package com.mou.springmvcdemo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.mou.springmvcdemo.domain.Account;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;

@Mapper
@Repository
public interface AccountMapper extends BaseMapper<Account> {
}

UtilTest.java

import com.mou.springmvcdemo.domain.Account;
import com.mou.springmvcdemo.mapper.AccountMapper;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

@Component
public class UtilTest {
    @Autowired
    AccountMapper accountMapper;

    @Test
    public void testMybatis() {
        List<Account> accounts = accountMapper.selectByMap(new HashMap<>() {
            {
                put("username", "mou");
                put("password", "123456");
            }
        });
        System.out.println(accounts.get(1).getJob());
    }
}

报错信息

java.lang.NullPointerException: Cannot invoke "com.mou.springmvcdemo.mapper.AccountMapper.selectByMap(java.util.Map)" because "this.accountMapper" is null

    at UtilTest.testMybatis(UtilTest.java:33)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.BlockJUnit4ClassRunner$1.evaluate(BlockJUnit4ClassRunner.java:100)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:103)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:63)
    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329)
    at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293)
    at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:413)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
    at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)


Process finished with exit code -1

Comment From: miemieYaho

https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java

Comment From: XiaoMouz

https://github.com/baomidou/mybatis-plus/blob/3.0/mybatis-plus-boot-starter/src/main/java/com/baomidou/mybatisplus/autoconfigure/MybatisPlusAutoConfiguration.java

我需要实现哪些东西,是要照着这里去替换 sqlSessionFactory 吗(?

Comment From: bchengwang

新版本 3.5.5 搭配 Java21 也会出现这个问题。如果搭配Java17则不会出现问题