resultMaps = ms.getResultMaps();
+ builder.resultMaps(resultMaps);
+ builder.resultSetType(ms.getResultSetType());
+ // setStatementCache()
+ builder.cache(ms.getCache());
+ builder.flushCacheRequired(ms.isFlushCacheRequired());
+ builder.useCache(ms.isUseCache());
+ return builder.build();
+ }
+
+ /**
+ * 根据msId获取接口类
+ *
+ * @param msId
+ * @return
+ * @throws ClassNotFoundException
+ */
+ public static Class> getMapperClass(String msId) {
+ String mapperClassStr = msId.substring(0, msId.lastIndexOf("."));
+ try {
+ return Class.forName(mapperClassStr);
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("无法获取Mapper接口信息:" + msId);
+ }
+ }
+
+ /**
+ * 获取执行的方法名
+ *
+ * @param ms
+ * @return
+ */
+ public static String getMethodName(MappedStatement ms) {
+ return getMethodName(ms.getId());
+ }
+
+ /**
+ * 获取执行的方法名
+ *
+ * @param msId
+ * @return
+ */
+ public static String getMethodName(String msId) {
+ return msId.substring(msId.lastIndexOf(".") + 1);
+ }
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperHelperForSharding.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperHelperForSharding.java
new file mode 100644
index 0000000..3c94e81
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperHelperForSharding.java
@@ -0,0 +1,284 @@
+package com.mogujie.trade.tsharding.route.orm;
+
+import com.mogujie.trade.tsharding.annotation.ShardingExtensionMethod;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSession;
+
+import java.lang.reflect.Method;
+import java.util.*;
+
+/**
+ * Mapper处理主要逻辑,最关键的一个类
+ *
+ *
+ * 参考项目地址 : https://github.com/abel533/Mapper
+ *
+ *
+ * @author qigong on 5/1/15
+ */
+public class MapperHelperForSharding {
+
+ /**
+ * 注册的通用Mapper接口
+ */
+ private Map, MapperEnhancer> registerMapper = new HashMap, MapperEnhancer>();
+
+ /**
+ * 缓存msid和MapperTemplate
+ */
+ private Map msIdCache = new HashMap();
+ /**
+ * 缓存skip结果
+ */
+ private final Map msIdSkip = new HashMap();
+
+ /**
+ * 缓存已经处理过的Collection
+ */
+ private Set> collectionSet = new HashSet>();
+
+ /**
+ * 是否使用的Spring
+ */
+ private boolean spring = false;
+
+ /**
+ * 是否为Spring4.x以上版本
+ */
+ private boolean spring4 = false;
+
+ /**
+ * Spring版本号
+ */
+ private String springVersion;
+
+ /**
+ * 缓存初始化时的SqlSession
+ */
+ private List sqlSessions = new ArrayList();
+
+ /**
+ * 针对Spring注入需要处理的SqlSession
+ *
+ * @param sqlSessions
+ */
+ public void setSqlSessions(SqlSession[] sqlSessions) {
+ if (sqlSessions != null && sqlSessions.length > 0) {
+ this.sqlSessions.addAll(Arrays.asList(sqlSessions));
+ }
+ }
+
+ /**
+ * Spring初始化方法,使用Spring时需要配置init-method="initMapper"
+ */
+ public void initMapper() {
+ // 只有Spring会执行这个方法,所以Spring配置的时候,从这儿可以尝试获取Spring的版本
+ // 先判断Spring版本,对下面的操作有影响
+ // Spring4以上支持泛型注入,因此可以扫描通用Mapper
+ if (!initSpringVersion()) {
+ throw new RuntimeException("Error! Spring4 is necessary!");
+ }
+
+ for (SqlSession sqlSession : sqlSessions) {
+ processConfiguration(sqlSession.getConfiguration());
+ }
+ }
+
+ /**
+ * 检测Spring版本号,Spring4.x以上支持泛型注入
+ */
+ private boolean initSpringVersion() {
+ try {
+ // 反射获取SpringVersion
+ Class> springVersionClass = Class.forName("org.springframework.core.SpringVersion");
+ springVersion = (String) springVersionClass.getDeclaredMethod("getVersion", new Class>[0]).invoke(null,
+ new Object[0]);
+ spring = true;
+ if (springVersion.indexOf(".") > 0) {
+ int MajorVersion = Integer.parseInt(springVersion.substring(0, springVersion.indexOf(".")));
+ if (MajorVersion > 3) {
+ spring4 = true;
+ } else {
+ spring4 = false;
+ }
+ }
+ } catch (Exception e) {
+ spring = false;
+ spring4 = false;
+ }
+ return spring && spring4;
+ }
+
+ /**
+ * 通过通用Mapper接口获取对应的MapperTemplate
+ *
+ * @param mapperClass
+ */
+ private MapperEnhancer fromMapperClass(Class> mapperClass) {
+ Method[] methods = mapperClass.getDeclaredMethods();
+ Class> templateClass = null;
+ Class> tempClass = null;
+ Set methodSet = new HashSet();
+ for (Method method : methods) {
+ if (method.isAnnotationPresent(ShardingExtensionMethod.class)) {
+ ShardingExtensionMethod annotation = method.getAnnotation(ShardingExtensionMethod.class);
+ tempClass = annotation.type();
+ methodSet.add(method.getName());
+ }
+ if (templateClass == null) {
+ templateClass = tempClass;
+ } else if (templateClass != tempClass) {
+ throw new RuntimeException("一个通用Mapper中只允许存在一个MapperTemplate子类!");
+ }
+ }
+ if (templateClass == null || !MapperEnhancer.class.isAssignableFrom(templateClass)) {
+ throw new RuntimeException("接口中不存在包含type为MapperTemplate的Provider注解,这不是一个合法的通用Mapper接口类!");
+ }
+ MapperEnhancer mapperEnhancer = null;
+ try {
+ mapperEnhancer = (MapperEnhancer) templateClass.getConstructor(Class.class).newInstance(mapperClass);
+ } catch (Exception e) {
+ throw new RuntimeException("实例化MapperTemplate对象失败:" + e.getMessage(), e);
+ }
+ // 注册方法
+ for (String methodName : methodSet) {
+ try {
+ mapperEnhancer.addMethodMap(methodName, templateClass.getMethod("enhancedShardingSQL", MappedStatement.class, Configuration.class, Long.class));
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException(templateClass.getCanonicalName() + "中缺少enhancedShardingSQL方法!");
+ }
+ }
+ return mapperEnhancer;
+ }
+
+ /**
+ * 注册通用Mapper接口
+ *
+ * @param mapperClass
+ * @throws Exception
+ */
+ public void registerMapper(Class> mapperClass) {
+ if (registerMapper.get(mapperClass) == null) {
+ MapperEnhancer enhancer = fromMapperClass(mapperClass);
+ registerMapper.put(mapperClass, enhancer);
+ } else {
+ throw new RuntimeException("已经注册过的通用Mapper[" + mapperClass.getCanonicalName() + "]不能多次注册!");
+ }
+ }
+
+ /**
+ * 注册通用Mapper接口
+ *
+ * @param mapperClass
+ * @throws Exception
+ */
+ public void registerMapper(String mapperClass) {
+ try {
+ registerMapper(Class.forName(mapperClass));
+ } catch (ClassNotFoundException e) {
+ throw new RuntimeException("注册通用Mapper[" + mapperClass + "]失败,找不到该通用Mapper!");
+ }
+ }
+
+ /**
+ * 方便Spring注入
+ *
+ * @param mappers
+ */
+ public void setMappers(String[] mappers) {
+ if (mappers != null && mappers.length > 0) {
+ for (String mapper : mappers) {
+ registerMapper(mapper);
+ }
+ }
+ }
+
+ /**
+ * 判断当前的接口方法是否需要进行拦截
+ *
+ * @param msId
+ * @return
+ */
+ public boolean isMapperMethod(String msId) {
+ if (msIdSkip.get(msId) != null) {
+ return msIdSkip.get(msId);
+ }
+ for (Map.Entry, MapperEnhancer> entry : registerMapper.entrySet()) {
+ if (entry.getValue().supportMethod(msId)) {
+ msIdSkip.put(msId, true);
+ return true;
+ }
+ }
+ msIdSkip.put(msId, false);
+ return false;
+ }
+
+ /**
+ * 获取MapperTemplate
+ *
+ * @param msId
+ * @return
+ */
+ private MapperEnhancer getMapperTemplate(String msId) {
+ MapperEnhancer mapperEnhancer = null;
+ if (msIdCache.get(msId) != null) {
+ mapperEnhancer = msIdCache.get(msId);
+ } else {
+ for (Map.Entry, MapperEnhancer> entry : registerMapper.entrySet()) {
+ if (entry.getValue().supportMethod(msId)) {
+ mapperEnhancer = entry.getValue();
+ break;
+ }
+ }
+ msIdCache.put(msId, mapperEnhancer);
+ }
+ return mapperEnhancer;
+ }
+
+ /**
+ * 重新设置SqlSource
+ *
+ * @param ms
+ */
+ public void setSqlSource(MappedStatement ms, Configuration configuration) {
+ MapperEnhancer mapperEnhancer = getMapperTemplate(ms.getId());
+ try {
+ if (mapperEnhancer != null) {
+ mapperEnhancer.setSqlSource(ms, configuration);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("调用方法异常:" + e.getMessage(), e);
+ }
+ }
+
+ /**
+ * 处理configuration中全部的MappedStatement
+ *
+ * @param configuration
+ */
+ public void processConfiguration(Configuration configuration) {
+ Collection collection = configuration.getMappedStatements();
+ // 防止反复处理一个
+ if (collectionSet.contains(collection)) {
+ return;
+ } else {
+ collectionSet.add(collection);
+ }
+
+ Collection tmpCollection = new HashSet<>();
+ tmpCollection.addAll(collection);
+
+ Iterator iterator = tmpCollection.iterator();
+ while (iterator.hasNext()) {
+ Object object = iterator.next();
+ if (object instanceof MappedStatement) {
+ MappedStatement ms = (MappedStatement) object;
+ if (isMapperMethod(ms.getId())) {
+ setSqlSource(ms, configuration);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperResourceEnhancer.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperResourceEnhancer.java
new file mode 100644
index 0000000..49ba0e0
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperResourceEnhancer.java
@@ -0,0 +1,123 @@
+package com.mogujie.trade.tsharding.route.orm;
+
+import com.mogujie.trade.tsharding.client.ShardingCaculator;
+import org.apache.ibatis.builder.StaticSqlSource;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.mapping.ParameterMapping;
+import org.apache.ibatis.mapping.SqlSource;
+import org.apache.ibatis.scripting.defaults.RawSqlSource;
+import org.apache.ibatis.scripting.xmltags.*;
+import org.apache.ibatis.session.Configuration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Mappper sql增强
+ *
+ * @author qigong on 5/1/15
+ */
+public class MapperResourceEnhancer extends MapperEnhancer{
+
+ Logger logger = LoggerFactory.getLogger(MapperResourceEnhancer.class);
+
+ public MapperResourceEnhancer(Class> mapperClass) {
+ super(mapperClass);
+ }
+
+ public SqlSource enhancedShardingSQL(MappedStatement ms, Configuration configuration, Long shardingPara) {
+
+ String tableName = ShardingCaculator.caculateTableName(shardingPara);
+ SqlSource result = null;
+
+ try {
+ if (ms.getSqlSource() instanceof DynamicSqlSource) {
+
+ DynamicSqlSource sqlSource = (DynamicSqlSource) ms.getSqlSource();
+
+ Class sqlSourceClass = sqlSource.getClass();
+
+ Field sqlNodeField = sqlSourceClass.getDeclaredField("rootSqlNode");
+ sqlNodeField.setAccessible(true);
+
+ MixedSqlNode rootSqlNode = (MixedSqlNode) sqlNodeField.get(sqlSource);
+
+ Class mixedSqlNodeClass = rootSqlNode.getClass();
+ Field contentsField = mixedSqlNodeClass.getDeclaredField("contents");
+ contentsField.setAccessible(true);
+ List textSqlNodes = (List) contentsField.get(rootSqlNode);
+ List newSqlNodesList = new ArrayList();
+
+ //StaticTextSqlNode
+ Class textSqlNodeClass = textSqlNodes.get(0).getClass();
+ Field textField = textSqlNodeClass.getDeclaredField("text");
+ textField.setAccessible(true);
+ for (SqlNode node : textSqlNodes) {
+ if (node instanceof StaticTextSqlNode) {
+ StaticTextSqlNode textSqlNode = (StaticTextSqlNode) node;
+ String text = (String) textField.get(textSqlNode);
+ if(!text.contains("TestTable") && !text.contains("TestTable1")){
+ newSqlNodesList.add(node);
+ }else {
+ newSqlNodesList.add(new StaticTextSqlNode(replaceWithShardingTableName(text, tableName, shardingPara)));
+ }
+ }else{
+ newSqlNodesList.add(node);
+ }
+ }
+
+ MixedSqlNode newrootSqlNode = new MixedSqlNode(newSqlNodesList);
+ result = new DynamicSqlSource(configuration, newrootSqlNode);
+ return result;
+
+ } else if (ms.getSqlSource() instanceof RawSqlSource) {
+
+ RawSqlSource sqlSource = (RawSqlSource) ms.getSqlSource();
+ Class sqlSourceClass = sqlSource.getClass();
+ Field sqlSourceField = sqlSourceClass.getDeclaredField("sqlSource");
+ sqlSourceField.setAccessible(true);
+ StaticSqlSource staticSqlSource = (StaticSqlSource) sqlSourceField.get(sqlSource);
+ Field sqlField = staticSqlSource.getClass().getDeclaredField("sql");
+ Field parameterMappingsField = staticSqlSource.getClass().getDeclaredField("parameterMappings");
+ sqlField.setAccessible(true);
+ parameterMappingsField.setAccessible(true);
+
+ //sql处理
+ String sql = (String) sqlField.get(staticSqlSource);
+
+ if(!sql.contains("TestTable") && !sql.contains("TestTable1")){
+ result = sqlSource;
+ }else {
+ sql = replaceWithShardingTableName(sql, tableName, shardingPara);
+ result = new RawSqlSource(configuration, sql, null);
+ //为sqlSource对象设置mappering参数
+ StaticSqlSource newStaticSqlSource = (StaticSqlSource) sqlSourceField.get(result);
+ List parameterMappings = (List)parameterMappingsField.get(staticSqlSource);
+ parameterMappingsField.set(newStaticSqlSource, parameterMappings);
+ }
+ return result;
+ } else {
+ throw new RuntimeException("wrong sqlSource type!" + ms.getResource());
+ }
+
+ } catch (Exception e) {
+ logger.error("reflect error!, ms resources:" + ms.getResource(), e);
+ }
+ return result;
+ }
+
+
+ private String replaceWithShardingTableName(String text, String tableName, Long shardingPara){
+ if(text.contains(" TestTablePressureTest")){
+ return text.replace(" TestTablePressureTest", " TestTablePressureTest" + ShardingCaculator.getNumberWithZeroSuffix(shardingPara));
+ }else if(text.contains(" TestTable1PressureTest")){
+ return text.replace(" TestTable1PressureTest", " TestTable1PressureTest" + ShardingCaculator.getNumberWithZeroSuffix(shardingPara));
+ }else if(text.contains(" TestTable1")){
+ return text.replace(" TestTable1", " TestTable1" + ShardingCaculator.getNumberWithZeroSuffix(shardingPara));
+ }
+ return text.replace(" TestTable", " " + tableName);
+ }
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperScannerWithSharding.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperScannerWithSharding.java
new file mode 100644
index 0000000..778f7bc
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperScannerWithSharding.java
@@ -0,0 +1,162 @@
+package com.mogujie.trade.tsharding.route.orm;
+
+import com.mogujie.tesla.db.DataSourceLookup;
+import com.mogujie.tesla.db.ReadWriteSplittingDataSource;
+import com.mogujie.trade.tsharding.route.orm.base.*;
+import org.apache.ibatis.mapping.MappedStatement;
+import org.apache.ibatis.session.Configuration;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionFactoryBean;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
+import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
+import org.springframework.core.io.Resource;
+
+import java.io.IOException;
+import java.lang.reflect.*;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tsharding MybatisMapper的扫描类,负责将Mapper接口与对应的xml配置文件整合,绑定设定的数据源,注入到Spring Context中。
+ *
+ * @author qigong
+ */
+public class MapperScannerWithSharding implements BeanFactoryPostProcessor, InitializingBean {
+
+ public static DataSourceLookup dataSourceLookup;
+
+ private String packageName;
+
+ private Resource[] mapperLocations;
+
+ private String[] mapperPacakages;
+
+ private SqlSessionFactoryLookup sqlSessionFactoryLookup;
+
+ public static DataSourceLookup getDataSourceLookup() {
+ return dataSourceLookup;
+ }
+
+ @Override
+ public void afterPropertiesSet() throws Exception {
+ this.initMapperPackage();
+ }
+
+ private void initMapperPackage() throws IOException {
+ this.mapperPacakages = packageName.split(",");
+ }
+
+ @Override
+ public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
+ this.dataSourceLookup = beanFactory.getBean(DataSourceLookup.class);
+
+ try {
+ this.initSqlSessionFactories(beanFactory);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ ClassPathScanHandler scanner = new ClassPathScanHandler();
+ Set> mapperClasses = new HashSet<>();
+ for (String mapperPackage : this.mapperPacakages) {
+ Set> classes = scanner.getPackageAllClasses(mapperPackage.trim(), false);
+ mapperClasses.addAll(classes);
+ }
+ for (Class> clazz : mapperClasses) {
+ if (isMapper(clazz)) {
+ Object mapper = this.newMapper(clazz);
+ beanFactory.registerSingleton(Character.toLowerCase(clazz.getSimpleName().charAt(0))
+ + clazz.getSimpleName().substring(1), mapper);
+ }
+ }
+
+ }
+
+ private void initSqlSessionFactories(ConfigurableListableBeanFactory beanFactory) throws Exception {
+ Map sqlSessionFactories = new HashMap<>(this.dataSourceLookup.getMapping().size());
+
+ ReadWriteSplittingDataSource defaultDataSource = null;
+ SqlSessionFactory defaultSqlSessionFactory = null;
+ for (ReadWriteSplittingDataSource dataSource : this.dataSourceLookup.getMapping().values()) {
+
+ SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
+ sessionFactoryBean.setMapperLocations(mapperLocations);
+ sessionFactoryBean.setDataSource(dataSource);
+ sessionFactoryBean.setTypeAliasesPackage(this.packageName + ".domain.entity");
+
+ // init 初始化所有sql对应的元数据、资源(sqlNode, sqlSource, mappedStatement)等
+ sessionFactoryBean.afterPropertiesSet();
+
+ if (defaultDataSource == null) {
+ //第一个
+ defaultDataSource = dataSource;
+ defaultSqlSessionFactory = sessionFactoryBean.getObject();
+ } else {
+ SqlSessionFactory newSqlSessionFactory = sessionFactoryBean.getObject();
+ Field conf = newSqlSessionFactory.getClass().getDeclaredField("configuration");
+ conf.setAccessible(true);
+ Configuration newConfiguration = (Configuration) conf.get(newSqlSessionFactory);
+ Field mappedStatementField = newConfiguration.getClass().getDeclaredField("mappedStatements");
+
+ //去掉final修饰符
+ Field modifiersField = Field.class.getDeclaredField("modifiers");
+ modifiersField.setAccessible(true);
+ modifiersField.setInt( mappedStatementField, mappedStatementField.getModifiers() & ~Modifier.FINAL);
+ mappedStatementField.setAccessible(true);
+
+ //后续的元数据复用
+ Configuration defaultConfiguration = defaultSqlSessionFactory.getConfiguration();
+ Map reUsedMappedStatement = (Map) mappedStatementField.get(defaultConfiguration);
+ mappedStatementField.set(newConfiguration, reUsedMappedStatement);
+ }
+ beanFactory.registerSingleton(dataSource.getName() + "SqlSessionFactory", sessionFactoryBean);
+ sqlSessionFactories.put(dataSource.getName(), sessionFactoryBean.getObject());
+ defaultSqlSessionFactory = sessionFactoryBean.getObject();
+ }
+
+ this.sqlSessionFactoryLookup = new SqlSessionFactoryLookup(sqlSessionFactories);
+ }
+
+ private boolean isMapper(Class> clazz) {
+ if (clazz.isInterface()) {
+ return true;
+ }
+ return false;
+ }
+
+ private Object newMapper(final Class> clazz) {
+
+ final Invoker invoker = new TShardingRoutingInvokeFactory(sqlSessionFactoryLookup).newInvoker(clazz);
+
+ return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz},
+ new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
+ return invoker.invoke(new DefaultInvocation(method, args));
+ }
+ });
+ }
+
+ /**
+ * 注入packageName配置
+ *
+ * @param packageName
+ */
+ public void setPackageName(String packageName) {
+ this.packageName = packageName;
+ }
+
+ /**
+ * 注入mapperLocations配置
+ *
+ * @param mapperLocations
+ */
+ public void setMapperLocations(Resource[] mapperLocations) {
+ this.mapperLocations = mapperLocations;
+ }
+}
+
+
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperShardingInitializer.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperShardingInitializer.java
new file mode 100644
index 0000000..11e8530
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/MapperShardingInitializer.java
@@ -0,0 +1,67 @@
+package com.mogujie.trade.tsharding.route.orm;
+
+import com.mogujie.service.xiaodian.trade.base.utils.SentryUtil;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.SqlSessionTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.BeansException;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 增强Mapper处理总入口:Mapper被mybatis初始化后,在这里做进一步的处理和增强
+ *
+ * @author qigong on 5/1/15
+ */
+public class MapperShardingInitializer implements ApplicationContextAware {
+
+
+ Logger logger = LoggerFactory.getLogger(getClass());
+
+ private String needEnhancedClasses;
+ private String[] needEnhancedClassesArray;
+
+ @Override
+ public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+
+ //初始化jvm监控信息
+ SentryUtil.getTradeOrderInstance().openAutoCollector();
+
+ Map sqlSessionFactories = applicationContext.getBeansOfType(SqlSessionFactory.class);
+ if (sqlSessionFactories.isEmpty()) {
+ return;
+ }
+ MapperHelperForSharding mapperHelperForSharding = new MapperHelperForSharding();
+ List sqlSessions = new ArrayList<>(sqlSessionFactories.size());
+ for (SqlSessionFactory sqlSessionFactory : sqlSessionFactories.values()) {
+ SqlSession sqlSession = new SqlSessionTemplate(sqlSessionFactory);
+ sqlSessions.add(sqlSession);
+ }
+ //Mapper代码增强 每个方法扩展出一个ShardingMapper类,增强为512个方法。
+ this.needEnhancedClassesArray = needEnhancedClasses.split(",");
+ this.enhanceMapperClass();
+ mapperHelperForSharding.setMappers(needEnhancedClassesArray);
+ mapperHelperForSharding.setSqlSessions(sqlSessions.toArray(new SqlSession[0]));
+ mapperHelperForSharding.initMapper();
+ }
+
+ private void enhanceMapperClass() {
+ for (String mapperClass : needEnhancedClassesArray) {
+ try {
+ MapperEnhancer.enhanceMapperClass(mapperClass);
+ } catch (Exception e) {
+ logger.error("Enhance {} class error", mapperClass, e);
+ }
+ }
+ }
+
+ public void setNeedEnhancedClasses(String needEnhancedClasses) {
+ this.needEnhancedClasses = needEnhancedClasses;
+ }
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ClassPathScanHandler.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ClassPathScanHandler.java
new file mode 100644
index 0000000..0115641
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ClassPathScanHandler.java
@@ -0,0 +1,273 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URL;
+import java.net.URLDecoder;
+import java.util.Enumeration;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.regex.Pattern;
+
+/**
+ * 扫描指定包(包括jar)下的class文件
+ * http://www.micmiu.com
+ *
+ * @author michael
+ */
+public class ClassPathScanHandler {
+
+ /**
+ * logger
+ */
+ private static final Logger logger = LoggerFactory.getLogger(ClassPathScanHandler.class);
+
+ /**
+ * 是否排除内部类 true->是 false->否
+ */
+ private boolean excludeInner = true;
+ /**
+ * 过滤规则适用情况 true—>搜索符合规则的 false->排除符合规则的
+ */
+ private boolean checkInOrEx = true;
+
+ /**
+ * 过滤规则列表 如果是null或者空,即全部符合不过滤
+ */
+ private List classFilters = null;
+
+ /**
+ * 无参构造器,默认是排除内部类、并搜索符合规则
+ */
+ public ClassPathScanHandler() {
+ }
+
+ /**
+ * excludeInner:是否排除内部类 true->是 false->否
+ * checkInOrEx:过滤规则适用情况 true—>搜索符合规则的 false->排除符合规则的
+ * classFilters:自定义过滤规则,如果是null或者空,即全部符合不过滤
+ *
+ * @param excludeInner
+ * @param checkInOrEx
+ * @param classFilters
+ */
+ public ClassPathScanHandler(Boolean excludeInner, Boolean checkInOrEx, List classFilters) {
+ this.excludeInner = excludeInner;
+ this.checkInOrEx = checkInOrEx;
+ this.classFilters = classFilters;
+
+ }
+
+ /**
+ * 扫描包
+ *
+ * @param basePackage
+ * 基础包
+ * @param recursive
+ * 是否递归搜索子包
+ * @return Set
+ */
+ public Set> getPackageAllClasses(String basePackage, boolean recursive) {
+ Set> classes = new LinkedHashSet>();
+ String packageName = basePackage;
+ if (packageName.endsWith(".")) {
+ packageName = packageName.substring(0, packageName.lastIndexOf('.'));
+ }
+ String package2Path = packageName.replace('.', '/');
+
+ Enumeration dirs;
+ try {
+ dirs = Thread.currentThread().getContextClassLoader().getResources(package2Path);
+ while (dirs.hasMoreElements()) {
+ URL url = dirs.nextElement();
+ String protocol = url.getProtocol();
+ if ("file".equals(protocol)) {
+ logger.info("扫描file类型的class文件....");
+ String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
+ doScanPackageClassesByFile(classes, packageName, filePath, recursive);
+ } else if ("jar".equals(protocol)) {
+ logger.info("扫描jar文件中的类....");
+ doScanPackageClassesByJar(packageName, url, recursive, classes);
+ }
+ }
+ } catch (IOException e) {
+ logger.error("IOException error:", e);
+ }
+
+ return classes;
+ }
+
+ /**
+ * 以jar的方式扫描包下的所有Class文件
+ *
+ * @param basePackage
+ * eg:michael.utils.
+ * @param url
+ * @param recursive
+ * @param classes
+ */
+ private void doScanPackageClassesByJar(String basePackage, URL url, final boolean recursive, Set> classes) {
+ String packageName = basePackage;
+ String package2Path = packageName.replace('.', '/');
+ JarFile jar;
+ try {
+ jar = ((JarURLConnection) url.openConnection()).getJarFile();
+ Enumeration entries = jar.entries();
+ while (entries.hasMoreElements()) {
+ JarEntry entry = entries.nextElement();
+ String name = entry.getName();
+ if (!name.startsWith(package2Path) || entry.isDirectory()) {
+ continue;
+ }
+
+ // 判断是否递归搜索子包
+ if (!recursive && name.lastIndexOf('/') != package2Path.length()) {
+ continue;
+ }
+ // 判断是否过滤 inner class
+ if (this.excludeInner && name.indexOf('$') != -1) {
+ logger.info("exclude inner class with name:" + name);
+ continue;
+ }
+ String classSimpleName = name.substring(name.lastIndexOf('/') + 1);
+ // 判定是否符合过滤条件
+ if (this.filterClassName(classSimpleName)) {
+ String className = name.replace('/', '.');
+ className = className.substring(0, className.length() - 6);
+ try {
+ classes.add(Thread.currentThread().getContextClassLoader().loadClass(className));
+ } catch (ClassNotFoundException e) {
+ logger.error("Class.forName error:", e);
+ }
+ }
+ }
+ } catch (IOException e) {
+ logger.error("IOException error:", e);
+ }
+ }
+
+ /**
+ * 以文件的方式扫描包下的所有Class文件
+ *
+ * @param packageName
+ * @param packagePath
+ * @param recursive
+ * @param classes
+ */
+ private void doScanPackageClassesByFile(Set> classes, String packageName, String packagePath,
+ boolean recursive) {
+ File dir = new File(packagePath);
+ if (!dir.exists() || !dir.isDirectory()) {
+ return;
+ }
+ final boolean fileRecursive = recursive;
+ File[] dirfiles = dir.listFiles(new FileFilter() {
+ // 自定义文件过滤规则
+ @Override
+ public boolean accept(File file) {
+ if (file.isDirectory()) {
+ return fileRecursive;
+ }
+ String filename = file.getName();
+ if (excludeInner && filename.indexOf('$') != -1) {
+ logger.info("exclude inner class with name:" + filename);
+ return false;
+ }
+ return filterClassName(filename);
+ }
+ });
+ for (File file : dirfiles) {
+ if (file.isDirectory()) {
+ doScanPackageClassesByFile(classes, packageName + "." + file.getName(), file.getAbsolutePath(),
+ recursive);
+ } else {
+ String className = file.getName().substring(0, file.getName().length() - 6);
+ try {
+ classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
+
+ } catch (ClassNotFoundException e) {
+ logger.error("IOException error:", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * 根据过滤规则判断类名
+ *
+ * @param className
+ * @return
+ */
+ private boolean filterClassName(String className) {
+ if (!className.endsWith(".class")) {
+ return false;
+ }
+ if (null == this.classFilters || this.classFilters.isEmpty()) {
+ return true;
+ }
+ String tmpName = className.substring(0, className.length() - 6);
+ boolean flag = false;
+ for (String str : classFilters) {
+ String tmpreg = "^" + str.replace("*", ".*") + "$";
+ Pattern p = Pattern.compile(tmpreg);
+ if (p.matcher(tmpName).find()) {
+ flag = true;
+ break;
+ }
+ }
+ return checkInOrEx && flag || !checkInOrEx && !flag;
+ }
+
+ /**
+ * @return the excludeInner
+ */
+ public boolean isExcludeInner() {
+ return excludeInner;
+ }
+
+ /**
+ * @return the checkInOrEx
+ */
+ public boolean isCheckInOrEx() {
+ return checkInOrEx;
+ }
+
+ /**
+ * @return the classFilters
+ */
+ public List getClassFilters() {
+ return classFilters;
+ }
+
+ /**
+ * @param pExcludeInner
+ * the excludeInner to set
+ */
+ public void setExcludeInner(boolean pExcludeInner) {
+ excludeInner = pExcludeInner;
+ }
+
+ /**
+ * @param pCheckInOrEx
+ * the checkInOrEx to set
+ */
+ public void setCheckInOrEx(boolean pCheckInOrEx) {
+ checkInOrEx = pCheckInOrEx;
+ }
+
+ /**
+ * @param pClassFilters
+ * the classFilters to set
+ */
+ public void setClassFilters(List pClassFilters) {
+ classFilters = pClassFilters;
+ }
+}
\ No newline at end of file
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/DefaultInvocation.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/DefaultInvocation.java
new file mode 100644
index 0000000..e58985f
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/DefaultInvocation.java
@@ -0,0 +1,41 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+public class DefaultInvocation implements Invocation {
+
+ private final Method method;
+
+ private final Object[] args;
+
+ public DefaultInvocation(Method method, Object[] args) {
+ this.method = method;
+ this.args = args;
+ }
+
+ @Override
+ public Method getMethod() {
+ return this.method;
+ }
+
+ @Override
+ public Object[] getArgs() {
+ return this.args;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("DefaultInvocation [");
+ if (method != null) {
+ builder.append("method=").append(method).append(", ");
+ }
+ if (args != null) {
+ builder.append("args=").append(Arrays.toString(args));
+ }
+ builder.append("]");
+ return builder.toString();
+ }
+
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invocation.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invocation.java
new file mode 100644
index 0000000..c77026c
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invocation.java
@@ -0,0 +1,14 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import java.lang.reflect.Method;
+
+/**
+ * @author qigong
+ *
+ */
+public interface Invocation {
+
+ Method getMethod();
+
+ Object[] getArgs();
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invoker.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invoker.java
new file mode 100644
index 0000000..32285f4
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/Invoker.java
@@ -0,0 +1,10 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+/**
+ * @author qigong
+ *
+ */
+public interface Invoker {
+
+ Object invoke(Invocation invocation) throws Throwable;
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/InvokerFactory.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/InvokerFactory.java
new file mode 100644
index 0000000..21327b2
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/InvokerFactory.java
@@ -0,0 +1,10 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+/**
+ * @author qigong
+ *
+ * @param
+ */
+public interface InvokerFactory {
+ Invoker newInvoker(T config);
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperBasicConfig.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperBasicConfig.java
new file mode 100644
index 0000000..63c29ea
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperBasicConfig.java
@@ -0,0 +1,28 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+/**
+ * Mapper管控基础 类-数据源
+ *
+ * @author qigong
+ *
+ */
+public class MapperBasicConfig {
+
+ private final Class> mapperInterface;
+
+ private final String dataSourceName;
+
+ public MapperBasicConfig(Class> mapperInterface, String dataSourceName) {
+ this.mapperInterface = mapperInterface;
+ this.dataSourceName = dataSourceName;
+ }
+
+ public Class> getMapperInterface() {
+ return mapperInterface;
+ }
+
+ public String getDataSourceName() {
+ return dataSourceName;
+ }
+
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperInitializeException.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperInitializeException.java
new file mode 100644
index 0000000..66e1492
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/MapperInitializeException.java
@@ -0,0 +1,24 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+public class MapperInitializeException extends RuntimeException {
+
+ private static final long serialVersionUID = -5010183715049161425L;
+
+ public MapperInitializeException(String message) {
+ super(message);
+ }
+
+ public MapperInitializeException(Throwable cause) {
+ super(cause);
+ }
+
+ public MapperInitializeException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public MapperInitializeException(String message, Throwable cause, boolean enableSuppression,
+ boolean writableStackTrace) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ }
+
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReadWriteSplittingContextInitializer.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReadWriteSplittingContextInitializer.java
new file mode 100644
index 0000000..5e8efca
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReadWriteSplittingContextInitializer.java
@@ -0,0 +1,79 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import com.mogujie.tesla.db.DataSourceType;
+import com.mogujie.tesla.db.ReadWriteSplitting;
+import com.mogujie.tesla.db.ReadWriteSplittingContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 读写分离的上下文初始化和清空
+ *
+ * @author qigong
+ */
+public class ReadWriteSplittingContextInitializer {
+
+ private final static Logger logger = LoggerFactory.getLogger(ReadWriteSplittingContextInitializer.class);
+
+ private final static String[] DEFAULT_WRITE_METHOD_NAMES = {"update", "save", "insert", "delete", "add",
+ "batchInsert", "batchUpdate", "batchSave", "batchAdd"};
+
+ private final static Map cache = new ConcurrentHashMap<>();
+
+ public static void initReadWriteSplittingContext(Method method) {
+ // 忽略object的方法,只关注Mapper的方法
+ if (method.getDeclaringClass() != Object.class) {
+ }
+ DataSourceType dataSourceType = getDataSourceType(method);
+ logger.debug("ReadWriteSplitting {} using dataSource of {}", method, dataSourceType);
+
+ ReadWriteSplittingContext.set(dataSourceType);
+ }
+
+ public static void clearReadWriteSplittingContext() {
+ ReadWriteSplittingContext.clear();
+ }
+
+ /**
+ * 获取方法对应的数据眼类型
+ *
+ * @param method
+ * @return
+ */
+ private static DataSourceType getDataSourceType(Method method) {
+ DataSourceType dataSourceType = cache.get(method);
+ if (dataSourceType == null) {
+ synchronized (method) {
+ dataSourceType = cache.get(method);
+ if (dataSourceType == null) {
+ dataSourceType = determineDataSourceType(method);
+ cache.put(method, dataSourceType);
+ }
+ }
+ }
+ return dataSourceType;
+ }
+
+ private static DataSourceType determineDataSourceType(Method method) {
+ DataSourceType dataSourceType = DataSourceType.slave;
+
+ ReadWriteSplitting readWriteSplitting = method.getAnnotation(ReadWriteSplitting.class);
+ if (readWriteSplitting != null) {
+ dataSourceType = readWriteSplitting.value();
+ dataSourceType = dataSourceType == null ? DataSourceType.master : dataSourceType;
+ } else {
+ for (String writeMethodName : DEFAULT_WRITE_METHOD_NAMES) {
+ if (method.getName().startsWith(writeMethodName)) {
+ dataSourceType = DataSourceType.master;
+ break;
+ }
+ }
+ }
+ return dataSourceType;
+ }
+
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReflectUtil.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReflectUtil.java
new file mode 100644
index 0000000..3737afa
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/ReflectUtil.java
@@ -0,0 +1,29 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import java.lang.reflect.Field;
+
+/**
+ * @author qigong on 15/9/17 下午8:05.
+ */
+public class ReflectUtil {
+
+ /**
+ * 循环向上转型, 获取对象的 DeclaredField
+ * @param object : 子类对象
+ * @param fieldName : 父类中的属性名
+ * @return 父类中的属性对象
+ */
+
+ public static Field getDeclaredField(Object object, String fieldName){
+ Field field = null ;
+ Class> clazz = object.getClass() ;
+ for(; clazz != Object.class ; clazz = clazz.getSuperclass()) {
+ try {
+ field = clazz.getDeclaredField(fieldName) ;
+ return field ;
+ } catch (Exception e) {
+ }
+ }
+ return null;
+ }
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/SqlSessionFactoryLookup.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/SqlSessionFactoryLookup.java
new file mode 100644
index 0000000..91155ff
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/SqlSessionFactoryLookup.java
@@ -0,0 +1,27 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import org.apache.ibatis.session.SqlSessionFactory;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SqlSessionFactoryLookup {
+
+ private Map mapping;
+
+ public SqlSessionFactoryLookup(Map mapping) {
+
+ Map tmpMap = new HashMap<>(mapping.size());
+ tmpMap.putAll(mapping);
+ this.mapping = Collections.unmodifiableMap(tmpMap);
+ }
+
+ public Map getMapping() {
+ return this.mapping;
+ }
+
+ public SqlSessionFactory get(String name) {
+ return this.mapping.get(name);
+ }
+}
diff --git a/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/TShardingRoutingInvokeFactory.java b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/TShardingRoutingInvokeFactory.java
new file mode 100644
index 0000000..b42ae7a
--- /dev/null
+++ b/tsharding-client/src/main/java/com/mogujie/trade/tsharding/route/orm/base/TShardingRoutingInvokeFactory.java
@@ -0,0 +1,213 @@
+package com.mogujie.trade.tsharding.route.orm.base;
+
+import com.alibaba.druid.pool.DruidDataSource;
+import com.alibaba.fastjson.JSON;
+import com.mogujie.tesla.db.DataSourceLookup;
+import com.mogujie.tesla.db.DataSourceRouting;
+import com.mogujie.tesla.db.DataSourceRoutingException;
+import com.mogujie.tesla.db.ReadWriteSplittingDataSource;
+import com.mogujie.trade.moguswitch.util.MoguStableSwitch;
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingBuyerPara;
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingOrderPara;
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingSellerPara;
+import com.mogujie.trade.tsharding.client.ShardingCaculator;
+import com.mogujie.trade.tsharding.route.TShardingRoutingHandler;
+import com.mogujie.trade.tsharding.route.orm.MapperScannerWithSharding;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.mybatis.spring.mapper.MapperFactoryBean;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.util.StringUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.SQLException;
+import java.util.List;
+
+public class TShardingRoutingInvokeFactory implements InvokerFactory> {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private SqlSessionFactoryLookup sqlSessionFactoryLookup;
+
+ public TShardingRoutingInvokeFactory(SqlSessionFactoryLookup sqlSessionFactoryLookup) {
+ this.sqlSessionFactoryLookup = sqlSessionFactoryLookup;
+ }
+
+ @Override
+ public Invoker newInvoker(Class> mapperInterface) {
+
+ final DataSourceRouting dataSourceRouting = mapperInterface.getAnnotation(DataSourceRouting.class);
+ final Class clazz = mapperInterface;
+
+ if (dataSourceRouting != null && !StringUtils.isEmpty(dataSourceRouting.value())) { //使用配置的数据源
+ logger.debug("TShardingRoutingInvokeFactory routing: emptyHandler and dataSourceRouting.value:" + dataSourceRouting.value());
+ return new Invoker() {
+ @Override
+ public Object invoke(Invocation invocation) throws Throwable {
+
+ MapperBasicConfig config = new MapperBasicConfig(clazz, dataSourceRouting.value());
+ final Object mapper = newMyBatisMapper(config);
+ try {
+ ReadWriteSplittingContextInitializer.initReadWriteSplittingContext(invocation.getMethod());
+ return invocation.getMethod().invoke(mapper, invocation.getArgs());
+ } finally {
+ ReadWriteSplittingContextInitializer.clearReadWriteSplittingContext();
+ }
+ }
+ };
+ } else if (dataSourceRouting != null && dataSourceRouting.handler() == TShardingRoutingHandler.class) { //使用Sharding数据源
+ logger.debug("TShardingRoutingInvokeFactory routing: dynamic handler: " + dataSourceRouting.handler().getName());
+ return new Invoker() {
+ @Override
+ public Object invoke(Invocation invocation) throws Throwable {
+
+ Method method = invocation.getMethod();
+ ShardingMetadata shardingMetadata = getShardingKey(method, invocation.getArgs());
+
+ if (shardingMetadata == null) {
+ throw new DataSourceRoutingException("dataSourceRouting error! Method Name:" + method.getName() + " shardingMetadata is null!");
+ }
+
+ //走分库分表环境
+ logger.debug("TShardingRoutingInvokeFactory routing to sharding db. Method Name:" + method.getName() + ". ShardingKey:" + shardingMetadata.getShardingKey());
+
+ Class newClass = clazz;
+ if (!"".equals(shardingMetadata.getSchemaName())) {
+ newClass = Class.forName(clazz.getCanonicalName() + "Sharding" + method.getName());
+ }
+ Method newMethod = newClass.getMethod(method.getName() + shardingMetadata.getTableSuffix(), method.getParameterTypes());
+ MapperBasicConfig config = new MapperBasicConfig(newClass, shardingMetadata.getSchemaName());
+ final Object mapper = newMyBatisMapper(config);
+ try {
+ ReadWriteSplittingContextInitializer.initReadWriteSplittingContext(invocation.getMethod());
+ return newMethod.invoke(mapper, invocation.getArgs());
+ } finally {
+ ReadWriteSplittingContextInitializer.clearReadWriteSplittingContext();
+ }
+ }
+ };
+ } else {
+ throw new DataSourceRoutingException("dataSourceRouting error! cannot find datasource");
+ }
+ }
+
+
+ private ShardingMetadata getShardingKey(Method method, Object[] args) throws NoSuchFieldException, IllegalAccessException {
+ Annotation[][] an = method.getParameterAnnotations();
+ if (an.length > 0) {
+ for (int i = 0; i < an.length; i++) {
+ for (int j = 0; j < an[i].length; j++) {
+ if (an[i][j] instanceof ShardingOrderPara || an[i][j] instanceof ShardingBuyerPara || an[i][j] instanceof ShardingSellerPara) {
+ Long shardingKey = 0L;
+ if (args[i] instanceof Long) {
+ shardingKey = (Long) args[i];
+ } else if (args[i] instanceof List) {
+ shardingKey = (Long) ((List) args[i]).get(0);
+ } else if (an[i][j] instanceof ShardingOrderPara && args[i] instanceof Object) {
+ Field field = ReflectUtil.getDeclaredField(args[i], "orderId");
+ field.setAccessible(true);
+ shardingKey = (Long) field.get(args[i]);
+ if (shardingKey == null) {
+ field = ReflectUtil.getDeclaredField(args[i], "parentOrderId");
+ field.setAccessible(true);
+ shardingKey = (Long) field.get(args[i]);
+ }
+ } else if (an[i][j] instanceof ShardingBuyerPara && args[i] instanceof Object) {
+ Field field = ReflectUtil.getDeclaredField(args[i], "buyerUserId");
+ field.setAccessible(true);
+ shardingKey = (Long) field.get(args[i]);
+ } else if (an[i][j] instanceof ShardingSellerPara && args[i] instanceof Object) {
+ Field field = ReflectUtil.getDeclaredField(args[i], "sellerUserId");
+ field.setAccessible(true);
+ shardingKey = (Long) field.get(args[i]);
+ }
+
+ String schemaName = null;
+ if (an[i][j] instanceof ShardingOrderPara) {
+ schemaName = ShardingCaculator.caculateSchemaName("orderId", shardingKey);
+ } else if (an[i][j] instanceof ShardingBuyerPara) {
+ schemaName = ShardingCaculator.caculateSchemaName("buyerUserId", shardingKey);
+ } else if (an[i][j] instanceof ShardingSellerPara) {
+ schemaName = ShardingCaculator.caculateSchemaName("sellerUserId", shardingKey);
+ }
+ ShardingMetadata shardingMetadata = new ShardingMetadata();
+ shardingMetadata.setShardingKey(shardingKey);
+ shardingMetadata.setTableSuffix(ShardingCaculator.getNumberWithZeroSuffix((shardingKey % 10000) % 512));
+ shardingMetadata.setSchemaName(schemaName);
+ return shardingMetadata;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object newMyBatisMapper(MapperBasicConfig config) {
+ MapperFactoryBean mapperFactoryBean = new MapperFactoryBean();
+ mapperFactoryBean.setMapperInterface(config.getMapperInterface());
+ mapperFactoryBean.setSqlSessionFactory(this.getSqlSessionFactory(config.getDataSourceName(),
+ config.getMapperInterface()));
+ mapperFactoryBean.afterPropertiesSet();
+ Object mapper = null;
+ try {
+ mapper = mapperFactoryBean.getObject();
+ } catch (Exception e) {
+ throw new MapperInitializeException(e);
+ }
+ return mapper;
+ }
+
+ private SqlSessionFactory getSqlSessionFactory(String dataSourceName, Class> mapperInterface) {
+ if (StringUtils.isEmpty(dataSourceName)) {
+ if (sqlSessionFactoryLookup.getMapping().size() == 1) {
+ return sqlSessionFactoryLookup.getMapping().values().iterator().next();
+ } else {
+ throw new DataSourceRoutingException("can't decided the datasource of "
+ + mapperInterface.getCanonicalName() + ",please add config by using @DataSourceRouting");
+ }
+ } else {
+ SqlSessionFactory sqlSessionFactory = sqlSessionFactoryLookup.get(dataSourceName);
+ if (sqlSessionFactory == null) {
+ throw new DataSourceRoutingException("can't find datasource named " + dataSourceName
+ + " while init!");
+ }
+ return sqlSessionFactory;
+ }
+ }
+
+ private class ShardingMetadata {
+
+ private Long shardingKey;
+
+ private String schemaName;
+
+ private String tableSuffix;
+
+ public String getSchemaName() {
+ return schemaName;
+ }
+
+ public void setSchemaName(String schemaName) {
+ this.schemaName = schemaName;
+ }
+
+ public String getTableSuffix() {
+ return tableSuffix;
+ }
+
+ public void setTableSuffix(String tableSuffix) {
+ this.tableSuffix = tableSuffix;
+ }
+
+ public Long getShardingKey() {
+ return shardingKey;
+ }
+
+ public void setShardingKey(Long shardingKey) {
+ this.shardingKey = shardingKey;
+ }
+ }
+}
diff --git a/tsharding-client/src/main/resources/tesla/support/service-loader.xml b/tsharding-client/src/main/resources/tesla/support/service-loader.xml
new file mode 100644
index 0000000..976ba0b
--- /dev/null
+++ b/tsharding-client/src/main/resources/tesla/support/service-loader.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDao.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDao.java
new file mode 100644
index 0000000..5c14abe
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDao.java
@@ -0,0 +1,13 @@
+package com.mogujie.service.tsharding.annotation;
+
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingBuyerPara;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @auther qigong on 5/29/15 11:13 AM.
+ */
+public interface SimpleDao {
+
+ public void abc(String a,String b,@ShardingBuyerPara @Param("test") Long id);
+
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoImpl.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoImpl.java
new file mode 100644
index 0000000..10e2196
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoImpl.java
@@ -0,0 +1,16 @@
+package com.mogujie.service.tsharding.annotation;
+
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingBuyerPara;
+import org.apache.ibatis.annotations.Param;
+
+/**
+ * @auther qigong on 5/29/15 2:24 PM.
+ */
+public class SimpleDaoImpl implements SimpleDao {
+
+
+ public void abc(String a,String b,@ShardingBuyerPara @Param("xxx") Long id){
+
+ System.out.println("real method");
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoWithAnotationTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoWithAnotationTest.java
new file mode 100644
index 0000000..9eb9985
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/annotation/SimpleDaoWithAnotationTest.java
@@ -0,0 +1,71 @@
+package com.mogujie.service.tsharding.annotation;
+
+import com.alibaba.fastjson.JSON;
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingBuyerPara;
+import org.apache.ibatis.annotations.Param;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * @auther qigong on 5/29/15 12:46 PM.
+ */
+public class SimpleDaoWithAnotationTest {
+
+ private static ShardingBuyerPara a;
+
+ public static void main(String[] args) {
+ Class z = SimpleDao.class;
+ try {
+
+ InvocationHandler handler = new InvocationHandler() {
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ System.out.println("proxy method");
+
+
+ System.out.println(JSON.toJSONString(method.getParameterTypes()));
+
+ Annotation[][] an = method.getParameterAnnotations();
+
+ if (an.length > 0) {
+ for (int i = 0; i < an.length; i++) {
+ System.out.print("i::" + i + ";");
+ System.out.println(JSON.toJSONString(an[i].length));
+ for (int j = 0; j < an[i].length; j++) {
+ System.out.println("j::" + j);
+ if(j==0) {
+ a = (ShardingBuyerPara) an[i][j];
+ System.out.println("sharding参数"+args[i]);
+ }else {
+ Param b = (Param) an[i][j];
+ System.out.println(b.value());
+ }
+ }
+ }
+ }
+
+
+ return method.invoke(SimpleDaoImpl.class.newInstance(), new Object[]{"abc", "cde", 4191574207234L});
+ }
+ };
+ Proxy proxy = (Proxy) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{SimpleDao.class}, handler);
+
+
+ Method m = z.getMethod("abc", new Class[]{String.class, String.class, Long.class});
+
+ try {
+ handler.invoke(proxy, m, new Object[]{"abc", "cde", 4191574207234L});
+ } catch (Throwable throwable) {
+ throwable.printStackTrace();
+ }
+
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/client/ShardingCaculatorTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/client/ShardingCaculatorTest.java
new file mode 100644
index 0000000..394c19f
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/client/ShardingCaculatorTest.java
@@ -0,0 +1,80 @@
+package com.mogujie.service.tsharding.client;
+
+import com.mogujie.trade.tsharding.client.ShardingCaculator;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * @auther qigong on 5/29/15 8:28 AM.
+ */
+public class ShardingCaculatorTest {
+
+
+ @Test
+ public void testCaculateTableName() {
+
+ ShardingparaObj para = new ShardingparaObj();
+ para.setName("buyerId");
+ para.setValue(100000000L);
+ Assert.assertEquals("TestTable0000", ShardingCaculator.caculateTableName(para.getValue()));
+ para.setValue(100000128L);
+ Assert.assertEquals("TestTable0128", ShardingCaculator.caculateTableName(para.getValue()));
+ para.setValue(100000512L);
+ Assert.assertEquals("TestTable0000", ShardingCaculator.caculateTableName(para.getValue()));
+ }
+
+ @Test
+ public void testCaculateSchemaName() {
+
+ ShardingparaObj para = new ShardingparaObj();
+ para.setName("sellerUserId");
+ para.setValue(100000000L);
+ Assert.assertEquals("sellertestschema0000", ShardingCaculator.caculateSchemaName(para.getName(), para.getValue()));
+ para.setValue(100000128L);
+ Assert.assertEquals("sellertestschema0002", ShardingCaculator.caculateSchemaName(para.getName(), para.getValue()));
+ para.setName("buyerUserId");
+ para.setValue(100000512L);
+ Assert.assertEquals("testschema0000", ShardingCaculator.caculateSchemaName(para.getName(), para.getValue()));
+ }
+
+ @Test
+ public void testCaculateDatasourceName() {
+
+ ShardingparaObj para = new ShardingparaObj();
+ para.setName("sellerUserId");
+ para.setValue(100000000L);
+ Assert.assertEquals("seller_ds_0", ShardingCaculator.caculateDatasourceName(para.getName(), para.getValue()));
+ para.setValue(100000128L);
+ Assert.assertEquals("seller_ds_0", ShardingCaculator.caculateDatasourceName(para.getName(), para.getValue()));
+ para.setName("buyerUserId");
+ para.setValue(100000511L);
+ Assert.assertEquals("buyer_ds_1", ShardingCaculator.caculateDatasourceName(para.getName(), para.getValue()));
+ }
+
+ @Test
+ public void testgetNumberWithZeroSuffix(){
+ Assert.assertEquals("0100", ShardingCaculator.getNumberWithZeroSuffix(100L));
+ }
+
+ private class ShardingparaObj {
+ private String name;
+ private Long value;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setValue(Long value) {
+ this.value = value;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Long getValue() {
+ return value;
+ }
+
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/InvokerTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/InvokerTest.java
new file mode 100644
index 0000000..969f386
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/InvokerTest.java
@@ -0,0 +1,13 @@
+package com.mogujie.service.tsharding.route;
+
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingOrderPara;
+import com.mogujie.trade.tsharding.route.orm.base.Invocation;
+
+/**
+ * @author qigong
+ *
+ */
+public interface InvokerTest {
+
+ Object invoke(@ShardingOrderPara("test") Invocation invocation) throws Throwable;
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/SchemaPropertiesTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/SchemaPropertiesTest.java
new file mode 100644
index 0000000..8295299
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/SchemaPropertiesTest.java
@@ -0,0 +1,31 @@
+package com.mogujie.service.tsharding.route;
+
+import com.mogujie.trade.tsharding.client.ShardingCaculator;
+
+/**
+ * @author qigong on 15/9/7 下午6:16.
+ */
+public class SchemaPropertiesTest {
+
+ public static void main(String[] args) {
+
+ String[] initConfs = new String[]{
+ "# testschemapressuretest master",
+ "testschemapressuretest.master.url=jdbc:mysql://10.11.4.110/testschemapressuretest",
+ "testschemapressuretest.master.port=3306",
+ "testschemapressuretest.master.username=pigeontest",
+ "testschemapressuretest.master.password=3ff143dd2bbd399a3af6349f1b46f2e2",
+ "testschemapressuretest.master.minPoolSize=1",
+ "testschemapressuretest.master.maxPoolSize=24",
+ "testschemapressuretest.master.initialPoolSize=1"
+ };
+
+ for (long i = 0; i < 8; i++) {
+ for (String conf : initConfs) {
+ conf = conf.replace("testschemapressuretest", "testschemapressuretest" + ShardingCaculator.getNumberWithZeroSuffix(i));
+ conf = conf.replace("sharding0", "sharding" + i/4);
+ System.out.println(conf);
+ }
+ }
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderDaoTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderDaoTest.java
new file mode 100644
index 0000000..cb4d363
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderDaoTest.java
@@ -0,0 +1,30 @@
+package com.mogujie.service.tsharding.route;
+
+import com.mogujie.service.tsharding.route.dal.ShopOrder;
+import com.mogujie.service.tsharding.route.dal.ShopOrderDao;
+import org.junit.Assert;
+import org.junit.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @auther qigong on 6/5/15 8:55 PM.
+ */
+public class ShopOrderDaoTest {
+
+ @Autowired
+ private ShopOrderDao shopOrderDao;
+
+ @Test
+ public void testGetShopOrderByShopOrderIdsOfOneBuyer() {
+
+ List listShopOrderIds = new ArrayList<>();
+ listShopOrderIds.add(3968484880824L);
+ listShopOrderIds.add(3968484880824L);
+
+ List shopOrders = shopOrderDao.getShopOrderByShopOrderIdsOfOneBuyer(listShopOrderIds);
+ Assert.assertTrue(shopOrders.size() == 2);
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderMapperTest.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderMapperTest.java
new file mode 100644
index 0000000..e83c004
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/ShopOrderMapperTest.java
@@ -0,0 +1,110 @@
+package com.mogujie.service.tsharding.route;
+
+import com.mogujie.service.tsharding.route.dal.ShopOrder;
+import com.mogujie.service.tsharding.route.dal.ShopOrderMapper;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class ShopOrderMapperTest {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private ConfigurableApplicationContext applicationContext;
+
+ @Autowired
+ private ShopOrderMapper shopOrderMapper;
+
+ @Before
+ public void setUp() throws Exception {
+ this.applicationContext = new ClassPathXmlApplicationContext("META-INF/tesla/support/service-loader.xml",
+ "META-INF/tesla/support/datasource.xml");
+ this.shopOrderMapper = applicationContext.getBean(ShopOrderMapper.class);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ this.applicationContext.close();
+ }
+
+
+ @Test
+ public void testXdOrderSharding() {
+
+ ShopOrder shopOrder = null;
+ try {
+ shopOrder = shopOrderMapper.getShopOrderByShopOrderId(3950627820824L);
+ logger.debug("{}", shopOrder);
+ Assert.assertNotNull(shopOrder);
+
+ shopOrder = shopOrderMapper.getShopOrderByShopOrderId(3950627820002L);
+ logger.debug("{}", shopOrder);
+ Assert.assertNull(shopOrder);
+
+// List shopOrders = shopOrderMapper.getShopOrderByShopOrderIdsOfOneBuyer(Arrays.asList(new Long[]{3950627820824L,3950631130824L}));
+// logger.debug("{}", shopOrders);
+// Assert.assertEquals(2, shopOrders.size());
+
+
+// Assert.assertEquals(new Integer(0), xdShopOrder.getVisible());
+// xdShopOrder.setVisible(1);
+// xdShopOrderMapperPart1.updateShopOrderByShopOrderIdAndStatus(xdShopOrder.getOrderId(),xdShopOrder, xdShopOrder.getStatus());
+// xdShopOrder = xdShopOrderMapper.getShopOrderByShopOrderId(3950627820824L);
+// logger.debug("after update visible:{}", xdShopOrders);
+// Assert.assertEquals(new Integer(1), xdShopOrder.getVisible());
+//
+// xdShopOrder.setVisible(0);
+// xdShopOrderMapperPart1.updateShopOrderByShopOrderIdAndStatus(xdShopOrder.getOrderId(),xdShopOrder, xdShopOrder.getStatus());
+// xdShopOrder = xdShopOrderMapper.getShopOrderByShopOrderId(3950627820824L);
+// Assert.assertEquals(new Integer(0), xdShopOrder.getVisible());
+
+// xdShopOrders = xdShopOrderMapperPart1.getShopOrdersByPayOrderIdsOfOneBuyer(Arrays.asList(new Long[]{3950627810824L, 3950631120824L}));
+// logger.debug("{}", xdShopOrders);
+// Assert.assertEquals(2, xdShopOrders.size());
+//
+// Long payOrderId = 3971291360824L;
+// XdShopOrder record = new XdShopOrder();
+// record.setParentOrderId(payOrderId);
+// record.setStatus(1 /*BusinessConstants.ORDER_STATUS_CANCELED*/);
+// Long now = (System.currentTimeMillis() / 1000);
+// record.setCancelTime(now);
+// record.setCancelReason("ddd");
+// record.setExpiredTime(1L);
+// record.setUpdated(1L);
+// xdShopOrderMapper.batchUpdateShopOrderByShopOrderIdsAndStatusOfOneBuyer(Arrays.asList(new Long[]{3971291380824L, 3971291370824L}), record, record.getStatus());
+// xdShopOrder = xdShopOrderMapper.getShopOrderByShopOrderId(3971291380824L);
+// logger.debug("{}", xdShopOrder);
+// Assert.assertEquals(now, xdShopOrder.getCancelTime());
+//
+//
+// //updateShipExpenseByShopOrderId
+// xdShopOrder.setOrderId(3981357789949L);
+// xdShopOrder.setShipExpense(1200L);
+// xdShopOrderMapper.updateShopOrderByShopOrderId(xdShopOrder.getOrderId(), xdShopOrder);
+// xdShopOrder = xdShopOrderMapper.getShopOrderByShopOrderId(3981357789949L);
+// logger.debug("{}", xdShopOrder);
+// Assert.assertEquals(new Long(1200), xdShopOrder.getShipExpense());
+//
+//
+// //扫单表
+// xdShopOrders = xdShopOrderMapper.getOrdersByUpdatedTime(181,1430740105L, 1430833654L);
+// logger.debug("{}", xdShopOrders);
+// Assert.assertTrue(xdShopOrders.size() > 1);
+
+ } catch (Exception e) {
+ logger.error("test error", e);
+ }
+
+ }
+
+
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/Test.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/Test.java
new file mode 100644
index 0000000..16a376e
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/Test.java
@@ -0,0 +1,61 @@
+package com.mogujie.service.tsharding.route;
+
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingOrderPara;
+import com.mogujie.trade.tsharding.route.orm.MapperAnnotationEnhancer;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+
+import java.io.IOException;
+
+/**
+ * @author qigong on 15/9/5 下午12:14.
+ */
+public class Test {
+
+ public static void main(String[] args) {
+
+ ClassPool cp = ClassPool.getDefault();
+ try {
+ CtClass cc = cp.makeInterface("com.mogujie.service.tsharding.route.Tester");
+
+ ClassPool pool = ClassPool.getDefault();
+
+ Class originClass = Class.forName("com.mogujie.service.tsharding.route.InvokerTest");
+
+
+ CtClass c = pool.get("com.mogujie.service.tsharding.route.InvokerTest");
+ CtMethod ctMethod = c.getDeclaredMethods()[0];
+ CtMethod cm = new CtMethod(ctMethod.getReturnType(), ctMethod.getName(), ctMethod.getParameterTypes(), cc);
+ ClassFile ccFile = cc.getClassFile();
+ ConstPool constPool = ccFile.getConstPool();
+ cm.getMethodInfo().addAttribute(MapperAnnotationEnhancer.duplicateParameterAnnotationsAttribute(constPool, originClass.getDeclaredMethods()[0]));
+
+ cc.addMethod(cm);
+ Class> newClass = cc.toClass();
+
+ java.lang.annotation.Annotation[][] annotations = newClass.getDeclaredMethods()[0].getParameterAnnotations();
+ if (annotations.length > 0) {
+ for (int i = 0; i < annotations.length; i++) {
+ for (int j = 0; j < annotations[i].length; j++) {
+ if (annotations[i][j] instanceof ShardingOrderPara) {
+ Long shardingPara = 0L;
+ System.out.println("comin again!");
+ break;
+ }
+ }
+ }
+ }
+
+ try {
+ cc.writeFile(".");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/TestJavaAssit.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/TestJavaAssit.java
new file mode 100644
index 0000000..f8b567a
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/TestJavaAssit.java
@@ -0,0 +1,61 @@
+package com.mogujie.service.tsharding.route;
+
+import javassist.CannotCompileException;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtMethod;
+import javassist.bytecode.AnnotationsAttribute;
+import javassist.bytecode.ClassFile;
+import javassist.bytecode.ConstPool;
+import javassist.bytecode.annotation.Annotation;
+import javassist.bytecode.annotation.IntegerMemberValue;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+/**
+ * @author qigong on 15/9/5 下午12:14.
+ */
+public class TestJavaAssit {
+
+ public static void main(String[] args){
+
+ ClassPool cp = ClassPool.getDefault();
+ try {
+ CtClass cc = cp.makeClass("com.mogujie.service.tsharding.route.Tester1");
+ CtMethod cm = new CtMethod(CtClass.intType, "add", new CtClass[] {
+ CtClass.intType, CtClass.intType }, cc);
+
+
+ ClassFile ccFile = cc.getClassFile();
+ ConstPool constpool = ccFile.getConstPool();
+
+ AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
+ Annotation annot = new Annotation("com.mogujie.trade.tsharding.annotation.parameter.ShardingTablePara", constpool);
+ annot.addMemberValue("value", new IntegerMemberValue(ccFile.getConstPool(), 0));
+
+ cm.getMethodInfo().addAttribute(attr);
+
+//
+ cm.setBody("return $1 + $2;");
+ cm.insertAfter("for(int i=0;i<$args.length;i++)"
+ + "{System.out.println(\"args[\"+i+\"]=\"+$args[i]);}");
+ cc.addMethod(cm);
+ Class> newClass = cc.toClass();
+ Object o = newClass.newInstance();
+ Method m = newClass.getDeclaredMethod("add", int.class, int.class);
+ System.out.println("Result:" + m.invoke(o, 1, 2));
+ try {
+ cc.writeFile(".");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (CannotCompileException e) {
+ e.printStackTrace();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/BaseOrder.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/BaseOrder.java
new file mode 100644
index 0000000..1f4c877
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/BaseOrder.java
@@ -0,0 +1,222 @@
+package com.mogujie.service.tsharding.route.dal;
+
+/**
+ * @author xueyan on 15/1/19.
+ */
+public abstract class BaseOrder {
+
+ /**
+ * 订单ID
+ */
+ private Long orderId;
+
+ /**
+ * 买家的userId
+ */
+ private Long buyerUserId;
+
+ /**
+ * 订单类型
+ */
+ private Integer type;
+
+ /**
+ * 商品级订单: 商品数量; 店铺级订单: 子商品级订单所有商品数量总和
+ */
+ private Integer number;
+
+ /**
+ * 商品级订单: 订单价格(计算商品打折), 店铺级订单: 子商品级订单价格总和(计算店铺优惠)
+ */
+ private Long price;
+
+ /**
+ * 促销详情:例如促销单号...
+ */
+ private String promotion;
+
+ /**
+ * 支付时间, 未支付时为空或NULL
+ */
+ private Long payTime;
+
+ /**
+ * 支付ID
+ */
+ private Long payId;
+ /**
+ * 订单当前状态的超时时刻
+ */
+ private Long expiredTime;
+
+ /**
+ * 下单平台类型, 0: PC; 1000000: APP
+ */
+ private Integer platformType;
+
+ /**
+ * 来源类型,用于来源跟踪
+ */
+ private String source;
+
+ /**
+ * 自定义扩展信息
+ */
+ private String extra;
+
+ /**
+ * 自定义扩展信息
+ */
+ private Long extraInt;
+
+ /**
+ * 创建时间
+ */
+ private Long created;
+
+ /**
+ * 更新时间
+ */
+ private Long updated;
+
+ public Long getOrderId() {
+ return orderId;
+ }
+
+ public void setOrderId(Long orderId) {
+ this.orderId = orderId;
+ }
+
+ public Long getBuyerUserId() {
+ return buyerUserId;
+ }
+
+ public void setBuyerUserId(Long buyerUserId) {
+ this.buyerUserId = buyerUserId;
+ }
+
+ public Integer getType() {
+ return type;
+ }
+
+ public void setType(Integer type) {
+ this.type = type;
+ }
+
+ public Integer getNumber() {
+ return number;
+ }
+
+ public void setNumber(Integer number) {
+ this.number = number;
+ }
+
+ public Long getPrice() {
+ return price;
+ }
+
+ public void setPrice(Long price) {
+ this.price = price;
+ }
+
+ public String getPromotion() {
+ return promotion;
+ }
+
+ public void setPromotion(String promotion) {
+ this.promotion = promotion;
+ }
+
+ public Long getPayTime() {
+ return payTime;
+ }
+
+ public void setPayTime(Long payTime) {
+ this.payTime = payTime;
+ }
+
+ public Long getPayId() {
+ return payId;
+ }
+
+ public void setPayId(Long payId) {
+ this.payId = payId;
+ }
+
+ public Long getExpiredTime() {
+ return expiredTime;
+ }
+
+ public void setExpiredTime(Long expiredTime) {
+ this.expiredTime = expiredTime;
+ }
+
+ public Integer getPlatformType() {
+ return platformType;
+ }
+
+ public void setPlatformType(Integer platformType) {
+ this.platformType = platformType;
+ }
+
+ public String getSource() {
+ return source;
+ }
+
+ public void setSource(String source) {
+ this.source = source;
+ }
+
+ public String getExtra() {
+ return extra;
+ }
+
+ public void setExtra(String extra) {
+ this.extra = extra;
+ }
+
+ public Long getExtraInt() {
+ return extraInt;
+ }
+
+ public void setExtraInt(Long extraInt) {
+ this.extraInt = extraInt;
+ }
+
+ public Long getCreated() {
+ return created;
+ }
+
+ public void setCreated(Long created) {
+ this.created = created;
+ }
+
+ public Long getUpdated() {
+ return updated;
+ }
+
+ public void setUpdated(Long updated) {
+ this.updated = updated;
+ }
+
+ @Override
+ public String toString() {
+ return "XdBaseOrder{" +
+ "orderId=" + orderId +
+ ", buyerUserId=" + buyerUserId +
+ ", type=" + type +
+ ", number=" + number +
+ ", price=" + price +
+ ", promotion='" + promotion + '\'' +
+ ", payTime=" + payTime +
+ ", payId=" + payId +
+ ", expiredTime=" + expiredTime +
+ ", platformType=" + platformType +
+ ", source='" + source + '\'' +
+ ", extra='" + extra + '\'' +
+ ", extraInt=" + extraInt +
+ ", created=" + created +
+ ", updated=" + updated +
+ '}';
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrder.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrder.java
new file mode 100644
index 0000000..6e5ba45
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrder.java
@@ -0,0 +1,280 @@
+package com.mogujie.service.tsharding.route.dal;
+
+/**
+ * @author xueyan on 15/1/19.
+ */
+public class ShopOrder extends BaseOrder {
+
+ /**
+ * 父订单ID
+ */
+ private Long parentOrderId;
+
+ /**
+ * 订单级别, 0: 商品级; 1: 店铺级
+ */
+ private final Integer level = 1;
+
+ /**
+ * 卖家的userId
+ */
+ private Long sellerUserId;
+
+ /**
+ * 交易流程类型, 0: 全款交易,1: 二阶段预售
+ */
+ private Integer processType;
+
+ /**
+ * 快递费用
+ */
+ private Long shipExpense;
+
+ /**
+ * 店铺级订单:店铺优惠; 支付级订单: 平台优惠
+ */
+ private Long promotionAmount;
+
+ /**
+ * 订单状态
+ */
+ private Integer status;
+
+ /**
+ * 订单扩展状态
+ */
+ private Integer statusEx;
+
+ /**
+ * 是否可见, 0: 可见 1: 不可见
+ */
+ private Integer visible;
+
+ /**
+ * 发货时间
+ */
+ private Long shipTime;
+
+ /**
+ * 确认收货时间
+ */
+ private Long receiveTime;
+
+ /**
+ * 确认收货触发类型, 0: 买家触发,1: 快递签收触发(自动), 2: 超时触发(自动)
+ */
+ private Integer receiveType;
+
+ /**
+ * 评论状态
+ */
+ private Integer rateFromBuyerVisible;
+
+ /**
+ * 取消订单时间, 如果取消与支付回调同时发生则优先考虑支付相关状态(取消时间为0)
+ */
+ private Long cancelTime;
+
+ /**
+ * 结算时间
+ */
+ private Long settlementTime;
+
+ /**
+ * 取消原因
+ */
+ private String cancelReason;
+
+ /**
+ * 买家备注
+ */
+ private String buyerComment;
+
+ /**
+ * 卖家备注
+ */
+ private String sellerComment;
+
+ /**
+ * 订单逆向流程状态
+ */
+ private Integer reverseStatus;
+
+
+ public static ShopOrder getInstance() {
+ return new ShopOrder();
+ }
+
+ public Long getParentOrderId() {
+ return this.parentOrderId;
+ }
+
+ public void setParentOrderId(Long parentOrderId) {
+ this.parentOrderId = parentOrderId;
+ }
+
+ public Integer getLevel() {
+ return level;
+ }
+
+ public Long getSellerUserId() {
+ return sellerUserId;
+ }
+
+ public void setSellerUserId(Long sellerUserId) {
+ this.sellerUserId = sellerUserId;
+ }
+
+ public Integer getProcessType() {
+ return processType;
+ }
+
+ public void setProcessType(Integer processType) {
+ this.processType = processType;
+ }
+
+ public Long getShipExpense() {
+ return shipExpense;
+ }
+
+ public void setShipExpense(Long shipExpense) {
+ this.shipExpense = shipExpense;
+ }
+
+ public Long getPromotionAmount() {
+ return promotionAmount;
+ }
+
+ public void setPromotionAmount(Long promotionAmount) {
+ this.promotionAmount = promotionAmount;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public void setStatus(Integer status) {
+ this.status = status;
+ }
+
+ public Integer getStatusEx() {
+ return statusEx;
+ }
+
+ public void setStatusEx(Integer statusEx) {
+ this.statusEx = statusEx;
+ }
+
+ public Integer getVisible() {
+ return visible;
+ }
+
+ public void setVisible(Integer visible) {
+ this.visible = visible;
+ }
+
+ public Long getShipTime() {
+ return shipTime;
+ }
+
+ public void setShipTime(Long shipTime) {
+ this.shipTime = shipTime;
+ }
+
+ public Long getReceiveTime() {
+ return receiveTime;
+ }
+
+ public void setReceiveTime(Long receiveTime) {
+ this.receiveTime = receiveTime;
+ }
+
+ public Integer getReceiveType() {
+ return receiveType;
+ }
+
+ public void setReceiveType(Integer receiveType) {
+ this.receiveType = receiveType;
+ }
+
+ public Integer getRateFromBuyerVisible() {
+ return rateFromBuyerVisible;
+ }
+
+ public void setRateFromBuyerVisible(Integer rateFromBuyerVisible) {
+ this.rateFromBuyerVisible = rateFromBuyerVisible;
+ }
+
+ public Long getCancelTime() {
+ return cancelTime;
+ }
+
+ public void setCancelTime(Long cancelTime) {
+ this.cancelTime = cancelTime;
+ }
+
+ public Long getSettlementTime() {
+ return settlementTime;
+ }
+
+ public void setSettlementTime(Long settlementTime) {
+ this.settlementTime = settlementTime;
+ }
+
+ public String getCancelReason() {
+ return cancelReason;
+ }
+
+ public void setCancelReason(String cancelReason) {
+ this.cancelReason = cancelReason;
+ }
+
+ public String getBuyerComment() {
+ return buyerComment;
+ }
+
+ public void setBuyerComment(String buyerComment) {
+ this.buyerComment = buyerComment;
+ }
+
+ public String getSellerComment() {
+ return sellerComment;
+ }
+
+ public void setSellerComment(String sellerComment) {
+ this.sellerComment = sellerComment;
+ }
+
+ public Integer getReverseStatus() {
+ return reverseStatus;
+ }
+
+ public void setReverseStatus(Integer reverseStatus) {
+ this.reverseStatus = reverseStatus;
+ }
+
+ @Override
+ public String toString() {
+ return "XdShopOrder{" +
+ "parentOrderId=" + parentOrderId +
+ ", level=" + level +
+ ", sellerUserId=" + sellerUserId +
+ ", processType=" + processType +
+ ", shipExpense=" + shipExpense +
+ ", promotionAmount=" + promotionAmount +
+ ", status=" + status +
+ ", statusEx=" + statusEx +
+ ", visible=" + visible +
+ ", shipTime=" + shipTime +
+ ", receiveTime=" + receiveTime +
+ ", receiveType=" + receiveType +
+ ", rateFromBuyerVisible=" + rateFromBuyerVisible +
+ ", cancelTime=" + cancelTime +
+ ", settlementTime=" + settlementTime +
+ ", cancelReason='" + cancelReason + '\'' +
+ ", buyerComment='" + buyerComment + '\'' +
+ ", sellerComment='" + sellerComment + '\'' +
+ ", reverseStatus=" + reverseStatus +
+ "} " + super.toString();
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDao.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDao.java
new file mode 100644
index 0000000..9a0e066
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDao.java
@@ -0,0 +1,31 @@
+package com.mogujie.service.tsharding.route.dal;
+
+import com.mogujie.tesla.db.DataSourceRouting;
+import com.mogujie.trade.tsharding.route.TShardingRoutingHandler;
+
+import java.util.List;
+
+/**
+ * @auther qigong on 6/5/15 8:50 PM.
+ */
+@DataSourceRouting(handler = TShardingRoutingHandler.class)
+public interface ShopOrderDao {
+
+ /**
+ * 根据店铺级订单ID获取订单信息(同一个买家)
+ *
+ * @param listShopOrderIds 店铺级订单ID集合
+ * @return List
+ */
+ List getShopOrderByShopOrderIdsOfOneBuyer(List listShopOrderIds);
+
+ /**
+ * 根据店铺级订单ID获取订单信息(多个买家)
+ *
+ * @param listShopOrderIds 店铺级订单ID集合
+ * @return List
+ */
+ List getShopOrderByShopOrderIdsOfMultiBuyer(List listShopOrderIds);
+
+
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDaoImpl.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDaoImpl.java
new file mode 100644
index 0000000..8cbcdcf
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderDaoImpl.java
@@ -0,0 +1,53 @@
+package com.mogujie.service.tsharding.route.dal;
+
+import com.mogujie.trade.tsharding.client.ShardingCaculator;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import java.util.*;
+
+/**
+ * @auther qigong on 6/5/15 8:52 PM.
+ */
+public class ShopOrderDaoImpl implements ShopOrderDao {
+
+ @Autowired
+ private ShopOrderMapper shopOrderMapper;
+
+ @Override
+ public List getShopOrderByShopOrderIdsOfOneBuyer(List listShopOrderIds) {
+ if (listShopOrderIds == null || listShopOrderIds.size() == 0) {
+ return null;
+ }
+ Set setShopOrderIds = new HashSet();
+ for (Long iShopOrderId : listShopOrderIds) {
+ if (iShopOrderId > 0) {
+ setShopOrderIds.add(iShopOrderId);
+ }
+ }
+ return shopOrderMapper.getShopOrderByShopOrderIdsOfOneBuyer(new ArrayList(setShopOrderIds));
+ }
+
+
+ @Override
+ public List getShopOrderByShopOrderIdsOfMultiBuyer(List listShopOrderIds) {
+ if (listShopOrderIds == null || listShopOrderIds.size() == 0) {
+ return null;
+ }
+ HashMap> shopOrderIdsMap = new HashMap();
+ for (Long shopOrderId : listShopOrderIds) {
+ Integer tableIndex = ShardingCaculator.caculateTableIndex(shopOrderId);
+ List orderIds = shopOrderIdsMap.get(tableIndex);
+ if (orderIds == null) {
+ orderIds = new ArrayList<>();
+ }
+ orderIds.add(shopOrderId);
+ shopOrderIdsMap.put(tableIndex, orderIds);
+ }
+ List result = new ArrayList<>();
+
+ for (Integer tableIndex : shopOrderIdsMap.keySet()) {
+ result.addAll(shopOrderMapper.getShopOrderByShopOrderIdsOfOneBuyer(shopOrderIdsMap.get(tableIndex)));
+ }
+ return result;
+ }
+}
diff --git a/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderMapper.java b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderMapper.java
new file mode 100644
index 0000000..e29e84f
--- /dev/null
+++ b/tsharding-client/src/test/java/com/mogujie/service/tsharding/route/dal/ShopOrderMapper.java
@@ -0,0 +1,20 @@
+package com.mogujie.service.tsharding.route.dal;
+
+import com.mogujie.tesla.db.DataSourceRouting;
+import com.mogujie.trade.tsharding.annotation.ShardingExtensionMethod;
+import com.mogujie.trade.tsharding.annotation.parameter.ShardingOrderPara;
+import com.mogujie.trade.tsharding.route.TShardingRoutingHandler;
+import com.mogujie.trade.tsharding.route.orm.MapperResourceEnhancer;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
+
+@DataSourceRouting(handler=TShardingRoutingHandler.class)
+public interface ShopOrderMapper {
+ @ShardingExtensionMethod(type = MapperResourceEnhancer.class, method = "enhancedShardingSQL")
+ public ShopOrder getShopOrderByShopOrderId(@ShardingOrderPara("orderId") Long shopOrderId);
+
+ @ShardingExtensionMethod(type = MapperResourceEnhancer.class, method = "enhancedShardingSQL")
+ public List getShopOrderByShopOrderIdsOfOneBuyer(@ShardingOrderPara("orderId") List xdShopOrderIds);
+
+}
\ No newline at end of file
diff --git a/tsharding-client/src/test/resources/META-INF/tesla/support/service-loader.xml b/tsharding-client/src/test/resources/META-INF/tesla/support/service-loader.xml
new file mode 100644
index 0000000..976ba0b
--- /dev/null
+++ b/tsharding-client/src/test/resources/META-INF/tesla/support/service-loader.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tsharding-client/src/test/resources/jdbc.properties b/tsharding-client/src/test/resources/jdbc.properties
new file mode 100644
index 0000000..7c08398
--- /dev/null
+++ b/tsharding-client/src/test/resources/jdbc.properties
@@ -0,0 +1,137 @@
+# testschema0000 master
+testschema0000.master.url=jdbc:mysql://127.0.0.1/testschema0000
+testschema0000.master.username=root
+testschema0000.master.password=root123
+testschema0000.master.minPoolSize=0
+testschema0000.master.maxPoolSize=10
+testschema0000.master.initialPoolSize=0
+# testschema0000 slave
+testschema0000.slave.url=jdbc:mysql://127.0.0.1/testschema0000
+testschema0000.slave.username=root
+testschema0000.slave.password=root123
+testschema0000.slave.minPoolSize=1
+testschema0000.slave.maxPoolSize=24
+testschema0000.slave.initialPoolSize=1
+
+# testschema0001 master
+testschema0001.master.url=jdbc:mysql://127.0.0.1/testschema0001
+testschema0001.master.username=root
+testschema0001.master.password=root123
+testschema0001.master.minPoolSize=0
+testschema0001.master.maxPoolSize=10
+testschema0001.master.initialPoolSize=0
+# testschema0001 slave
+testschema0001.slave.url=jdbc:mysql://127.0.0.1/testschema0001
+testschema0001.slave.username=root
+testschema0001.slave.password=root123
+testschema0001.slave.minPoolSize=1
+testschema0001.slave.maxPoolSize=24
+testschema0001.slave.initialPoolSize=1
+
+# testschema0002 master
+testschema0002.master.url=jdbc:mysql://127.0.0.1/testschema0002
+testschema0002.master.username=root
+testschema0002.master.password=root123
+testschema0002.master.minPoolSize=0
+testschema0002.master.maxPoolSize=10
+testschema0002.master.initialPoolSize=0
+# testschema0002 slave
+testschema0002.slave.url=jdbc:mysql://127.0.0.1/testschema0002
+testschema0002.slave.username=root
+testschema0002.slave.password=root123
+testschema0002.slave.minPoolSize=1
+testschema0002.slave.maxPoolSize=24
+testschema0002.slave.initialPoolSize=1
+
+# testschema0003 master
+testschema0003.master.url=jdbc:mysql://127.0.0.1/testschema0003
+
+testschema0003.master.username=root
+testschema0003.master.password=root123
+testschema0003.master.minPoolSize=0
+testschema0003.master.maxPoolSize=10
+testschema0003.master.initialPoolSize=0
+# testschema0003 slave
+testschema0003.slave.url=jdbc:mysql://127.0.0.1/testschema0003
+testschema0003.slave.username=root
+testschema0003.slave.password=root123
+testschema0003.slave.minPoolSize=1
+testschema0003.slave.maxPoolSize=24
+testschema0003.slave.initialPoolSize=1
+
+# testschema0004 master
+testschema0004.master.url=jdbc:mysql://127.0.0.1/testschema0004
+testschema0004.master.username=root
+testschema0004.master.password=root123
+testschema0004.master.minPoolSize=0
+testschema0004.master.maxPoolSize=10
+testschema0004.master.initialPoolSize=0
+# testschema0004 slave
+testschema0004.slave.url=jdbc:mysql://127.0.0.1/testschema0004
+testschema0004.slave.username=root
+testschema0004.slave.password=root123
+testschema0004.slave.minPoolSize=1
+testschema0004.slave.maxPoolSize=24
+testschema0004.slave.initialPoolSize=1
+
+# testschema0005 master
+testschema0005.master.url=jdbc:mysql://127.0.0.1/testschema0005
+testschema0005.master.username=root
+testschema0005.master.password=root123
+testschema0005.master.minPoolSize=0
+testschema0005.master.maxPoolSize=10
+testschema0005.master.initialPoolSize=0
+# testschema0005 slave
+testschema0005.slave.url=jdbc:mysql://127.0.0.1/testschema0005
+testschema0005.slave.username=root
+testschema0005.slave.password=root123
+testschema0005.slave.minPoolSize=1
+testschema0005.slave.maxPoolSize=24
+testschema0005.slave.initialPoolSize=1
+
+# testschema0006 master
+testschema0006.master.url=jdbc:mysql://127.0.0.1/testschema0006
+testschema0006.master.username=root
+testschema0006.master.password=root123
+testschema0006.master.minPoolSize=0
+testschema0006.master.maxPoolSize=10
+testschema0006.master.initialPoolSize=0
+# testschema0006 slave
+testschema0006.slave.url=jdbc:mysql://127.0.0.1/testschema0006
+testschema0006.slave.username=root
+testschema0006.slave.password=root123
+testschema0006.slave.minPoolSize=1
+testschema0006.slave.maxPoolSize=24
+testschema0006.slave.initialPoolSize=1
+
+# testschema0007 master
+testschema0007.master.url=jdbc:mysql://127.0.0.1/testschema0007
+testschema0007.master.username=root
+testschema0007.master.password=root123
+testschema0007.master.minPoolSize=0
+testschema0007.master.maxPoolSize=10
+testschema0007.master.initialPoolSize=0
+# testschema0007 slave
+testschema0007.slave.url=jdbc:mysql://127.0.0.1/testschema0007
+testschema0007.slave.username=root
+testschema0007.slave.password=root123
+testschema0007.slave.minPoolSize=1
+testschema0007.slave.maxPoolSize=24
+testschema0007.slave.initialPoolSize=1
+
+# testschema单库 master
+testschema.master.url=jdbc:mysql://127.0.0.1/trade
+testschema.master.username=root
+testschema.master.password=root123
+testschema.master.minPoolSize=0
+testschema.master.maxPoolSize=10
+testschema.master.initialPoolSize=0
+# rootjie slave
+testschema.slave.url=jdbc:mysql://127.0.0.1/trade
+testschema.slave.username=root
+testschema.slave.password=root123
+testschema.slave.minPoolSize=1
+testschema.slave.maxPoolSize=24
+testschema.slave.initialPoolSize=1
+
+
diff --git a/tsharding-client/src/test/resources/logback-test.xml b/tsharding-client/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..ef8fb9f
--- /dev/null
+++ b/tsharding-client/src/test/resources/logback-test.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+ %d [%t] %5p \(%F:%L\) %M\(\) - %m%n
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tsharding-client/src/test/resources/sqlmap/shoporder-mapper.xml b/tsharding-client/src/test/resources/sqlmap/shoporder-mapper.xml
new file mode 100644
index 0000000..14256f4
--- /dev/null
+++ b/tsharding-client/src/test/resources/sqlmap/shoporder-mapper.xml
@@ -0,0 +1,4 @@
+
+
diff --git a/tsharding-client/src/test/resources/tesla.properties b/tsharding-client/src/test/resources/tesla.properties
new file mode 100644
index 0000000..670a3d2
--- /dev/null
+++ b/tsharding-client/src/test/resources/tesla.properties
@@ -0,0 +1,19 @@
+tesla.application.name = test-service
+tesla.owner = tesla
+tesla.port = 20019
+
+tesla.invokerExecutor.corePoolSize = 24
+tesla.invokerExecutor.maxPoolSize = 96
+tesla.invokerExecutor.keepAliveSeconds = 60
+tesla.invokerExecutor.queueCapacity = 5120
+
+tesla.worker.threads=0
+
+tesla.registry.ignoreError = true
+tesla.monitorTask.timeSpan = 60000
+
+tesla.network.readIdleTimeout = 0
+tesla.network.compressThreshold = 1024
+
+tesla.orm.mapperPacakge=com.mogujie.service.tsharding.route.dal
+tsharding.needEnhancedClasses=com.mogujie.service.tsharding.route.dal.ShopOrderMapper
\ No newline at end of file