mybatis sql攔截修改

自定義註解:



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

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface TableSeg {
    //表名
    public String tableName() default "tb_shop_order";
    // 分表方式
    public String shardType() default "";
    //根據什麼字段分表 ,多個字段用逗號,隔開
    public String shardBy();

}

自定義攔截器:

package com.fhgl.shop.interceptor;

import java.lang.reflect.Method;
import java.sql.Connection;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fhgl.shop.annotation.TableSeg;
import com.fhgl.shop.utils.DateUtils;
import com.fhgl.shop.utils.OrderTableShardingUtils;

/**
 * 分表Interceptor
 *
 */
@Intercepts({
    @Signature(type = StatementHandler .class, method = "prepare", args = {Connection.class, Integer.class}),
})
public class MybatisSqlInterceptor implements Interceptor{

	private static final Logger LOGGER = LoggerFactory.getLogger(MybatisSqlInterceptor.class);
	
	@SuppressWarnings("unchecked")
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		 try {
			StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
			    //通過MetaObject優雅訪問對象的屬性,這裏是訪問statementHandler的屬性;:MetaObject是Mybatis提供的一個用於方便、
			    //優雅訪問對象屬性的對象,通過它可以簡化代碼、不需要try/catch各種reflect異常,同時它支持對JavaBean、Collection、Map三種類型對象的操作。
			    MetaObject metaObject = MetaObject
			        .forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY,
			            new DefaultReflectorFactory());
			    //先攔截到RoutingStatementHandler,裏面有個StatementHandler類型的delegate變量,其實現類是BaseStatementHandler,然後就到BaseStatementHandler的成員變量mappedStatement
			    MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");
			    //id爲執行的mapper方法的全路徑名,如com.uv.dao.UserMapper.insertUser
			    String sqlId = mappedStatement.getId();
			    //sql語句類型 select、delete、insert、update
			    String sqlCommandType = mappedStatement.getSqlCommandType().toString();
			    
		        String className = sqlId.substring(0, sqlId.lastIndexOf("."));
		        
		        String methodName = sqlId.replace(className+".", "");
		        Class<?> classObj = Class.forName(className);
		        Method[] methods = classObj.getDeclaredMethods();
		        Method method = null;
		        for(Method method1 : methods) {
		        	if(method1.getName().equals(methodName)) {
		        		method = method1;
		        		break;
		        	}
		        }
			    TableSeg tableSeg = method.getAnnotation(TableSeg.class);
		        if(null == tableSeg){
		            //不需要分表,直接傳遞給下一個攔截器處理
		            return invocation.proceed();
		        }
		        LOGGER.info("MybatisSqlInterceptor--sqlId:{}",sqlId);
		        //獲取分表字段
		        String[] shardField = tableSeg.shardBy().split(",");
		        List<String> timeList = new ArrayList<>();
		        String orderId = "";
		        List<String> orderIdList = new ArrayList<>();
		        Set<String> tableNameSet = new HashSet<String>();
		        Map<String, Object> parameters = (Map<String, Object>) metaObject.getValue("delegate.boundSql.parameterObject");
		        LOGGER.info("MybatisSqlInterceptor--parameters:{}",parameters);
		        //獲取參數
		        for (int i = 0;i < shardField.length;i++) {
		        	Object paramValue = parameters.get(shardField[i]);
		        	if(paramValue != null) {
		        		System.out.println(paramValue.getClass());
		        		if(paramValue.getClass() == String.class) {
		        			//參數是訂單號或者時間
		        			String paramStr = (String) paramValue;
		        			String paramIndex = paramStr.substring(0, 2);
		        			if(paramIndex.equals("DD")) {
		        				//訂單號
		        				orderId = paramStr;
		        				String tableName = OrderTableShardingUtils.getTableNameByOrderId(orderId);
		        				if(tableName != null) {
		        					tableNameSet.add(tableName);
		        				}
		        				
		        			} else {
		        				//時間
		        				boolean isDate = DateUtils.checkDate(paramStr, "yyyy-MM-dd HH:mm:ss");
		        				
		        				if(isDate) {
		        					//時間
		        					timeList.add(paramStr);
		        				}
		        			}
		        		} else if(List.class.isAssignableFrom(paramValue.getClass())) {
		        			//參數是orderList
		        			orderIdList = (List<String>) paramValue;
		        			if(orderIdList != null && orderIdList.size()>0) {
		        				for(String ordersId : orderIdList) {
		        					String tableName = OrderTableShardingUtils.getTableNameByOrderId(ordersId);
			        				if(tableName != null) {
			        					tableNameSet.add(tableName);
			        				}
		        				}
		        			}
		        		}
		        	}
		        }
		        //判斷時間
		        Date beginTime = null;
		        Date endTime = null;
		        if(timeList.size() == 2) {
		        	beginTime = DateUtils.strToDateLong(timeList.get(0));
		        	endTime = DateUtils.strToDateLong(timeList.get(1));
		        	List<String> tableName = new ArrayList<>();
		        	if (beginTime.getTime() > endTime.getTime()) {
		        		tableName = OrderTableShardingUtils.getTableNameListByTimes(timeList.get(1), timeList.get(0));
		        	} else {
		        		tableName = OrderTableShardingUtils.getTableNameListByTimes(timeList.get(0), timeList.get(1));
		        	}
		        	if (tableName != null && tableName.size()>0) {
		        		for(String tn: tableName) {
        					if(tn != null) {
        						tableNameSet.add(tn);
        					}
        				}
		        	}
		        	
		        }
		        
