项目中遇到了数据库需要分库的情况,已有功能都是按单库实现的。因此升级过程中有个很重要的一项,sql查询时要增加数据名参数。如果是sql值定义在mapper.xml中,那在接口增加一个数据库名参数即可(工作量也不小),但是通过BaseMapper执行的sql,没有办法手动增加库名,所以必须找一个办法既解决basemapper的问题最好也能一劳永逸。项目中用的是mybatis-plus(以下简称mp),我们需要在mp的生成sql动作到执行sql动作中间将数据库加到表名前面。mp有InnerInterceptor接口,增加库名可以在beforePrepare方法可以实现。下面是具体实现方案。
首先自定义一个注解@SubDatabase,把所有需要分库的表对应的实体上都加上把需要分库的实体都加上注解。项目启动(实现CommandLineRunner)时,将注解扫出来,把实体名称按驼峰转下划线改成表名,拼接一下,以备后面正则匹配用。
其次,定义一个ThreadLocal变量,再自定义个web过滤器HandlerInterceptorAdapter,在过滤器里面实现查询用户所归属的数据库,并把库名放到ThreadLocal变量中,便于后面增加库名。这里代码就不贴了,找地方放这个变量就行。
然后写个方法,用正则表达式匹配把sql中的表名替换成带库名的表名,代码如下图所示。
可能有些同学会问为什么不直接匹配表名,要加上left join等操作匹配呢。这里主要是防止有些参数可能和表名一致,导致sql异常的问题。
最后自定义个InnerInterceptor,利用反射修改sql。
注意点,此方案适用于单数据源多数据库场景,多数据源场景得根据具体情况调整。
如果各位有更好的方案,请多多指教,谢谢。