常见问题
本文档收录了 MyBatis-Plus 使用时遇到的各种常见问题,如果您在使用 MyBatis-Plus 的时候遇到了问题,请您优先查看本文档。
以下三种方式选择一种即可:
- Spring MVC 配置(参考 mybatisplus-spring-mvc )
- Spring Boot 配置(参考 mybatisplus-spring-boot )
- 将配置文件放在 resource 文件夹中
- 对于 Maven 项目,可指定 POM 文件的 resource
- 修改类型tinyint(1)为tinyint(2)或者int
-
需要修改请求连接添加参数
tinyInt1isBit=false
,如下: - 使用自定义的 Map 对象或者普通的 Java 对象来传递参数。
- 通过在方法参数中使用 @Param(“page”) int page, @Param(“size”) int size 来传递页码和每页大小。
- SQL 长度存在限制:处理海量数据时,单条 SQL 可能无法执行或者容易引起内存泄漏、JDBC 连接超时等问题。
- 不同数据库的单条 SQL 批量语法不一致,不利于通用性。
- 解决方案:采用循环预处理批量提交的方法。虽然性能比单条 SQL 插入稍慢,但可以解决上述问题。
- 将 Druid 数据源升级至 1.1.21 版本以上,以解决此问题。
- 如果无法升级 Druid 数据源,可以选择保持 MyBatis Plus 版本在 3.1.0 及之前的版本。
- 若要继续使用最新的 MyBatis Plus 版本,可以考虑更换其他兼容新版 JDBC 的数据源,以避免出现此异常。
- 升级您的 JDBC 驱动至支持 JDBC 4.2 API 的版本。
- 如果无法升级 JDBC 驱动,可以考虑将 MyBatis Plus 版本回滚至 3.1.0 或之前的版本。
- 将 Spring Boot 降级至 2.1.x 版本,或升级至 2.2.1 版本以上。建议直接升级至 Spring Boot 2.2.2 版本以获取更好的稳定性和修复。
-
去除
spring-boot-maven-plugin
插件进行打包,或者 - 升级至 MyBatis Plus 3.3.2 版本。您可以参考示例 分离打包 进行操作。
-
如果您使用了
@TableField
注解,并希望保持全局格式化,需要设置参数keepGlobalFormat=true
。 -
您也可以在
@TableField
注解中直接指定固定格式的数据库关键词,例如@TableField("'status'")
。 -
使用变量
_databaseId
: -
使用标签属性
databaseId
: - 查看本机hostname
- 编辑系统hosts文件,将本机hostname写入至hosts文件
- 原因:驱动配置不兼容
使用
transient
修饰
private transient String noColumn;
使用
static
修饰
private static String noColumn;
使用
TableField
注解
@TableField(exist=false)private String noColumn;
使用
transient
修饰需要排除的父类属性
/** * 忽略父类 createTime 字段映射 */private transient String createTime;
出现该异常通常是由于配置不正确或者 Mapper 没有被正确扫描到导致的。解决方案如下:
检查是否存在 jar 包冲突。
检查 Mapper.java 的扫描路径:
方法一:在
Configuration
类上使用注解
MapperScan
@
Configuration@MapperScan("com.yourpackage.*.mapper")public class YourConfigClass{ ...}
方法二:在
Configuration
类中配置
MapperScannerConfigurer
(
查看示例
)
@Beanpublic MapperScannerConfigurer mapperScannerConfigurer(){ MapperScannerConfigurer scannerConfigurer = new MapperScannerConfigurer(); // 可以通过环境变量获取你的 mapper 路径,这样 mapper 扫描可以通过配置文件配置了 scannerConfigurer.setBasePackage("com.yourpackage.*.mapper"); return scannerConfigurer;}
检查是否指定了主键。如果未指定,将导致
selectById
相关 ID 无法操作。请使用注解
@TableId
注解表 ID 主键。当然,
@TableId
注解可以省略,但是你的主键必须叫 id(忽略大小写)。
不要使用原生的 SqlSessionFactory,请使用 MybatisSqlSessionFactory。
检查是否自定义了 SqlInjector,是否复写了
getMethodList()
方法。在该方法中是否注入了你需要的方法(可参考 DefaultSqlInjector)。
IDEA 默认的 build 步骤可能会导致 mapper 文件无法正常编译到对应的 resources 文件夹中。请检查 build 后相关资源文件夹是否有对应的 XML 文件。如果没有,请调整 IDEA 的 build 设置。推荐调整为 Maven 或 Gradle 的 build。
问题描述:指在 XML 中里面自定义 SQL,却无法调用。本功能同
MyBatis
一样需要配置 XML 扫描路径:
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="typeAliasesPackage" value="xxx.entity" /> <property name="mapperLocations" value="classpath*:/mybatis/*/*.xml"/> ...</bean>
mybatis-plus: mapper-locations: classpath*:/mapper/**/*.xml
对于
IDEA
系列编辑器,XML 文件是不能放在 java 文件夹中的,IDEA 默认不会编译源码文件夹中的 XML 文件,可以参照以下方式解决:
<build> <resources> <resource> <!-- xml放在java目录下--> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> <!--指定资源的位置(xml放在resources下,可以不用指定)--> <resource> <directory>src/main/resources</directory> </resource> </
resources></build>
java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class
MapperScan 需要排除 com.baomidou.mybatisplus.mapper.BaseMapper 类 及其 子类(自定义公共 Mapper),比如:
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface SuperMapper<T> extends BaseMapper<T> { // your methods}
Injection of autowired
原因:低版本不支持泛型注入,请升级 Spring 版本到 4+ 以上。
java.lang.NoSuchMethodError: org.apache.ibatis.session.Configuration.getDefaultScriptingLanguageInstance() Lorg/apache/ibatis/scripting/LanguageDriver
版本引入问题:3.4.1 版本里没有,3.4.2 里面才有!
检查是不是用了
long
而不是
Long
!
JavaScript 无法处理 Java 的长整型 Long 导致精度丢失,具体表现为主键最后两位永远为 0,解决思路: Long 转为 String 返回
FastJson 处理方式
@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonHttpMessageConverter fastJsonConverter = new FastJsonHttpMessageConverter(); FastJsonConfig fjc = new FastJsonConfig(); // 配置序列化策略 fjc.setSerializerFeatures(SerializerFeature.BrowserCompatible); fastJsonConverter.setFastJsonConfig(fjc); converters.add(fastJsonConverter);}
JackJson 处理方式
// 注解处理,这里可以配置公共 baseEntity 处理@JsonSerialize(using=ToStringSerializer.class)public long getId() { return id;}
// 全局配置序列化返回 JSON 处理final ObjectMapper objectMapper = new ObjectMapper();SimpleModule simpleModule = new SimpleModule();simpleModule.addSerializer(Long.class, ToStringSerializer.instance);objectMapper.registerModule(simpleModule);
方式二:调整字段验证注解
根据具体情况,在需要更新的字段中调整验证注解,如验证非空:
@TableField(strategy=FieldStrategy.NOT_EMPTY)
方式三:使用
UpdateWrapper
(3.x)
使用以下方法来进行更新或插入操作:
//updateAllColumnById(entity) // 全部字段更新: 3.0已经移除mapper.update( new User().setName("mp").setAge(3), Wrappers.<User>lambdaUpdate() .set(User::getEmail, null) //把email设置成null .eq(User::getId, 2));//也可以参考下面这种写法mapper.update( null, Wrappers.<User>lambdaUpdate() .set(User::getAge, 3) .set(User::getName, "mp") .set(User::getEmail, null) //把email设置成null .eq(User::getId, 2));
默认mysql驱动会把tinyint(1)字段映射为boolean: 0=false, 非0=true
MyBatis 是不会自动处理该映射,如果不想把tinyint(1)映射为boolean类型:
jdbc:mysql://127.0.0.1:3306/mp?tinyInt1isBit=false
原因:配了 2 个分页拦截器! 检查配置文件或者代码,只留一个!
insert 后主键会自动 set 到实体的 ID 字段,所以你只需要 getId() 就好
EntityWrapper.sqlSelect 配置你想要查询的字段
EntityWrapper<H2User> ew = new EntityWrapper<>();ew.setSqlSelect("test_id as id, name, age");//只查询3个字段List<H2User> list = userService.selectList(ew);for(H2User u:list){ Assert.assertNotNull(u.getId()); Assert.assertNotNull(u.getName()); Assert.assertNull(u.getPrice()); // 这个字段没有查询出来}
//3.xmapper.selectList( Wrappers.<User>lambdaQuery() .select(User::getId, User::getName));//或者使用QueryWrappermapper.selectList( new QueryWrapper<User>() .select("id","name"));
我们建议缓存放到 service 层,你可以自定义自己的 BaseServiceImpl 重写注解父类方法,继承自己的实现。
如果你按照 mybatis 的方式配置第三方二级缓存,并且使用 2.0.9 以上的版本,则会发现自带的方法无法更新缓存内容,那么请按如下方式解决(二选一):
1.在代码中 mybatis 的 mapper 层添加缓存注释,声明 implementation 或 eviction 的值为 cache 接口的实现类
@CacheNamespace(implementation=MybatisRedisCache.class,eviction=MybatisRedisCache.class)public interface DataResourceMapper extends BaseMapper<DataResource>{}
2.在对应的 mapper.xml 中将原有注释修改为链接式声明,以保证 xml 文件里的缓存能够正常
<cache-ref namespace="com.mst.cms.dao.DataResourceMapper"></cache-ref>
配置 jdbcTypeForNull=NULL Spring Bean 配置方式:
MybatisConfiguration configuration = new MybatisConfiguration();configuration.setDefaultScriptingLanguage(MybatisXMLLanguageDriver.class);configuration.setJdbcTypeForNull(JdbcType.NULL);configuration.setMapUnderscoreToCamelCase(true);//开启下划线转驼峰sqlSessionFactory.setConfiguration(configuration);
yml 配置
mybatis-plus: configuration: jdbc-type-for-null: 'null'
在自定义 SQL 中,由于 Page 对象继承自 RowBounds,在 Mapper 中无法直接获取。为了解决这个问题,请考虑以下替代方案:
这些方法可以帮助您在自定义 SQL 中正确传递参数,确保代码的顺利运行。
当使用
resultType="java.util.Map"
时,您可以通过以下步骤在 Spring Boot 中实现下划线自动转换为驼峰:
在您的 Spring Boot 项目中创建一个配置类。
@Configurationpublic class MybatisConfigurationCustomizer { @Bean public ConfigurationCustomizer configurationCustomizer
() { return configuration -> configuration.setObjectWrapperFactory(new MybatisMapWrapperFactory()); }}
通过这样配置,您就可以实现自动将 Map 中的下划线转换为驼峰形式。这样,在 MyBatis 查询结果映射到 Map 对象时,键名会自动进行转换,使得您在代码中更加便捷地访问数据。
您可以通过以下方式在 Wrapper 中使用
limit
限制 SQL 结果集:
// 仅获取一条数据wrapper.last("limit 1");
这段代码会在 SQL 语句末尾添加 limit 1,以限制结果集返回的行数为 1。
将通用的批量插入操作放在 Service 层处理有以下原因:
如果您想使用单条 SQL 插入方案,可以自行注入选装方法 insertbatchsomecolumn ,或查看 SQL 注入器 中提供的方法。
在 MyBatis Plus 3.x 中,不再提供自动识别关键字进行处理的功能。处理数据库关键字的方法有以下几种:
不同数据库对关键字的处理方式不同,因此很难维护。在数据库设计时,建议避免使用关键字作为字段名或表名。
如果必须使用关键字,可以通过在字段或表名前后添加反引号(`)来进行处理,如下所示:
@TableField(value = "`status`")private Boolean status;
综上所述,为了避免出现问题,建议尽量避免在数据库设计中使用关键字。
针对 MyBatis Plus 3.1.1 及更高版本出现的问题:
现象:在单元测试中没有问题,但是在启动服务器进行调试时出现该异常。
原因:在 3.1.1 版本及以后的版本中,针对字段缓存进行了优化,使用
.class
作为键来替换了原来的类名(className)。然而,当使用 dev-tools 时,可能会导致
.class
使用不同的类加载器加载,从而导致出现找不到属性的情况。
解决方案:移除 dev-tools 插件。这样可以避免使用不同的类加载器加载
.class
,从而解决该异常问题。
针对 MyBatis Plus 3.1.1 及更高版本出现的问题:
现象:在集成 Druid 数据源时,升级到 3.1.1 版本及之后的版本后,出现错误:java.sql.SQLFeatureNotSupportedException。而在 3.1.0 版本之前没有此问题。
原因:MyBatis Plus 3.1.1 版本及更高版本采用了新版 JDBC,对于新的日期类型(如 LocalDateTime)处理方式进行了升级。然而,Druid 在 1.1.21 版本之前不支持此特性,导致出现此异常。详细信息请参考 相关问题 。
解决方案:
如果您将 MyBatis Plus 从 3.1.0 及以下版本升级到较高版本,且遇到新日期类型(如 LocalDateTime)无法映射的报错,可能是由于以下原因:
MP_3.1.0 及之前版本依赖的是 MyBatis 3.5.0。而 MP_3.1.1 升级了 MyBatis 的依赖到 3.5.1,而在 MyBatis 3.5.1 中,新日期类型需要 JDBC 驱动支持 JDBC 4.2 API。
如果您的 JDBC 驱动版本不支持 JDBC 4.2 API,就会出现无法映射新日期类型的报错。
参考 MyBatis 官方博客 的内容:
There is one backward incompatible changes since 3.5.0. Because of the fix for #1478 , LocalDateTypeHandler, LocalTimeTypeHandler and LocalDateTimeTypeHandler now require a JDBC driver that supports JDBC 4.2 API. [EDIT] These type handlers no longer work with Druid. Please see #1516 .
解决方案:
如果您在将 Spring Boot 版本从 2.2.0 升级到更高版本时遇到此问题,可能是由于以下原因:
现象:在本地启动时没有问题,但是将打成 war 包部署到服务器时出现此问题。
原因:在 Spring Boot 2.2.0 中存在构造器注入的问题,导致 MyBatis 的私有构造器无法正确绑定属性,进而导致依赖 MyBatis 的框架(如 MyBatis Plus)报错。详细信息请参考 相关 issue 。此问题已在 Spring Boot 2.2.1 中得到修复。
解决方案:
现象:在开发工具中运行没有问题,但是将项目打包部署到服务器后,执行 Lambda 表达式时出现 ClassNotFoundException。
针对 MyBatis Plus 3.3.2 以下版本,如果在分离打包部署时出现 ClassNotFoundException 的问题,可能是因为在执行反序列化操作时,类加载器发生了错误。
解决方案:
您可以通过以下两种方式来启用 MyBatis 内部的日志记录:
方式一:
在您的
application.yml
或
application.properties
文件中添加以下配置:
mybatis-plus: configuration: # 如果项目无日志框架,可以考虑指定为 org.apache.ibatis.logging.stdout.StdOutImpl (请勿在实际生产中使用). log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
这将使用 MyBatis 内置的 StdOutImpl 日志记录实现将日志输出到控制台。
方式二:
在您的
application.yml
或
application.properties
文件中增加日志级别配置,以指定特定包的日志级别。例如:
logging: level: com.baomidou.example.mapper: debug
这将指定 com.baomidou.example.mapper 包下的日志级别为 debug。您可以根据需要调整级别。
通过以上配置,您可以启用 MyBatis 内部的日志记录,并根据需要调整日志级别。
如果您想要在更新操作时对某个字段进行自增操作,您可以使用 MyBatis Plus 提供的
Wrapper
进行更新。以下是一种可行的解决方案:
UpdateWrapper<Entity> wrapper = new UpdateWrapper<>();wrapper.setSql("column = column + 1");
// 或者使用 Lambda 表达式UpdateWrapper<Entity> wrapper = new UpdateWrapper<>();wrapper.setSql("column = column + 1");
// 调用 update 方法baseMapper.update(null, wrapper);
这样可以在更新时对字段进行自增操作。请注意,需要在
setSql
方法中直接指定 SQL 更新语句。
如果您想要全局处理数据库关键词,可以使用 MyBatis Plus 提供的全局配置。以下是配置示例(以 MySQL 为例):
mybatis-plus: global-config: db-config: column-format: "`%s`"
这样配置后,MyBatis Plus 将会在生成 SQL 语句时,对数据库字段名称使用反引号(“)进行包裹,以确保不与数据库关键词冲突。
需要注意的是:
通过以上配置,您可以全局处理数据库关键词,确保生成的 SQL 语句不会受到关键词影响。
如果您想要在 XML 中根据数据库类型选择不同的 SQL 片段,您可以使用 MyBatis Plus 提供的
database-id
参数。以下是配置示例(以 MySQL 为例):
mybatis-plus: configuration: database-id: mysql
这样配置后,MyBatis Plus 将会在执行 SQL 语句时,根据
database-id
参数选择不同的 SQL 片段。
您可以根据不同的写法在 XML 中进行判断:
<
select id="selectAllNames" resultType="java.lang.String">select<choose> <when test="_databaseId == 'mysql'"> GROUP_CONCAT(name SEPARATOR ',') </when> <otherwise> array_to_string(ARRAY_AGG(name), ',') </otherwise></choose>form user</select>
<select id="selectAllNames" databaseId="mysql" resultType="java.lang.String"> select GROUP_CONCAT(name SEPARATOR ',') form user</select><select id="selectAllNames" databaseId="pgsql" resultType="java.lang.String"> select array_to_string(ARRAY_AGG(name), ',') form user</select>
通过以上配置,您可以根据不同的数据库类型选择不同的 SQL 片段。
MyBatis Plus 不支持复合主键并强制使用唯一的 ID,这是出于以下考虑:
增加了表与表之间的相互依赖性:使用复合主键会使表与表之间的关系更加复杂,增加了维护和管理的难度。
增加了数据复杂的约束、规则:复合主键会增加数据的约束和规则,例如需要约束唯一性,而完全可以使用联合索引来实现。
增加了更新数据的限制:在更新数据时,需要更新所有复合主键的值,这增加了更新操作的限制和复杂性。
严重的数据冗余和更新异常问题:复合主键可能导致数据冗余和更新异常的问题,特别是在大型系统中,可能会出现更新异常的情况。
性能问题:使用复合主键时,查询某个 ID 时无法使用索引,会导致性能下降。
综上所述,虽然使用复合主键可以省去一个 ID 字段,但是这种做法的缺点大于优点,不建议也不推荐这样做。MyBatis Plus 坚持使用唯一的 ID,以保证数据管理的简单性、可维护性和性能。
初始化默认雪花ID导致
# 示例,例如我的hostname名字为nieqiurong-PC,那我在hosts文件配置如下即可127.0.0.1 localhost nieqiurong-PC
检查数据库连接池初始化
碰到过使用hikari在linux系统下启动缓慢
解决方案:
在java启动命令中指定 -Djava.security.egd=file:/dev/urandom把获取随机数的方式从 /dev/random改为/dev/urandom
示例: java -Djava.security.egd=file:/dev/urandom -jar xxxx.jar
解决方案:驱动连接去掉
rewriteBatchedStatements=true
配置