Mybatis plus 动态表名插件开发

Mybatis plus 动态表名插件开发

开发背景:表进行数据归档时,结构一致,但调用的时候又不想重复复制相关的代码逻辑,所以开发了个动态修改表名的插件。虽然说高版本的 mybatis plus 提供了同样的插件,但是需要升级版本。高版本的 mybatis plus 改动太大,升级的话有很大的风险,所以就自己开发了一个插件。

插件

mybatis 插件

import com.baomidou.mybatisplus.core.toolkit.PluginUtils;import com.rookie.mybatis.study.entity.DynamicTableInfo;import com.rookie.mybatis.study.entity.DynamicTableName;import net.sf.jsqlparser.parser.CCJSqlParserUtil;import net.sf.jsqlparser.schema.Table;import net.sf.jsqlparser.statement.Statement;import net.sf.jsqlparser.statement.delete.Delete;import net.sf.jsqlparser.statement.insert.Insert;import net.sf.jsqlparser.statement.select.Join;import net.sf.jsqlparser.statement.select.PlainSelect;import net.sf.jsqlparser.statement.select.Select;import net.sf.jsqlparser.statement.update.Update;import org.apache.commons.lang3.ObjectUtils;import org.apache.commons.lang3.StringUtils;import org.apache.ibatis.executor.statement.StatementHandler;import org.apache.ibatis.mapping.BoundSql;import org.apache.ibatis.mapping.MappedStatement;import org.apache.ibatis.mapping.SqlCommandType;import org.apache.ibatis.plugin.*;import org.apache.ibatis.reflection.MetaObject;import org.apache.ibatis.reflection.SystemMetaObject;import java.sql.Connection;import java.util.ArrayList;import java.util.List;import java.util.Map;import java.util.Properties;import java.util.stream.Collectors;/** * @author rookie */@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})public class DynamicTableInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { StatementHandler statementHandler = PluginUtils.realTarget(invocation.getTarget()); MetaObject metaObject = SystemMetaObject.forObject(statementHandler); MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");// 只处理增删改查语句 if (SqlCommandType.SELECT != mappedStatement.getSqlCommandType() && SqlCommandType.INSERT != mappedStatement.getSqlCommandType() && SqlCommandType.DELETE != mappedStatement.getSqlCommandType() && SqlCommandType.UPDATE != mappedStatement.getSqlCommandType()) { return invocation.proceed(); } // 针对定义了rowBounds,做为mapper接口方法的参数 BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); Object paramObj = boundSql.getParameterObject(); DynamicTableInfo info = extractInfo(paramObj); Map tableMap = TablePluginContainer.TABLE_NAME_THREAD_LOCAL.get();// 如果参数或者 ThreadLocal 中带有需要替换的表名 boolean replaceProcess = (info == null || ObjectUtils.isEmpty(info.getTableNames())) && tableMap == null; if (replaceProcess) { return invocation.proceed(); } Map tableNameMap = null;// 以 ThreadLocal 中的数据优先 if (tableMap != null) { tableNameMap = tableMap; } else { tableNameMap = info.getTableNames().stream().collect(Collectors.toMap(DynamicTableName::getCurrentTable, DynamicTableName::getItemTable, (v1, v2) -> v1)); } String originalSql = boundSql.getSql(); Statement statement = CCJSqlParserUtil.parse(originalSql);// 替换相应表名 originalSql = alterTableName(mappedStatement, statement, tableNameMap); metaObject.setValue("delegate.boundSql.sql", originalSql); return invocation.proceed(); } @Override public Object plugin(Object target) { if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } return target; } @Override public void setProperties(Properties properties) { } private static Table alterTableName(Table table, Map tableNameMap) { String tableName = tableNameMap.get(table.getName()); if (StringUtils.isNotBlank(tableName)) { table.setName(tableName); } return table; } private static DynamicTableInfo extractInfo(Object paramObj) { // 判断参数里是否有DynamicTableInfo对象 DynamicTableInfo info = null; if (paramObj instanceof DynamicTableInfo) { info = (DynamicTableInfo) paramObj; } else if (paramObj instanceof Map) { for (Object arg : ((Map) paramObj).values()) { if (arg instanceof DynamicTableInfo) { info = (DynamicTableInfo) arg; break; } } } return info; } private static String alterTableName(MappedStatement mappedStatement, Statement statement, Map tableNameMap) { List tableList = new ArrayList(); String originalSql = null; if (mappedStatement.getSqlCommandType() == SqlCommandType.INSERT) { Insert insertStatement = (Insert) statement; Table table = alterTableName(insertStatement.getTable(), tableNameMap); insertStatement.setTable(table); originalSql = insertStatement.toString(); } else if (mappedStatement.getSqlCommandType() == SqlCommandType.DELETE) { Delete deleteStatement = (Delete) statement; Table table = alterTableName(deleteStatement.getTable(), tableNameMap); deleteStatement.setTable(table); originalSql = deleteStatement.toString(); } else if (mappedStatement.getSqlCommandType() == SqlCommandType.UPDATE) { Update updateStatement = (Update) statement; for (Table p : updateStatement.getTables()) { Table table = alterTableName(p, tableNameMap); tableList.add(table); } updateStatement.setTables(tableList); originalSql = updateStatement.toString(); } else if (mappedStatement.getSqlCommandType() == SqlCommandType.SELECT) { Select selectStatement = (Select) statement; PlainSelect plainSelect = (PlainSelect) selectStatement.getSelectBody(); Table fromItem = (Table) plainSelect.getFromItem(); Table table = alterTableName(fromItem, tableNameMap); plainSelect.setFromItem(table); List joinList = new ArrayList(); if (!ObjectUtils.isEmpty(plainSelect.getJoins())) { for (Join p : plainSelect.getJoins()) { if (p.getRightItem() != null) { Table tableInner = alterTableName((Table) p.getRightItem(), tableNameMap); p.setRightItem(tableInner); joinList.add(p); } } plainSelect.setJoins(joinList); } originalSql = plainSelect.toString(); } return originalSql; }}复制代码

因为 mybatis plus 重写了一些类,只要将相关的插件放到 Spring 当中就会自动加载插件。所以这里注册一下 Bean 就行。

@Configurationpublic class MybatisPlusConfig { @Bean public DynamicTableInterceptor dynamicTableInterceptor(){ return new DynamicTableInterceptor(); }}复制代码

其他类

public class TablePluginContainer { /** * 需要更改表名的缓存,请调用后手动清除 * 当前表,需要替换的表名 */ public static ThreadLocal TABLE_NAME_THREAD_LOCAL = new ThreadLocal();}@Datapublic class DynamicTableInfo { private List tableNames;}@Data@AllArgsConstructorpublic class DynamicTableName { /** * 当前表 */ private String currentTable; /** * 需要替换的表 */ private String itemTable;}复制代码使用方法

这里提供了两种方式注入。

第一种是像分页插件一样在 mapper 注入相应的实体类就可以了。

void selectTest(DynamicTableInfo dynamicTableInfo);复制代码

第二种是在逻辑代码的前后添加,考虑到 plus 是动态生成 sql,还是 threadLocal 方便点。

Map map = new HashMap(); map.put("order_case","order_case_1"); TablePluginContainer.TABLE_NAME_THREAD_LOCAL.set(map); OrderCase orderCase = orderMapper.selectById(1); TablePluginContainer.TABLE_NAME_THREAD_LOCAL.remove();复制代码

比丘资源网 » Mybatis plus 动态表名插件开发

发表回复

提供最优质的资源集合

立即查看 了解详情