package com.mogujie.trade.tsharding.route.orm.base;
import com.mogujie.trade.db.DataSourceRouting;
import com.mogujie.trade.db.DataSourceRoutingException;
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 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.util.List;
public class TShardingRoutingInvokeFactory implements InvokerFactory<Class<?>> {
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;
}
}
}