PostgreSql分库分表

时间:2025-02-18 08:51:59
import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.ibatis.cache.CacheKey; import org.apache.ibatis.executor.Executor; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; import org.apache.ibatis.mapping.SqlCommandType; import org.apache.ibatis.mapping.SqlSource; import org.apache.ibatis.plugin.*; import org.apache.ibatis.session.ResultHandler; import org.apache.ibatis.session.RowBounds; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.lang.reflect.Field; import java.util.Properties; /** * MybatisSubmeterInterceptor * @Description: 分表拦截器 * @Author: * @Date 2022/7/29 9:43 * @Version 1.0.0 */ @ConditionalOnClass(value = org.apache.ibatis.plugin.Interceptor.class) @Component("mybatisSubmeterInterceptor") @Intercepts({ @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}) }) @Order(0) @Slf4j public class MybatisSubmeterInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { Object[] args = invocation.getArgs(); Executor executor = (Executor) invocation.getTarget(); // 获取原始的sql语句 MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0]; Object parameter = invocation.getArgs()[1]; BoundSql boundSql = mappedStatement.getBoundSql(parameter); String oldsql = boundSql.getSql(); // 判断是否需要分表 if (isReplaceTableName(oldsql)) { log.error("\n分表前的sql:{}", oldsql); // 查询单独处理 if (mappedStatement.getSqlCommandType().compareTo(SqlCommandType.SELECT) == 0) { RowBounds rowBounds = (RowBounds) args[2]; ResultHandler resultHandler = (ResultHandler) args[3]; CacheKey cacheKey; BoundSql boundSqlnew; if (args.length == 4) { boundSqlnew = mappedStatement.getBoundSql(parameter); cacheKey = executor.createCacheKey(mappedStatement, parameter, rowBounds, boundSqlnew); } else { boundSqlnew = (BoundSql) args[5]; cacheKey = (CacheKey) args[4]; } String sql = boundSqlnew.getSql(); String newsql = sql.replace("db_father", "db_father_" + "2400"); String newsql2 = sql.replace("DB_FATHER", "DB_FATHER_" + "2400"); newsql = StringUtils.equals(sql,newsql) ? newsql2 : newsql; //通过反射修改sql语句 Field field = boundSqlnew.getClass().getDeclaredField("sql"); field.setAccessible(true); field.set(boundSqlnew, newsql); log.error("\n分表后的sql:{}", boundSqlnew.getSql()); // 直接query可能会应该其他拦截器 return executor.query(mappedStatement, parameter, rowBounds, resultHandler, cacheKey, boundSqlnew); } else { // 分表策略,这个地方可以使用注解,配置类,配置数据库等方式 String newsql = oldsql.replace("db_father", "db_father_" + "2400"); String newsql2 = oldsql.replace("DB_FATHER", "DB_FATHER_" + "2400"); newsql = StringUtils.equals(oldsql,newsql) ? newsql2 : newsql; //重新生成一个BoundSql对象 BoundSql bs = new BoundSql(mappedStatement.getConfiguration(),newsql,boundSql.getParameterMappings(),parameter); //重新生成一个MappedStatement对象 MappedStatement newMappedStatement = copyMappedStatement(mappedStatement, new BoundSqlSqlSource(bs)); //赋回给实际执行方法所需的参数中 args[0] = newMappedStatement; } log.error("\n分表后的sql:{}", ((MappedStatement)args[0]).getBoundSql(parameter).getSql()); } return invocation.proceed(); } @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } @Override public void setProperties(Properties properties) { log.info("setProperties " + properties.toString()); } /** * * * @Description: 判断是否需要分表 * @Author: * @Date 2022/7/29 11:53 * @param sql sql * @return boolean * @Version 1.0.0 **/ private boolean isReplaceTableName(String sql) { // 测试数据,将ddb_father 重新定位到db_father_2400, // 这个地方可以使用注解,配置类,配置数据库等方式判断 if (sql.contains("db_father") || sql.contains("DB_FATHER")) { return true; } return false; } /** * * * @Description: 生成一个新的mappedStatement * @Date 2022/7/29 13:49 * @param ms * @param newSqlSource * @return * @Version 1.0.0 **/ private MappedStatement copyMappedStatement (MappedStatement ms, SqlSource newSqlSource) { MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource, ms.getSqlCommandType()); builder.resource(ms.getResource()); builder.fetchSize(ms.getFetchSize()); builder.statementType(ms.getStatementType()); builder.keyGenerator(ms.getKeyGenerator()); if (ms.getKeyProperties() != null && ms.getKeyProperties().length > 0) { builder.keyProperty(String.join(",",ms.getKeyProperties())); } builder.timeout(ms.getTimeout()); builder.parameterMap(ms.getParameterMap()); builder.resultMaps(ms.getResultMaps()); builder.resultSetType(ms.getResultSetType()); builder.cache(ms.getCache()); builder.flushCacheRequired(ms.isFlushCacheRequired()); builder.useCache(ms.isUseCache()); return builder.build(); } /*** * MappedStatement构造器接受的是SqlSource * 实现SqlSource接口,将BoundSql封装进去 */ public static class BoundSqlSqlSource implements SqlSource { private BoundSql boundSql; public BoundSqlSqlSource(BoundSql boundSql) { this.boundSql = boundSql; } @Override public BoundSql getBoundSql(Object parameterObject) { return boundSql; } } }