mybatis 拦截器实现按年分表

mybatis 拦截器实现分表 操作具体如下:

1、准备一张数据库表 demo_2019 

2、搭建一套基本的ssm框架

3、采用策略设计模式来实现分表操作 

下面是策略设计模式核心类图

   

4、具体观看下面的核心代码:

      自定义分表注解:TableSplit 类

package com.lmdsoft.util.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @Author: lmdsoft
 * @Date: 2019/6/4 18:07
 * @Version V1.0
 * @Description: 自定义分表注解
 **/

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
public @interface TableSplit {
	//是否分表
	 public boolean split() default true;
	 
	 public String value() default "";

	 public String field() default "";
	 
	 //获取分表策略
	 public String strategy();
	 
}

 定义策略接口:Strategy 

package com.lmdsoft.util.split;

import java.util.Map;


/**
 * @Author: lmdsoft
 * @Date: 2019/6/4 19:03
 * @Version V1.0
 * @Description: 定义策略接口
 **/

public interface Strategy {

	public static final String TABLE_NAME="table_name";
	public static final String SPLIT_FIELD="split_field";
	public static final String EXECUTE_PARAM_DECLARE="execute_param_declare";
	public static final String EXECUTE_PARAM_VALUES="execute_param_values";
	/**
	 * 传入一个需要分表的表名,返回一个处理后的表名 
	 * Strategy必须包含一个无参构造器
	 * @param params
	 * @return
	 */
	public String convert(Map<String, Object> params) throws Exception;
}
按年分表策略实现
package com.lmdsoft.util.split.impl;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;

import com.lmdsoft.util.split.Strategy;

/**
 * @Author: lmdsoft
 * @Date: 2019/6/4 18:03
 * @Version V1.0
 * @Description: 按年分表策略
 **/

public class YYYYStrategy implements Strategy {

	@Override
	public String convert(Map<String, Object> params) {
		SimpleDateFormat sdf = new SimpleDateFormat("YYYY");
		StringBuilder sb=new StringBuilder(params.get(Strategy.TABLE_NAME).toString());
		sb.append("_");
		sb.append(sdf.format(new Date()));
		return sb.toString();
	}
	
}
策略管理者类
package com.lmdsoft.util.split;

import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @Author: lmdsoft
 * @Date: 2019/6/4 19:03
 * @Version V1.0
 * @Description: 策略管理者
 **/

public class StrategyManager{
	private  Log log= LogFactory.getLog(StrategyManager.class);
	private  Map<String,Strategy> strategies = new ConcurrentHashMap<String,Strategy>(10);
	
	public  Strategy getStrategy(String key){
		return strategies.get(key);
	}

	public   Map<String, Strategy> getStrategies() {
		return strategies;
	}

	public  void setStrategies(Map<String, String> strategies) {
		for(Entry<String, String> entry : strategies.entrySet()){
			try {
				this.strategies.put(entry.getKey(),(Strategy)Class.forName(entry.getValue()).newInstance());
			} catch (Exception e) {
				log.error("实例化策略出错", e);
			}
		}
		printDebugInfo();
	}
	private void printDebugInfo(){
		StringBuffer msg= new StringBuffer("初始化了"+strategies.size()+"策略");
		for(String key: strategies.keySet()){
			msg.append("\n");
			msg.append(key);
			msg.append("  --->  ");
			msg.append(strategies.get(key));
		}
		log.debug(msg.toString());
	}
}
分表拦截器
package com.lmdsoft.util.split;

import com.lmdsoft.util.ContextHelper;
import com.lmdsoft.util.JsonUtil;
import com.lmdsoft.util.annotation.TableSplit;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMap;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory;
import org.apache.ibatis.reflection.factory.ObjectFactory;
import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;
import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
 * @Author: lmdsoft
 * @Date: 2019/6/4 19:03
 * @Version V1.0
 * @Description: 分表拦截器
 **/

@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) })
public class TableSplitInterceptor implements Interceptor {
	private Log log = LogFactory.getLog(getClass());
	private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();
	private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
		MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
		BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
		Object parameterObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject");
		doSplitTable(metaStatementHandler,parameterObject);
		// 传递给下一个拦截器处理
		return invocation.proceed();

	}

	@Override
	public Object plugin(Object target) {
		// 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数
		if (target instanceof StatementHandler) {
			return Plugin.wrap(target, this);
		} else {
			return target;
		}
	}

	@Override
	public void setProperties(Properties properties) {

	}

	private void doSplitTable(MetaObject metaStatementHandler,Object param ) throws Exception {
		String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");
		if (originalSql != null && !originalSql.equals("")) {
			log.info("分表前的SQL:\n" + originalSql);
			MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
			String id = mappedStatement.getId();
			String className = id.substring(0, id.lastIndexOf("."));
			String methodName = id.substring(id.lastIndexOf(".") + 1);
			Class<?> clazz = Class.forName(className);
			ParameterMap paramMap = mappedStatement.getParameterMap();
			Method method = findMethod(clazz.getDeclaredMethods(), methodName);
			// 根据配置自动生成分表SQL
			TableSplit tableSplit = null;
			if (method != null) {
				tableSplit = method.getAnnotation(TableSplit.class);
			}

			if (tableSplit == null) {
				tableSplit = clazz.getAnnotation(TableSplit.class);
			}
			System.out.printf(JsonUtil.toString(paramMap));
			if (tableSplit != null && tableSplit.split() && StringUtils.isNotBlank(tableSplit.strategy())) {
				StrategyManager strategyManager = ContextHelper.getBean(StrategyManager.class);
				String convertedSql = "";
				String[] strategies = tableSplit.strategy().split(",");
				for (String str : strategies) {
					Strategy strategy = strategyManager.getStrategy(str);
					Map<String,Object> params =new HashMap<String,Object>();
					params.put(Strategy.TABLE_NAME, tableSplit.value());
					params.put(Strategy.SPLIT_FIELD, tableSplit.field());
					params.put(Strategy.EXECUTE_PARAM_DECLARE, paramMap);
					params.put(Strategy.EXECUTE_PARAM_VALUES, param);

					convertedSql = originalSql.replaceAll(tableSplit.value(), strategy.convert(params));
				}
				metaStatementHandler.setValue("delegate.boundSql.sql", convertedSql);

				log.info("分表后的SQL:\n" + convertedSql);
			}
		}
	}

	private Method findMethod(Method[] methods, String methodName) {
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				return method;
			}
		}
		return null;
	}

}

本文只是简单的介绍了一下采用策略设计模式来用拦截器实现分表的操作,完整的代码可以来下载:

https://github.com/lmdsoft/TableSplit.git

https://download.csdn.net/download/lmdsoft/11275331

发布了33 篇原创文章 · 获赞 26 · 访问量 102万+

猜你喜欢

转载自blog.csdn.net/lmdsoft/article/details/94624430