利用Mybatis拦截器对数据库水平分表

首先你要知道在哪些sql上面要处理分表?你可能需要一个注解:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.common.split;  
  2.   
  3. import java.lang.annotation.ElementType;  
  4. import java.lang.annotation.Retention;  
  5. import java.lang.annotation.RetentionPolicy;  
  6. import java.lang.annotation.Target;  
  7.   
  8. @Retention(RetentionPolicy.RUNTIME)  
  9. @Target({ ElementType.TYPE })  
  10. public @interface TableSplit {  
  11.     //是否分表  
  12.      public boolean split() default true;  
  13.      //表名  
  14.      public String value();  
  15.        
  16.      //获取分表策略  
  17.      public String strategy();  
  18.        
  19. }  

然后你可能需要一个拦截器处理类:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.common.split;  
  2.   
  3. import java.sql.Connection;  
  4. import java.util.Properties;  
  5.   
  6. import org.apache.commons.logging.Log;  
  7. import org.apache.commons.logging.LogFactory;  
  8. import org.apache.ibatis.executor.statement.StatementHandler;  
  9. import org.apache.ibatis.mapping.BoundSql;  
  10. import org.apache.ibatis.mapping.MappedStatement;  
  11. import org.apache.ibatis.plugin.Interceptor;  
  12. import org.apache.ibatis.plugin.Intercepts;  
  13. import org.apache.ibatis.plugin.Invocation;  
  14. import org.apache.ibatis.plugin.Plugin;  
  15. import org.apache.ibatis.plugin.Signature;  
  16. import org.apache.ibatis.reflection.MetaObject;  
  17. import org.apache.ibatis.reflection.factory.DefaultObjectFactory;  
  18. import org.apache.ibatis.reflection.factory.ObjectFactory;  
  19. import org.apache.ibatis.reflection.wrapper.DefaultObjectWrapperFactory;  
  20. import org.apache.ibatis.reflection.wrapper.ObjectWrapperFactory;  
  21.   
  22. import com.dusk.domyself.stock.common.ContextHelper;  
  23.   
  24. @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class }) })  
  25. public class TableSplitInterceptor implements Interceptor {  
  26.     private Log log =LogFactory.getLog(getClass());  
  27.     private static final ObjectFactory DEFAULT_OBJECT_FACTORY = new DefaultObjectFactory();  
  28.     private static final ObjectWrapperFactory DEFAULT_OBJECT_WRAPPER_FACTORY = new DefaultObjectWrapperFactory();  
  29.   
  30.     @Override  
  31.     public Object intercept(Invocation invocation) throws Throwable {  
  32.         StatementHandler statementHandler = (StatementHandler) invocation.getTarget();  
  33.         MetaObject metaStatementHandler = MetaObject.forObject(statementHandler, DEFAULT_OBJECT_FACTORY, DEFAULT_OBJECT_WRAPPER_FACTORY);  
  34.           
  35.         BoundSql boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");  
  36.         // Configuration configuration = (Configuration) metaStatementHandler  
  37.         // .getValue("delegate.configuration");  
  38.         Object parameterObject = metaStatementHandler.getValue("delegate.boundSql.parameterObject");  
  39.         doSplitTable(metaStatementHandler);  
  40.         // 传递给下一个拦截器处理  
  41.         return invocation.proceed();  
  42.   
  43.     }  
  44.   
  45.     @Override  
  46.     public Object plugin(Object target) {  
  47.         // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身,减少目标被代理的次数  
  48.         if (target instanceof StatementHandler) {  
  49.             return Plugin.wrap(target, this);  
  50.         } else {  
  51.             return target;  
  52.         }  
  53.     }  
  54.   
  55.     @Override  
  56.     public void setProperties(Properties properties) {  
  57.   
  58.     }  
  59.   
  60.     private void doSplitTable(MetaObject metaStatementHandler) throws ClassNotFoundException{  
  61.         String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");  
  62.         if (originalSql != null && !originalSql.equals("")) {  
  63.             log.info("分表前的SQL:"+originalSql);  
  64.             MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");  
  65.             String id = mappedStatement.getId();  
  66.             String className = id.substring(0, id.lastIndexOf("."));  
  67.             Class<?> classObj = Class.forName(className);  
  68.             // 根据配置自动生成分表SQL  
  69.             TableSplit tableSplit = classObj.getAnnotation(TableSplit.class);  
  70.             if (tableSplit != null && tableSplit.split()) {  
  71.                 StrategyManager strategyManager = ContextHelper.getStrategyManager();  
  72.                 Strategy strategy=strategyManager.getStrategy(tableSplit.strategy());//获取分表策略来处理分表  
  73.                 String convertedSql=originalSql.replaceAll(tableSplit.value(), strategy.convert(tableSplit.value()));  
  74.                 metaStatementHandler.setValue("delegate.boundSql.sql",convertedSql);  
  75.                 log.info("分表后的SQL:"+convertedSql);  
  76.             }  
  77.         }  
  78.     }  
  79. }  