			    //攔截查詢sql
			    if(sqlCommandType.equals("SELECT")) {
			    	BoundSql boundSql = statementHandler.getBoundSql();
				    String sql = boundSql.getSql();
				    if(sql.toLowerCase().indexOf("tb_shop_order") < 0 || sql.toLowerCase().indexOf("tb_shop_order_") >= 0) {
				    	return invocation.proceed();
				    }
				    if(tableNameSet.size() == 0) {
				    	return invocation.proceed();
				    } 
				    Iterator<String> it = tableNameSet.iterator();
				    List<String> tableNameList = new ArrayList<>();
				    while(it.hasNext()) {
				    	tableNameList.add(it.next());
				    }
				    String pattern = "(?i)tb_shop_order";
				    if(tableNameList.size() == 1) {
					    sql = sql.replaceAll(pattern, tableNameList.get(0));
				    } else {
				    	for (int i=0;i<tableNameList.size();i++) {
				    		if(i == 0) {
				    			sql = sql.replaceAll(pattern, tableNameList.get(0));
				    		}
				    	}
				    }
				    LOGGER.info("MybatisSqlInterceptor--sql:{}",sql);
				    metaObject.setValue("delegate.boundSql.sql", sql);
				    
			    } else {
			    	return invocation.proceed();
			    }
		} catch (Exception e) {
			LOGGER.error("MybatisSqlInterceptor--Exception:{}",e);
		}

        return invocation.proceed();
	}
    
	
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);

	}

	@Override
	public void setProperties(Properties properties) {
		
	}	

//  定義一個內部輔助類,作用是包裝sql
  class BoundSqlSqlSource implements SqlSource {
      private BoundSql boundSql;
      public BoundSqlSqlSource(BoundSql boundSql) {
          this.boundSql = boundSql;
      }
      @Override
      public BoundSql getBoundSql(Object parameterObject) {
          return boundSql;
      }
  }
  
}

處理邏輯工具類:

package com.fhgl.shop.utils;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TimeZone;

/**
 * 訂單表分表工具類
 * @author admin
 *
 */

public class OrderTableShardingUtils {

	/**
	 * 通過訂單號獲取表名
	 * @param orderId
	 * @return
	 */
	public static String getTableNameByOrderId(String orderId) {
		if(orderId.length() == 14) {
			return "tb_shop_order_2018";
		} else if(orderId.length() == 19) {
			String timeStr = orderId.substring(2, 15);
			Date date = new Date(Long.parseLong(timeStr));
			int year = ShardingUtil.getYear(date);
			int season = ShardingUtil.getSeason(date);
			String tableName = "tb_shop_order_"+year+"_"+season;
			return tableName;
		} else {
			return null;
		}
	}
	
	/**
	 * 通過時間段獲取表名列表
	 * @param beginTime
	 * @param endTime
	 * @return
	 */
	public static List<String> getTableNameListByTimes(String beginTime,String endTime){
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		List<String> tableNameList = new ArrayList<>();
		try {
			Date beginDate = sdf.parse(beginTime);
			Date endDate = sdf.parse(endTime);
			
			Long  time = System.currentTimeMillis();  //當前時間的時間戳
		    long zero = time/(1000*3600*24)*(1000*3600*24) - TimeZone.getDefault().getRawOffset();//當天的0點
			if(beginDate.getTime() > endDate.getTime() || endDate.getTime()>time) {
				return null;
			}
		    if(beginDate.getTime() > zero-1000*3600*24*100L) {
		    	tableNameList.add("tb_shop_order");
		    	return tableNameList;
		    }
		    if(endDate.getTime() <= sdf.parse("2019-01-01 00:00:00").getTime()) {
				tableNameList.add("tb_shop_order_2018");
		    	return tableNameList;
			}
			if(beginDate.getTime() < sdf.parse("2019-01-01 00:00:00").getTime()) {
				tableNameList.add("tb_shop_order_2018");
				beginDate = sdf.parse("2019-01-01 00:00:00");
			}
			int beginYear = ShardingUtil.getYear(beginDate);
			int beginSeason = ShardingUtil.getSeason(beginDate);
			
			int endYear = ShardingUtil.getYear(endDate);
			int endSeason = ShardingUtil.getSeason(endDate);
			if(beginYear == endYear) {
				for(int season = beginSeason; season <= endSeason; season++) {
					String tableName = "";
					tableName = "tb_shop_order_"+beginYear+"_"+season;
					tableNameList.add(tableName);
				}
			} else {
				for (int year = beginYear; year <= endYear ;year++) {
					String tableName = "";
					if(year == beginYear) {
						for(int season = beginSeason; season <= 4; season++) {
							tableName = "tb_shop_order_"+year+"_"+season;
							tableNameList.add(tableName);
						}
					} else if(year == endYear) {
						for(int season = 1; season <= endSeason; season++) {
							tableName = "tb_shop_order_"+year+"_"+season;
							tableNameList.add(tableName);
						}
					} else {
						for(int season = 1; season <= 4; season++) {
							tableName = "tb_shop_order_"+year+"_"+season;
							tableNameList.add(tableName);
						}
					}
				}
			}
			
			if(endDate.getTime() >= zero-1000*3600*24*10) {
				tableNameList.add("tb_shop_order");
			}
			
		} catch (Exception e) {
			return null;
		}
		return tableNameList;
	}
	
	public static List<String> getBeginTimeAndEndTimeByTableName(String TableName,String beginTime,String endTime) {
		return null;
	}
}

啓動類添加初始化攔截器:

@Bean
  ConfigurationCustomizer mybatisConfigurationCustomizer() {
      return new ConfigurationCustomizer() {
          @Override
          public void customize(org.apache.ibatis.session.Configuration configuration) {
              configuration.addInterceptor(new MybatisSqlInterceptor());
          }
      };
  }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章