基于mybatisPlus的多库支持实践
开发中, 存在一种这样的业务场景,就是我们的应用要支持多库,同一个应用,我a项目用mysql,b项目用oracle
收获
充分利用sql的特性,和代码的特性,以及框架本身的特性,用最小的代价,来完成应用对多库的支持
多库支持原则
- 最小代价原则,充分利用sql的特性,充分利用代码的特性,利用框架的特性
分析
- 在使用mybatis plus过程中,有两种dao风格,一种是dsl风格,使用wrapper类,一种是需要join的sql,一般使用xml里面配置
- 从sql的角度入手,无论是oracle还是mysql,虽然差异也是有的,但是sql本身也是有规范的,所以,大部分简单sql本身就是支持oracle和mysql的,使用了函数的sql,或者groupby的sql一般是不能支持多db的,只需要关注差异化的sql即可
- 分页,分页每个数据库都不一样,mysql和postgresql完全一样,但是oracle比较古老,分页的sql也相对复杂,所以,分页各不一样
方案
差异语句,使用wrapper
一般wrapper使用了last
,apply
等函数的,一定是需要差异化处理的,这个时候,可以,自己配置springbootde的参数,根据参数来确定使用那种语句,而且多个
地方使用同一个语句的话,还可以抽象成一个函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| private String substrNodeFlag() { String subStr; DbType dbType = plbPaginationProperties.getPagination(); switch (dbType) { case ORACLE: case DB2: { subStr = "node_Flag = substr(node_Flag,0," + (Node.PUSH.getIndex() * 2) + ")||" + NodeStatus.Push.SUCCESS.code() + "||substr(node_Flag," + (Node.PUSH.getIndex() * 2 + 2) + ")"; break; } case MYSQL: { subStr = "node_Flag = substring(node_Flag,0," + (Node.PUSH.getIndex() * 2) + ")||" + NodeStatus.Push.SUCCESS.code() + "||substring(node_Flag," + (Node.PUSH.getIndex() * 2 + 2) + ")"; break; } default: { throw new IllegalArgumentException(String.format("unSupport dbType [%s]", dbType)); } } return subStr; }
|
差异化sql,分开成不同的xml
因为mybatis允许一个dao文件,对应多个xml,如果一个xml文件里面的sql需要差异化处理,可以非差异化的sql放到一个文件里面,然后差异化的sql,放到各自的文件夹里面,
最后通过配置来区分
- oracle和mysql都可以用的文件放到这里
- mysql专用的sql
- oracle专用的sql
配置
1 2 3 4 5
| mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl map-underscore-to-camel-case: true mapperLocations: classpath*:mappers/*.xml,classpath*:oraclemappers/*.xml
|
分页插件
springboot里面配置一个参数,然后通过不同的参数,构建不同数据库的分页插件即可
1 2 3 4 5 6 7 8 9 10 11
| @Autowired private PaginationProperties paginationProperties;
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(CustomDataPermissionInterceptor customDataPermissionInterceptor) { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(customDataPermissionInterceptor); interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(paginationProperties.getPagination())); return interceptor; }
|
id自增问题
因为mysql很容易实现id自增,然后oracle id自增比较麻烦,所以,使用oracle的时候,可以自定义一个id序列,如使用雪花算法
1 2 3 4 5
| @Bean @ConditionalOnProperty(prefix = "ladon", matchIfMissing = true, name = "pagination", havingValue = "ORACLE") public IdentifierGenerator customIdGenerator() { return new CustomIdGenerator(); }
|
小技巧
- 如果查看项目里面那些地方有差异化sql,直接使用全文检索即可