然后看一下其中的一个应用:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.mapper;  
  2.   
  3. import java.util.List;  
  4.   
  5. import com.dusk.domyself.stock.common.split.TableSplit;  
  6. import com.dusk.domyself.stock.entity.StockDay;  
  7. @TableSplit(value="stock_day", strategy="YYYY")  
  8. public interface StockDayMapper {  
  9.     int deleteByPrimaryKey(Long id);  
  10.   
  11.     int insert(StockDay record);  
  12.   
  13.     int insertSelective(StockDay record);  
  14.   
  15.     List<StockDay> selectBystockId(String stockId);  
  16.   
  17.     int updateByPrimaryKeySelective(StockDay record);  
  18.   
  19.     int updateByPrimaryKey(StockDay record);  
  20. }  
配置的分表策略:

[html]  view plain  copy
  1. <!-- 配置分表策略 -->  
  2. <bean id="strategyManager" class="com.dusk.domyself.stock.common.split.StrategyManager">  
  3.     <property name="strategies">  
  4.         <map>  
  5.             <entry key="YYYY" value="com.dusk.domyself.stock.common.split.YYYYStrategy" />  
  6.         </map>  
  7.     </property>  
  8. </bean>  
 其中分表策略实现是:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.common.split;  
  2.   
  3. import java.text.SimpleDateFormat;  
  4. import java.util.Date;  
  5.   
  6. /** 
  7.  * 按年分表 
  8.  * @author dushangkui 
  9.  * 
  10.  */  
  11. public class YYYYStrategy implements Strategy{  
  12.   
  13.     @Override  
  14.     public String convert(String tableName) {  
  15.         SimpleDateFormat sdf = new SimpleDateFormat("YYYY");  
  16.         StringBuilder sb=new StringBuilder(tableName);  
  17.         sb.append("_");  
  18.         sb.append(sdf.format(new Date()));  
  19.         return sb.toString();  
  20.     }  
  21.       
  22. }  

通用策略接口是:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.common.split;  
  2.   
  3. public interface Strategy {  
  4.     /** 
  5.      * 传入一个需要分表的表名,返回一个处理后的表名  
  6.      * Strategy必须包含一个无参构造器 
  7.      * @param tableName 
  8.      * @return 
  9.      */  
  10.     public String convert(String tableName);  
  11. }  
还有一个可能需要的地方是:

[java]  view plain  copy
  1. package com.dusk.domyself.stock.common;  
  2.   
  3. import org.springframework.beans.BeansException;  
  4. import org.springframework.context.ApplicationContext;  
  5. import org.springframework.context.ApplicationContextAware;  
  6. import org.springframework.stereotype.Component;  
  7.   
  8. import com.dusk.domyself.stock.common.split.StrategyManager;  
  9.   
  10. @Component  
  11. public class ContextHelper implements ApplicationContextAware {  
  12.     private static ApplicationContext context;  
  13.   
  14.     @Override  
  15.     public void setApplicationContext(ApplicationContext context)  
  16.             throws BeansException {  
  17.         ContextHelper.context=context;  
  18.     }  
  19.       
  20.     public static StrategyManager getStrategyManager(){  
  21.         return context.getBean(StrategyManager.class);  
  22.     }  
  23. }  

跑起来看结果:

[html]  view plain  copy
  1. [2016-08-30 22:32:30,170] [http-apr-8080-exec-7] (TableSplitInterceptor.java:63) INFO  com.dusk.domyself.stock.common.split.TableSplitInterceptor - 分表前的SQL:select   
  2.        
  3.     id, stock_id, data_date, top_price, bottom_price, open_price, close_price, main_funds,   
  4.     retail_funds, funds, volume  
  5.      
  6.     from stock_day  
  7.     where stock_id = ?  
  8. [2016-08-30 22:32:30,170] [http-apr-8080-exec-7] (AbstractBeanFactory.java:249) DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'strategyManager'  
  9. [2016-08-30 22:32:30,171] [http-apr-8080-exec-7] (TableSplitInterceptor.java:75) INFO  com.dusk.domyself.stock.common.split.TableSplitInterceptor - 分表后的SQL:select   
  10.        
  11.     id, stock_id, data_date, top_price, bottom_price, open_price, close_price, main_funds,   
  12.     retail_funds, funds, volume  
  13.      
  14.     from stock_day_2016  
  15.     where stock_id = ?  

需要其他的分表策略可以自己实现然后注册就好了


里面设计的类已经github共享:https://github.com/dushangkui/common-util



https://blog.csdn.net/jiaotuwoaini/article/details/52373883

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值