package jef.database.routing.jdbc;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collection;
import java.util.List;
import jef.database.DbUtils;
import jef.database.ORMConfig;
import jef.database.TransactionalSession;
import jef.database.jdbc.GenerateKeyReturnOper;
import jef.database.jdbc.JDBCTarget;
import jef.database.jsqlparser.RemovedDelayProcess;
import jef.database.jsqlparser.SqlFunctionlocalization;
import jef.database.jsqlparser.expression.Table;
import jef.database.jsqlparser.statement.select.Select;
import jef.database.jsqlparser.visitor.Statement;
import jef.database.meta.AbstractMetadata;
import jef.database.query.DbTable;
import jef.database.query.ParameterProvider;
import jef.database.routing.sql.ExecuteablePlan;
import jef.database.routing.sql.QueryablePlan;
import jef.database.routing.sql.SqlAnalyzer;
import jef.database.routing.sql.SqlAndParameter;
import jef.database.routing.sql.TableMetaCollector;
import jef.tools.StringUtils;
import com.google.common.collect.Multimap;
public class RoutingSQLExecutor implements SQLExecutor {
private JDBCTarget db;
private int fetchSize = ORMConfig.getInstance().getGlobalFetchSize();
private int maxResult = 0;
private Statement st;
private SqlFunctionlocalization l;
/**
* 从SQL语句加上返回类型构造
*
* @param db
* @param sql
* @param resultClass
*/
public RoutingSQLExecutor(TransactionalSession db, Statement sql) {
if (StringUtils.isEmpty(sql)) {
throw new IllegalArgumentException("Please don't input an empty SQL.");
}
this.db = db.selectTarget(null);
this.st = sql;
l = new SqlFunctionlocalization(this.db.getProfile(), this.db);
sql.accept(l);
}
/**
* 返回fetchSize
*
* @return 每次游标获取的缓存大小
*/
public int getFetchSize() {
return fetchSize;
}
/**
* 设置fetchSize
*
* @param size
* 设置每次获取的缓冲大小
*/
public void setFetchSize(int size) {
this.fetchSize = size;
}
/**
* 以迭代器模式返回查询结果
*
* @return
* @throws SQLException
*/
public ResultSet getResultSet(int type, int concurrency, int holder, List<ParameterContext> params) throws SQLException {
SqlAndParameter parse = getSqlAndParams(db, this, params);
QueryablePlan plan = SqlAnalyzer.getSelectExecutionPlan((Select) parse.statement, parse.getParamsMap(), parse.params, db);
return plan.getResultSet(parse, maxResult, fetchSize);
}
private SqlAndParameter getSqlAndParams(JDBCTarget db2, RoutingSQLExecutor jQuery, List<ParameterContext> params) {
ContextProvider cp = new ContextProvider(params);
SqlAndParameter sp = new SqlAndParameter(st, SqlAnalyzer.asValue(params), cp);
if (l.delayLimit != null || l.delayStartWith != null) {
sp.setInMemoryClause(new RemovedDelayProcess(l.delayLimit, l.delayStartWith));
}
return sp;
}
static class ContextProvider implements ParameterProvider {
private List<ParameterContext> params;
public ContextProvider(List<ParameterContext> params) {
this.params = params;
}
@Override
public Object getNamedParam(String name) {
throw new UnsupportedOperationException();
}
@Override
public Object getIndexedParam(int index) {
return params.get(index).getValue();
}
@Override
public boolean containsParam(Object key) {
if (key instanceof Integer) {
return ((Integer) key) < params.size();
} else {
throw new UnsupportedOperationException();
}
}
}
/**
* 对于各种DDL、insert、update、delete等语句,不需要返回结果的,调用此方法来执行
*
* @return 返回影响到的记录条数(针对update\delete)语句
*/
public UpdateReturn executeUpdate(GenerateKeyReturnOper generateKeys, List<ParameterContext> params) throws SQLException {
SqlAndParameter parse = getSqlAndParams(db, this, params);
Statement sql = parse.statement;
ExecuteablePlan plan = SqlAnalyzer.getExecutionPlan(sql, parse.getParamsMap(), parse.params, db);
return plan.processUpdate(generateKeys);
}
/**
* 限制返回的最大结果数
*/
public void setMaxResults(int maxResult) {
this.maxResult = maxResult;
}
/**
* 得到查询所在的dbclient对象
*
* @return
*/
public JDBCTarget getDb() {
return db;
}
@Override
public String toString() {
return this.st.toString();
}
@Override
public void setQueryTimeout(int queryTimeout) {
}
// Batch的约束,每个语句必是单库单表查询
@Override
public BatchReturn executeBatch(GenerateKeyReturnOper generateKeys, List<List<ParameterContext>> params) throws SQLException {
TableMetaCollector collector = SqlAnalyzer.getTableMeta(st);
if (collector.get() == null) {// 无需路由
return processBatch(null, null, params, collector, generateKeys);
}
// 先按路由结果分组
AbstractMetadata meta = collector.get();
if (meta.getPartition() == null) {
if (meta.getBindDsName() != null) {
DbTable dbTable = meta.getBaseTable(db.getProfile());
return processBatch(dbTable.getDbName(), null, params, collector, generateKeys);
} else {
return processBatch(null, null, params, collector, generateKeys);
}
}
// 分库分表
Multimap<String, List<ParameterContext>> result = SqlAnalyzer.doGroup(meta, params, this.st, this.db);
BatchReturn ur = new BatchReturn();
for (String s : result.keySet()) {
int index = s.indexOf('-');
String db = s.substring(0, index);
String table = s.substring(index + 1);
BatchReturn u = processBatch(db, table, params, collector, generateKeys);
ur.merge(u.getBatchResult(), u.getGeneratedKeys());
}
return ur;
}
private BatchReturn processBatch(String database, String table, Collection<List<ParameterContext>> params, TableMetaCollector collector, GenerateKeyReturnOper oper) throws SQLException {
JDBCTarget db;
if (database != null && !database.equals(this.db.getDbkey())) {
db = this.db.getTarget(database);
} else {
db = this.db;
}
String sql = getSql(table, collector);
PreparedStatement st = null;
try {
st = oper.prepareStatement(db, sql);
for (List<ParameterContext> record : params) {
for (ParameterContext context : record) {
context.apply(st);
}
st.addBatch();
}
BatchReturn result = new BatchReturn(st.executeBatch());
oper.getGeneratedKey(result, st);
return result;
} finally {
DbUtils.close(st);
}
}
private String getSql(String table, TableMetaCollector collector) {
if (table == null)
return this.st.toString();
for (Table t : collector.getModificationPoints()) {
t.setReplace(table);
}
String s = this.st.toString();
for (Table t : collector.getModificationPoints()) {
t.removeReplace();
}
return s;
}
}