在使用 MyBatis-Plus 时,采用动态表名策略后,selectPage 方法无法正常生效。
- MyBatis-Plus动态表名插件配置
- MyBatis-Plus动态表名失效原因
- MyBatis-Plus动态表名失效解决办法
MyBatis-Plus动态表名插件配置
以下是我项目中 MyBatis - Plus 的插件配置:
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.aspect.DataPermissionAspect;
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler;
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;import static org.dromara.common.core.utils.ServletUtils.getLanguage;/*** mybatis-plus配置类*/
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan("${mybatis-plus.mapperPackage}")
@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class)
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 数据权限处理interceptor.addInnerInterceptor(dataPermissionInterceptor());// 分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor());// 乐观锁插件interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());// 动态表名插件interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());return interceptor;}/*** 动态表名插件*/public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> switch (tableName) {case "device_type","device_model" -> tableName + "_" + getLanguage();default -> tableName;});return dynamicTableNameInnerInterceptor;}/*** 数据权限拦截器*/public PlusDataPermissionInterceptor dataPermissionInterceptor() {return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));}/*** 数据权限切面处理器*/@Beanpublic DataPermissionAspect dataPermissionAspect() {return new DataPermissionAspect();}/*** 分页插件,自动识别数据库类型*/public PaginationInnerInterceptor paginationInnerInterceptor() {PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 分页合理化paginationInnerInterceptor.setOverflow(true);return paginationInnerInterceptor;}/*** 乐观锁插件*/public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {return new OptimisticLockerInnerInterceptor();}/*** 元对象字段填充控制器*/@Beanpublic MetaObjectHandler metaObjectHandler() {return new InjectionMetaObjectHandler();}/*** 使用网卡信息绑定雪花生成器* 防止集群雪花ID重复*/@Beanpublic IdentifierGenerator idGenerator() {return new DefaultIdentifierGenerator(NetUtil.getLocalhost());}/*** 异常处理器*/@Beanpublic MybatisExceptionHandler mybatisExceptionHandler() {return new MybatisExceptionHandler();}/*** 初始化表对象处理器*/@Beanpublic PostInitTableInfoHandler postInitTableInfoHandler() {return new PlusPostInitTableInfoHandler();}}
MyBatis-Plus动态表名失效原因
在运用 com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
时,其 intercept
方法具备这样一种特性:每进行一次 SQL 查询操作,该方法便会对已加载的全部拦截器进行逐一遍历。在实际应用场景中,尤其是当你需要对 device_type
和 device_model
库开展动态表名查询时,这一机制可能会带来一些影响。
举例来说,若你希望依据语言环境动态拼接表名,当当前语言为 zh_CN
时,就会去查询 device_type_zh_CN
表。经过多次测试后发现,使用 selectById
或者 selectList
方法能够顺利完成查询任务,但使用 selectPage
方法时却会报错。经过深入分析,问题的根源在于拦截器的执行顺序存在不当之处。
实际上,在使用 com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor
执行 SQL 查询时,intercept
方法会对以 List
形式存储的拦截器进行遍历。通常情况下,第一个被调用的拦截器是分页查询拦截器,也就是 com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor
类中的 willDoQuery
方法。在这个 willDoQuery
方法里,会触发一次额外的数据库查询操作,而恰恰是这次额外的查询操作,最终引发了报错问题。
MyBatis-Plus动态表名失效解决办法
通过调整拦截器的顺序,把分页拦截器置于动态表名拦截器之后,便可有效解决当前面临的问题。
import cn.hutool.core.net.NetUtil;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.handlers.PostInitTableInfoHandler;
import com.baomidou.mybatisplus.core.incrementer.DefaultIdentifierGenerator;
import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.DynamicTableNameInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.dromara.common.core.factory.YmlPropertySourceFactory;
import org.dromara.common.core.utils.SpringUtils;
import org.dromara.common.mybatis.aspect.DataPermissionAspect;
import org.dromara.common.mybatis.handler.InjectionMetaObjectHandler;
import org.dromara.common.mybatis.handler.MybatisExceptionHandler;
import org.dromara.common.mybatis.handler.PlusPostInitTableInfoHandler;
import org.dromara.common.mybatis.interceptor.PlusDataPermissionInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.transaction.annotation.EnableTransactionManagement;import static org.dromara.common.core.utils.ServletUtils.getLanguage;/*** mybatis-plus配置类*/
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan("${mybatis-plus.mapperPackage}")
@PropertySource(value = "classpath:common-mybatis.yml", factory = YmlPropertySourceFactory.class)
public class MybatisPlusConfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor() {MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();// 数据权限处理interceptor.addInnerInterceptor(dataPermissionInterceptor());// 动态表名插件interceptor.addInnerInterceptor(dynamicTableNameInnerInterceptor());// 分页插件interceptor.addInnerInterceptor(paginationInnerInterceptor());// 乐观锁插件interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());return interceptor;}/*** 动态表名插件*/public DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor() {DynamicTableNameInnerInterceptor dynamicTableNameInnerInterceptor = new DynamicTableNameInnerInterceptor();dynamicTableNameInnerInterceptor.setTableNameHandler((sql, tableName) -> switch (tableName) {case "device_type","device_model" -> tableName + "_" + getLanguage();default -> tableName;});return dynamicTableNameInnerInterceptor;}/*** 数据权限拦截器*/public PlusDataPermissionInterceptor dataPermissionInterceptor() {return new PlusDataPermissionInterceptor(SpringUtils.getProperty("mybatis-plus.mapperPackage"));}/*** 数据权限切面处理器*/@Beanpublic DataPermissionAspect dataPermissionAspect() {return new DataPermissionAspect();}/*** 分页插件,自动识别数据库类型*/public PaginationInnerInterceptor paginationInnerInterceptor() {PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();// 分页合理化paginationInnerInterceptor.setOverflow(true);return paginationInnerInterceptor;}/*** 乐观锁插件*/public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor() {return new OptimisticLockerInnerInterceptor();}/*** 元对象字段填充控制器*/@Beanpublic MetaObjectHandler metaObjectHandler() {return new InjectionMetaObjectHandler();}/*** 使用网卡信息绑定雪花生成器* 防止集群雪花ID重复*/@Beanpublic IdentifierGenerator idGenerator() {return new DefaultIdentifierGenerator(NetUtil.getLocalhost());}/*** 异常处理器*/@Beanpublic MybatisExceptionHandler mybatisExceptionHandler() {return new MybatisExceptionHandler();}/*** 初始化表对象处理器*/@Beanpublic PostInitTableInfoHandler postInitTableInfoHandler() {return new PlusPostInitTableInfoHandler();}}