我最早接触MyBatis-Plus的多数据库配置,是因为一个特别尴尬的场景。公司有个老项目,Oracle跑了五年,突然要兼容MySQL,领导说“反正都是SQL,改改就行”。结果一改就是两周,不是SQL方言的问题,就是分页语句报错,最头疼的是同一个Mapper在不同数据库下返回的结果集还有细微差别。后来用了MyBatis-Plus的多数据库配置,才算把这事理顺。今天就把我踩过的坑和总结的经验掰开揉碎讲清楚。

先说最基础的一层:多数据库配置到底配什么。很多人以为就是改个URL和驱动,太天真了。MyBatis-Plus的核心是内置的SQL注入器,它会在启动时根据你配置的数据库类型,生成对应的分页、插入、更新语句。你如果不告诉它“我连的是Oracle”,它默认按MySQL语法生成,分页直接给你来个LIMIT,Oracle直接报ORA-00933。所以第一步,在你的application.yml里明确指定数据库类型,像这样:
这个db-type就是关键开关。它支持mysql、oracle、db2、h2、hsql、sqlite、postgresql、sqlserver、dm,还有国产的kingbase、gauss等。不同数据库的分页方言、主键生成策略、逻辑删除写法,全靠它来区分。
但光配置这个还不够。如果你的项目真的需要同时连多套数据库——比如一套MySQL存业务数据,一套PostgreSQL存分析数据——那事情就复杂了。这时候你面临的是“多数据源”问题,跟“多数据库方言”是两码事。多数据源意味着你要配两套连接池、两套事务管理器,甚至两套Mapper扫描路径。MyBatis-Plus官方推荐配合dynamic-datasource这个框架来用,它能让你在Service层用@DS注解动态切换数据源,就像这样:
public List
return userMapper.selectList(null);
public List
return userMapper.selectList(null);
这个注解可以打在类上也可以打在方法上,粒度很灵活。但有个坑我必须提醒你:如果你配了多数据源,又没指定db-type,MyBatis-Plus会拿默认的MySQL方言去生成SQL。到时候你切到PostgreSQL去执行分页,它照样给你生成LIMIT,PostgreSQL虽然也支持LIMIT,但如果你用了PostgreSQL的特定分页语法,比如OFFSET...FETCH,那就直接报错。所以多数据源场景下,每个数据源都要单独指定db-type。
有人会问,那我在多数据源配置里能不能为每个源单独设置db-type?答案是能,但你要用dynamic-datasource的自定义配置,在yml里给每个数据源配一个专属的MyBatis-Plus属性。比如:
url: jdbc:mysql://...
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:postgresql://...
driver-class-name: org.postgresql.Driver
注意,这个mybatis-plus配置是dynamic-datasource框架提供的扩展,不是原生MyBatis-Plus就有的。你需要在pom里引入dynamic-datasource-spring-boot-starter,版本建议3.5.0以上。我见过很多人在这一步卡住,因为网上教程大多只讲单数据源配置,多数据源加方言切换的资料特别少。
再说一个更隐蔽的坑:分页插件。MyBatis-Plus的分页插件PaginationInnerInterceptor,默认情况下它会自动识别当前数据源的数据库类型来做分页优化。但如果你用了多数据源,且没有给每个数据源指定dbType,这个插件可能取到的是第一个数据源的类型,然后所有数据源分页都按那个类型处理。解决办法是在配置分页插件时,手动指定数据库类型列表:
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
PaginationInnerInterceptor mysqlInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
PaginationInnerInterceptor pgInterceptor = new PaginationInnerInterceptor(DbType.POSTGRESQL);
interceptor.addInnerInterceptor(mysqlInterceptor);
interceptor.addInnerInterceptor(pgInterceptor);
但注意,这样配置后,分页插件会根据实际执行的SQL去匹配对应的方言,匹配不上就会报错。所以你得保证每个数据源都用到了对应的分页拦截器。还有个更简单的做法:直接不配置分页拦截器,用MyBatis-Plus内置的方言自动识别,前提是你在yml里给每个数据源都配好了db-type。我个人推荐后者,少写代码,少出错。
接下来说说主键策略。默认情况下,MyBatis-Plus的IdType是ASSIGNID,也就是雪花算法生成Long型ID。这在MySQL和PostgreSQL下都没问题,但如果你连的是Oracle,Oracle没有自增列,你得用序列。MyBatis-Plus支持IdType.INPUT,让你自己传入ID,或者配合Oracle的序列用。但如果你在多数据源环境下,一个表用自增,一个表用序列,那就不只是配置的问题了,你得在实体类上针对不同数据源写不同的主键策略。这个比较麻烦,我建议的做法是:统一用ASSIGN_ID,让MyBatis-Plus生成雪花ID,不管什么数据库都能存,只是Oracle你得把字段类型改成NUMBER(20)。这样最省心。
还有逻辑删除的坑。MyBatis-Plus的逻辑删除默认是全局配置,比如logic-delete-field: deleted,logic-delete-value: 1,logic-not-delete-value: 0。这在MySQL下没问题,但Oracle里没有TINYINT类型,你的deleted字段得用NUMBER(1)存0和1,这个倒不影响。但如果你在PostgreSQL里用了布尔类型BOOLEAN,那逻辑删除的值就得配成true/false,而不是1/0。所以多数据库环境下,逻辑删除的配置也得分开。好在MyBatis-Plus支持在@TableLogic注解上单独指定值:
@TableLogic(value = "false", delval = "true")
private Boolean deleted;
这样不管全局配置是什么,这个字段就按注解来。但这种方法只能解决字段级别的差异,如果整个数据源的逻辑删除配置都不一样,还是得在yml里针对数据源单独设置,这个跟db-type一样,属于dynamic-datasource的扩展配置。
说一个我实际项目里用的方案。我们有个服务要同时连Oracle和MySQL,Oracle是主库,MySQL是分析库。我用的就是dynamic-datasource + MyBatis-Plus,配置上给Oracle指定db-type: oracle,MySQL指定db-type: mysql。分页插件用自动识别,主键全用雪花ID,逻辑删除字段统一用NUMBER(1)存0和1。跑了大半年,没出过方言问题。唯一的坑是事务,多数据源下默认事务只针对主数据源,如果你在Service层同时操作两个库,得用@DSTransactional注解,这个注解是dynamic-datasource提供的,支持分布式事务,但底层用的是本地消息表或者XA,性能一般,建议只在必要时用。
如果你跟我一样,项目里既有老的Oracle又有新的MySQL,多数据库配置这件事千万别偷懒。别以为写SQL的时候注意点就行,MyBatis-Plus帮你自动生成的SQL才是大头。配置好db-type,选对分页插件,统一主键和逻辑删除策略,这三个点做到位,多数据库切换基本就是改个URL的事。别问我怎么知道的,我当年改配置改到凌晨三点,发现就是yml里少写了一个db-type,那个心情,你懂的。


