/*
* Copyright 2014 mango.jfaster.org
*
* The Mango Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package org.jfaster.mango.operator.generator;
import org.jfaster.mango.annotation.Sharding;
import org.jfaster.mango.annotation.ShardingBy;
import org.jfaster.mango.annotation.TableShardingBy;
import org.jfaster.mango.binding.BindingParameter;
import org.jfaster.mango.binding.BindingParameterInvoker;
import org.jfaster.mango.binding.ParameterContext;
import org.jfaster.mango.descriptor.ParameterDescriptor;
import org.jfaster.mango.exception.DescriptionException;
import org.jfaster.mango.exception.IncorrectParameterTypeException;
import org.jfaster.mango.sharding.NotUseShardingStrategy;
import org.jfaster.mango.sharding.NotUseTableShardingStrategy;
import org.jfaster.mango.sharding.TableShardingStrategy;
import org.jfaster.mango.util.reflect.Reflection;
import org.jfaster.mango.util.reflect.TypeToken;
import org.jfaster.mango.util.reflect.TypeWrapper;
import javax.annotation.Nullable;
import java.lang.reflect.Type;
/**
* @author ash
*/
public class TableGeneratorFactory {
public TableGenerator getTableGenerator(
@Nullable Sharding shardingAnno,
@Nullable String table,
boolean isSqlUseGlobalTable,
ParameterContext context) {
TableShardingStrategy strategy = getTableShardingStrategy(shardingAnno);
TypeToken<?> strategyToken = null;
if (strategy != null) {
strategyToken = TypeToken.of(strategy.getClass()).resolveFatherClass(TableShardingStrategy.class);
}
// 是否配置使用全局表
boolean isUseGlobalTable = table != null;
// 是否配置使用表切分
boolean isUseTableShardingStrategy = strategy != null;
if (isSqlUseGlobalTable && !isUseGlobalTable) {
throw new DescriptionException("if sql use global table '#table'," +
" @DB.table must be defined");
}
if (isUseTableShardingStrategy && !isUseGlobalTable) {
throw new DescriptionException("if @Sharding.tableShardingStrategy is defined, " +
"@DB.table must be defined");
}
int num = 0;
String parameterName = null;
String propertyPath = null;
for (ParameterDescriptor pd : context.getParameterDescriptors()) {
TableShardingBy tableShardingByAnno = pd.getAnnotation(TableShardingBy.class);
if (tableShardingByAnno != null) {
parameterName = context.getParameterNameByPosition(pd.getPosition());
propertyPath = tableShardingByAnno.value();
num++;
continue; // 有了@TableShardingBy,则忽略@ShardingBy
}
ShardingBy shardingByAnno = pd.getAnnotation(ShardingBy.class);
if (shardingByAnno != null) {
parameterName = context.getParameterNameByPosition(pd.getPosition());
propertyPath = shardingByAnno.value();
num++;
}
}
TableGenerator tableGenerator;
if (isUseTableShardingStrategy) {
if (num == 1) {
BindingParameter bp = BindingParameter.create(parameterName, propertyPath, null);
BindingParameterInvoker invoker = context.getBindingParameterInvoker(bp);
Type targetType = invoker.getTargetType();
TypeWrapper tw = new TypeWrapper(targetType);
Class<?> mappedClass = tw.getMappedClass();
if (mappedClass == null || tw.isIterable()) {
throw new IncorrectParameterTypeException("the type of parameter Modified @TableShardingBy is error, " +
"type is " + targetType + ", " +
"please note that @ShardingBy = @TableShardingBy + @DatabaseShardingBy");
}
TypeToken<?> shardToken = TypeToken.of(targetType);
if (!strategyToken.isAssignableFrom(shardToken.wrap())) {
throw new ClassCastException("TableShardingStrategy[" + strategy.getClass() + "]'s " +
"generic type[" + strategyToken.getType() + "] must be assignable from " +
"the type of parameter Modified @TableShardingBy [" + shardToken.getType() + "], " +
"please note that @ShardingBy = @TableShardingBy + @DatabaseShardingBy");
}
tableGenerator = new ShardedTableGenerator(table, invoker, strategy);
} else {
throw new DescriptionException("if @Sharding.tableShardingStrategy is defined, " +
"need one and only one @TableShardingBy on method's parameter but found " + num + ", " +
"please note that @ShardingBy = @TableShardingBy + @DatabaseShardingBy");
}
} else {
tableGenerator = new SimpleTableGenerator(table);
}
return tableGenerator;
}
@Nullable
private TableShardingStrategy getTableShardingStrategy(@Nullable Sharding shardingAnno) {
if (shardingAnno == null) {
return null;
}
Class<? extends TableShardingStrategy> strategyClass = shardingAnno.tableShardingStrategy();
if (!strategyClass.equals(NotUseTableShardingStrategy.class)) {
TableShardingStrategy strategy = Reflection.instantiateClass(strategyClass);
return strategy;
}
strategyClass = shardingAnno.shardingStrategy();
if (!strategyClass.equals(NotUseShardingStrategy.class)) {
TableShardingStrategy strategy = Reflection.instantiateClass(strategyClass);
return strategy;
}
return null;
}
}