当前使用版本(必填,否则不予处理)
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则不会出现问题