mybatis攔截器進行水平分表

(mybatis 版本3.2.*)java

 

import java.sql.Connection;  
import java.util.Properties;  
  
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.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.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;spring

import rml.model.MUser;
import rml.util.ContextHelper;  
  
  
@Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.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);  
          
        doSplitTable(metaStatementHandler);  
        // 傳遞給下一個攔截器處理  
        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) throws ClassNotFoundException{  
        String originalSql = (String) metaStatementHandler.getValue("delegate.boundSql.sql");  
        if (originalSql != null && !originalSql.equals("")) {  
            log.info("分表前的SQL:"+originalSql);  
            MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");  
            String id = mappedStatement.getId();  
            String className = id.substring(0, id.lastIndexOf("."));  
            Class<?> classObj = Class.forName(className);  
            MUser muser = (MUser)metaStatementHandler.getValue("delegate.boundSql.parameterObject");
            // 根據配置自動生成分表SQL  
            TableSplit tableSplit = classObj.getAnnotation(TableSplit.class);  
            if (tableSplit != null && tableSplit.split()) {  
                StrategyManager strategyManager = ContextHelper.getStrategyManager();  
                Strategy strategy=strategyManager.getStrategy(tableSplit.strategy());//獲取分表策略來處理分表  
                String convertedSql=originalSql.toLowerCase().replaceAll(tableSplit.value(), strategy.convert(tableSplit.value(), muser.getName()));  
                metaStatementHandler.setValue("delegate.boundSql.sql",convertedSql);  
                log.info("分表後的SQL:"+convertedSql);  
            }  
        }  
    }  
    
}sql

 

public interface Strategy {
     /** 
     * 傳入一個須要分表的表名,返回一個處理後的表名  
     * Strategy必須包含一個無參構造器 
     * @param tableName 
     * @return 
     */  
    public String convert(String tableName, String name);  
}apache

 

import java.util.List;
import rml.model.MUser;
import rml.split.TableSplit;
@TableSplit(value="muser", strategy="%5")
public interface MUserMapper {
    int deleteByPrimaryKey(String id);spring-mvc

    int insert(MUser record);mybatis

    int insertSelective(MUser record);mvc

    MUser selectByPrimaryKey(String id);app

    int updateByPrimaryKeySelective(MUser record);ide

    int updateByPrimaryKey(MUser record);
    
    List<MUser> getAll(MUser record);
}測試

 

 

 

 

 

import java.lang.annotation.ElementType;  
import java.lang.annotation.Retention;  
import java.lang.annotation.RetentionPolicy;  
import java.lang.annotation.Target;  
  
@Retention(RetentionPolicy.RUNTIME)  
@Target({ ElementType.TYPE })  
public @interface TableSplit {  
    //是否分表  
     public boolean split() default true;  
     //表名  
     public String value();  
       
     //獲取分表策略  
     public String strategy(); 
}

 

 

 

public class NameStrategy implements Strategy{

    @Override
    public String convert(String tableName,String name) {
        StringBuilder sb=new StringBuilder(tableName);  
        sb.append("_");
        sb.append(Integer.parseInt(name)%5);
        return sb.toString();
    }

}

 

 

 

 

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;

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());
    }
}
 

 

 

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

import rml.split.StrategyManager;

@Component
public class ContextHelper implements ApplicationContextAware {
    private static ApplicationContext context;

    @Override
    public void setApplicationContext(ApplicationContext context)
            throws BeansException {
        ContextHelper.context = context;
    }

    public static ApplicationContext getApplicationContext() {
        return ContextHelper.context;
    }

    public static <T> T getBean(Class<T> clazz) {
        return context.getBean(clazz);
    }

    public static Object getBean(String name) {
        return context.getBean(name);
    }

    public static <T> T getBean(String name, Class<T> clazz) {
        return context.getBean(name, clazz);
    }

    public static StrategyManager getStrategyManager() {
        return context.getBean(StrategyManager.class);
    }
}
 

 

 

<!-- 配置分表策略 -->  
    <bean id="strategyManager" class="rml.split.StrategyManager">  
        <property name="strategies">  
            <map>  
                <entry key="%5" value="rml.split.NameStrategy" />  
            </map>  
        </property>  
    </bean>

 

 

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  
   <plugins>
    <plugin interceptor="rml.split.TableSplitInterceptor" />
    </plugins>
     
</configuration>

 

 

/** 
 * 測試分庫分表規則 
 */  
@RunWith(SpringJUnit4ClassRunner.class)    
@ContextConfiguration(locations = { "classpath*:spring.xml, classpath*:spring-mvc.xml, classpath*:spring-mybatis.xml, classpath*:mybatis-config.xml" })    
public class ShardingJdbcMybatisTest {
    @Resource
    public MUserMapper mUserMapper;
    
    @Test    
    public void testuserinsert() {    
        MUser mUser = new MUser();
        mUser.setName("5");
        mUser.setAge(10);
        mUser.setAddress("阿什頓發順豐");
        Assert.assertEquals(mUserMapper.insert(mUser)>0, true);    
    }    
}

 

 

打印結果

[rml.split.TableSplitInterceptor]分表前的SQL:insert into MUSER (NAME, AGE, ADDRESS)     values (?, ?, ?) [org.springframework.beans.factory.support.DefaultListableBeanFactory]Returning cached instance of singleton bean 'strategyManager' [rml.split.TableSplitInterceptor]分表後的SQL:insert into muser_0 (name, age, address)     values (?, ?, ?)

相關文章
相關標籤/搜索