/**
* Alipay.com Inc.
* Copyright (c) 2004-2012 All Rights Reserved.
*/
package com.alipay.zdal.client.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import com.alipay.zdal.client.RouteCondition;
import com.alipay.zdal.client.ThreadLocalString;
import com.alipay.zdal.client.config.DataSourceConfigType;
import com.alipay.zdal.client.controller.RuleController;
import com.alipay.zdal.client.controller.TargetDBMeta;
import com.alipay.zdal.client.dispatcher.DispatcherResult;
import com.alipay.zdal.client.dispatcher.SqlDispatcher;
import com.alipay.zdal.client.jdbc.DBSelector.AbstractDataSourceTryer;
import com.alipay.zdal.client.jdbc.DBSelector.DataSourceTryer;
import com.alipay.zdal.client.jdbc.parameter.ParameterContext;
import com.alipay.zdal.client.jdbc.resultset.CountTResultSet;
import com.alipay.zdal.client.jdbc.resultset.DummyTResultSet;
import com.alipay.zdal.client.jdbc.resultset.EmptySimpleTResultSet;
import com.alipay.zdal.client.jdbc.resultset.MaxTResultSet;
import com.alipay.zdal.client.jdbc.resultset.MinTResultSet;
import com.alipay.zdal.client.jdbc.resultset.OrderByTResultSet;
import com.alipay.zdal.client.jdbc.resultset.SimpleTResultSet;
import com.alipay.zdal.client.jdbc.resultset.SumTResultSet;
import com.alipay.zdal.client.util.ExceptionUtils;
import com.alipay.zdal.client.util.ThreadLocalMap;
import com.alipay.zdal.common.Constants;
import com.alipay.zdal.common.SqlType;
import com.alipay.zdal.common.exception.checked.ZdalCheckedExcption;
import com.alipay.zdal.common.exception.sqlexceptionwrapper.ZdalCommunicationException;
import com.alipay.zdal.common.jdbc.sorter.ExceptionSorter;
import com.alipay.zdal.common.lang.StringUtil;
import com.alipay.zdal.parser.GroupFunctionType;
import com.alipay.zdal.parser.ParserCache;
import com.alipay.zdal.parser.result.DefaultSqlParserResult;
import com.alipay.zdal.parser.visitor.OrderByEle;
import com.alipay.zdal.rule.config.beans.AppRule;
import com.alipay.zdal.rule.ruleengine.entities.retvalue.TargetDB;
import com.alipay.zdal.rule.ruleengine.exception.RuleRuntimeExceptionWrapper;
import com.alipay.zdal.rule.ruleengine.exception.ZdalRuleCalculateException;
import com.alipay.zdal.rule.ruleengine.rule.EmptySetRuntimeException;
/**
*
* @author ����
* @version $Id: ZdalStatement.java, v 0.1 2014-1-6 ����01:19:26 Exp $
*/
public class ZdalStatement implements Statement {
//TODO: ���һ��ѡ��booleanֵ������statlog���м��
private static final Logger log = Logger
.getLogger(ZdalStatement.class);
private static final Logger sqlLog = Logger
.getLogger(Constants.CONFIG_LOG_NAME_LOGNAME);
/**
* �����ж��Ƿ���һ��select ... for update��sql
*/
private static final Pattern SELECT_FOR_UPDATE_PATTERN = Pattern
.compile(
"^select\\s+.*\\s+for\\s+update.*$",
Pattern.CASE_INSENSITIVE);
/**��DB2��ϵͳ���л�ȡsequence���¼���sql. */
private static final Pattern SELECT_FROM_SYSTEMIBM = Pattern
.compile(
"^select\\s+.*\\s+from\\s+sysibm.*$",
Pattern.CASE_INSENSITIVE);
private static final Pattern SELECT_FROM_DUAL_PATTERN = Pattern
.compile(
"^select\\s+.*\\s+from\\s+dual.*$",
Pattern.CASE_INSENSITIVE);
/**
* Ĭ�ϵ�ÿ����ִ��sql�ij�ʱʱ��
*/
public static final long DEFAULT_TIMEOUT_FOR_EACH_TABLE = 100;
private static final ParserCache globalCache = ParserCache.instance();
protected Map<String, DBSelector> dbSelectors;
protected DBSelector groupDBSelector = null;
protected RuleController ruleController;
protected final SqlDispatcher writeDispatcher;
protected final SqlDispatcher readDispatcher;
//��¼��ǰ�IJ�����д��������Ƕ������
protected DB_OPERATION_TYPE operation_type;
public enum DB_OPERATION_TYPE {
WRITE_INTO_DB, READ_FROM_DB;
}
private ZdalConnection connectionProxy;
protected List<Statement> actualStatements = new ArrayList<Statement>();
protected ResultSet results;
protected boolean moreResults;
protected int updateCount;
protected boolean closed;
/*
* �Ƿ��滻hint�е���������Ĭ���Dz��滻
*/
private boolean isHintReplaceSupport = false;
/**
* ���Դ������ⲿָ��
*/
protected int retryingTimes;
/**
* fetchsize Ĭ��Ϊ10
*/
private int fetchSize = 10;
private int resultSetType = -1;
private int resultSetConcurrency = -1;
private int resultSetHoldability = -1;
protected boolean autoCommit = true;
/**
* �����batch������dbId
*/
private String batchDataBaseId = null;
private boolean readOnly;
/**
* ��ԭ��ResultSet�ӿ��·ŵ�Dummy������������֧���Զ��巽��
*/
protected Set<ResultSet> openResultSets = new HashSet<ResultSet>();
protected List<Object> batchedArgs;
private long timeoutForEachTable = DEFAULT_TIMEOUT_FOR_EACH_TABLE;
protected DataSourceConfigType dbConfigType = null;
private int autoGeneratedKeys;
private int[] columnIndexes;
private String[] columnNames;
/**����Դ������. */
protected String appDsName = null;
protected static void dumpSql(String originalSql, Map<String, SqlAndTable[]> targets) {
dumpSql(originalSql, targets, null);
}
public ZdalStatement(SqlDispatcher writeDispatcher, SqlDispatcher readDispatcher) {
this.writeDispatcher = writeDispatcher;
this.readDispatcher = readDispatcher;
}
protected static void dumpSql(String originalSql, Map<String, SqlAndTable[]> targets,
Map<Integer, ParameterContext> parameters) {
if (sqlLog.isDebugEnabled()) {
StringBuilder buffer = new StringBuilder();
buffer.append("\n[original sql]:").append(originalSql.trim()).append("\n");
for (Entry<String, SqlAndTable[]> entry : targets.entrySet()) {
for (SqlAndTable targetSql : entry.getValue()) {
buffer.append(" [").append(entry.getKey()).append(".").append(targetSql.table)
.append("]:").append(targetSql.sql.trim()).append("\n");
}
}
if (parameters != null && !parameters.isEmpty() && !parameters.values().isEmpty()) {
buffer.append("[parameters]:").append(parameters.values().toString());
}
sqlLog.debug(buffer.toString());
}
}
/**
* ���SQL�������
*
* @param sql SQL���
* @throws SQLException ��SQL��䲻��SELECT��INSERT��UPDATE��DELETE���ʱ���׳��쳣��
*/
protected static SqlType getSqlType(String sql) throws SQLException {
SqlType sqlType = globalCache.getSqlType(sql);
if (sqlType == null) {
String noCommentsSql = StringUtil.stripComments(sql, "'\"", "'\"", true, false, true,
true).trim();
if (StringUtil.startsWithIgnoreCaseAndWs(noCommentsSql, "select")) {
if (SELECT_FROM_DUAL_PATTERN.matcher(noCommentsSql).matches()) {
sqlType = SqlType.SELECT_FROM_DUAL;
} else if (SELECT_FOR_UPDATE_PATTERN.matcher(noCommentsSql).matches()) {
sqlType = SqlType.SELECT_FOR_UPDATE;
} else if (SELECT_FROM_SYSTEMIBM.matcher(noCommentsSql).matches()) {
sqlType = SqlType.SELECT_FROM_SYSTEMIBM;
} else {
sqlType = SqlType.SELECT;
}
} else if (StringUtil.startsWithIgnoreCaseAndWs(noCommentsSql, "insert")) {
sqlType = SqlType.INSERT;
} else if (StringUtil.startsWithIgnoreCaseAndWs(noCommentsSql, "update")) {
sqlType = SqlType.UPDATE;
} else if (StringUtil.startsWithIgnoreCaseAndWs(noCommentsSql, "delete")) {
sqlType = SqlType.DELETE;
} else {
throw new SQLException("only select, insert, update, delete sql is supported");
}
sqlType = globalCache.setSqlTypeIfAbsent(sql, sqlType);
}
return sqlType;
}
/**
* �滻SQL������������Ϊʵ�ʱ����� �� �滻_tableName$ �滻_tableName_ �滻tableName.
* �滻tableName(
* �����滻 _tableName, ,tableName, ,tableName_
*
* @param originalSql
* SQL���
* @param virtualName
* �������
* @param actualName
* ʵ�ʱ���
* @return �����滻���SQL��䡣
*/
protected String replaceTableName(String originalSql, String virtualName, String actualName) {
if (log.isDebugEnabled()) {
StringBuilder buffer = new StringBuilder();
buffer.append("virtualName = ").append(virtualName).append(", ");
buffer.append("actualName = ").append(actualName);
log.debug(buffer.toString());
}
if (virtualName.equalsIgnoreCase(actualName)) {
return originalSql;
}
//add by boya for testcase for schemaname.tablename to ignore replaceTablename.
if (virtualName.contains(actualName)) {
return originalSql;
}
List<String> sqlPieces = globalCache.getTableNameReplacement(originalSql);
if (sqlPieces == null) {
//�滻 tableName$
Pattern pattern1 = Pattern.compile(new StringBuilder("\\s").append(virtualName).append(
"$").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces1 = new ArrayList<String>();
Matcher matcher1 = pattern1.matcher(originalSql);
int start1 = 0;
while (matcher1.find(start1)) {
pieces1.add(originalSql.substring(start1, matcher1.start() + 1));
start1 = matcher1.end();
}
pieces1.add(originalSql.substring(start1));
//�滻 tableName
Pattern pattern2 = Pattern.compile(new StringBuilder("\\s").append(virtualName).append(
"\\s").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces2 = new ArrayList<String>();
for (String piece : pieces1) {
Matcher matcher2 = pattern2.matcher(piece);
int start2 = 0;
while (matcher2.find(start2)) {
pieces2.add(piece.substring(start2 - 1 < 0 ? 0 : start2 - 1,
matcher2.start() + 1));
start2 = matcher2.end();
}
pieces2.add(piece.substring(start2 - 1 < 0 ? 0 : start2 - 1));
}
//�滻 tableName.
Pattern pattern3 = Pattern.compile(new StringBuilder().append(virtualName)
.append("\\.").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces3 = new ArrayList<String>();
for (String piece : pieces2) {
Matcher matcher3 = pattern3.matcher(piece);
int start3 = 0;
while (matcher3.find(start3)) {
pieces3.add(piece.substring(start3 - 1 < 0 ? 0 : start3 - 1, matcher3.start()));
start3 = matcher3.end();
}
pieces3.add(piece.substring(start3 - 1 < 0 ? 0 : start3 - 1));
}
//�滻 tablename(
Pattern pattern4 = Pattern.compile(new StringBuilder("\\s").append(virtualName).append(
"\\(").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces4 = new ArrayList<String>();
for (String piece : pieces3) {
Matcher matcher4 = pattern4.matcher(piece);
int start4 = 0;
while (matcher4.find(start4)) {
pieces4.add(piece.substring(start4 - 1 < 0 ? 0 : start4 - 1,
matcher4.start() + 1));
start4 = matcher4.end();
}
pieces4.add(piece.substring(start4 - 1 < 0 ? 0 : start4 - 1));
}
//�滻_tableName,
Pattern pattern5 = Pattern.compile(new StringBuilder("\\s").append(virtualName).append(
"\\,").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces5 = new ArrayList<String>();
for (String piece : pieces4) {
Matcher matcher5 = pattern5.matcher(piece);
int start5 = 0;
while (matcher5.find(start5)) {
pieces5.add(piece.substring(start5 - 1 < 0 ? 0 : start5 - 1,
matcher5.start() + 1));
start5 = matcher5.end();
}
pieces5.add(piece.substring(start5 - 1 < 0 ? 0 : start5 - 1));
}
//�滻,tableName
Pattern pattern6 = Pattern.compile(new StringBuilder("\\,").append(virtualName).append(
"\\s").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces6 = new ArrayList<String>();
for (String piece : pieces5) {
Matcher matcher6 = pattern6.matcher(piece);
int start6 = 0;
while (matcher6.find(start6)) {
pieces6.add(piece.substring(start6 - 1 < 0 ? 0 : start6 - 1,
matcher6.start() + 1));
start6 = matcher6.end();
}
pieces6.add(piece.substring(start6 - 1 < 0 ? 0 : start6 - 1));
}
//�滻 ,tableName,
Pattern pattern7 = Pattern.compile(new StringBuilder("\\,").append(virtualName).append(
"\\,").toString(), Pattern.CASE_INSENSITIVE);
List<String> pieces7 = new ArrayList<String>();
for (String piece : pieces6) {
Matcher matcher7 = pattern7.matcher(piece);
int start7 = 0;
while (matcher7.find(start7)) {
pieces7.add(piece.substring(start7 - 1 < 0 ? 0 : start7 - 1,
matcher7.start() + 1));
start7 = matcher7.end();
}
pieces7.add(piece.substring(start7 - 1 < 0 ? 0 : start7 - 1));
}
sqlPieces = globalCache.setTableNameReplacementIfAbsent(originalSql, pieces7);
}
// ��������SQL
StringBuilder buffer = new StringBuilder();
boolean first = true;
for (String piece : sqlPieces) {
if (!first) {
buffer.append(actualName);
} else {
first = false;
}
buffer.append(piece);
}
String sql_replace = buffer.toString();
/*
* added by fanzeng
* ֧����Ĭ�ϲ��滻HINT��ı����������Ҫ�滻��������������ļ���ָ��
* <property name="isHintReplaceSupport" value="true"/>
* */
if (log.isDebugEnabled()) {
log.debug("�Ƿ�֧���滻hint����������isHintSupport = " + this.isHintReplaceSupport);
}
//�滻 hint����ʽ���ٽ�������
if (isHintReplaceSupport) {
Pattern pattern8 = Pattern.compile(new StringBuilder("/\\s?\\*\\s?.*").append(
virtualName).append(".*\\s?\\*\\s?/").toString(), Pattern.CASE_INSENSITIVE);
String sql_pieces[] = new String[2];
String hint = "";
Matcher matcher8 = pattern8.matcher(sql_replace);
int start8 = 0;
if (matcher8.find(start8)) {
sql_pieces[0] = sql_replace.substring(start8 - 1 < 0 ? 0 : start8 - 1, matcher8
.start());
sql_pieces[1] = sql_replace.substring(matcher8.end());
hint = sql_replace.substring(matcher8.start(), matcher8.end()).toUpperCase();
hint = hint.replace(virtualName.toUpperCase(), actualName.toUpperCase());
sql_replace = sql_pieces[0] + hint + sql_pieces[1];
}
}
if (log.isDebugEnabled()) {
log.debug("�滻�������sqlΪ��" + sql_replace);
}
return sql_replace;
}
protected SqlExecutionContext getExecutionContext(String originalSql, List<Object> parameters)
throws SQLException {
SqlExecutionContext executionContext = null;
try {
executionContext = getExecutionContext1(originalSql, parameters); //�¹���
} catch (RuleRuntimeExceptionWrapper e) {
//��ΪRUleRuntimeExceptionҲ�Ǹ�RuntimeException,�������ں���runtimeExceptionǰ��
SQLException sqlException = e.getSqlException();
if (sqlException instanceof ZdalCommunicationException) {
//���ظ��Ľ��а�װ������쳣��zdal��ѯ�߷ֿ�ʱ���ֿ����Դ�����������ʱ���Լ����׳��ġ�ҵ����Ҫ����쳣
throw e;
} else {
//���ڷ�zdal��Ϊ����������mapping rule �Ͳ����ݿ��ѯ�ij�����Ҫ��sqlException���а�װ���׳�
throw new ZdalCommunicationException("rule sql exceptoin.", sqlException);
}
} catch (ZdalRuleCalculateException e) {
log.error("��������������sql=" + originalSql, e);
throw e;
} catch (RuntimeException e) {
String context = ExceptionUtils.getErrorContext(originalSql, parameters,
"An error occerred on routing or getExecutionContext,sql is :");
//log.error(context, e);
throw new RuntimeException(context, e);
}
return executionContext;
}
/**
* @param dbSelectorID
* @param retringContext
* @throws SQLException
*/
public void createConnectionByID(String dbSelectorID) throws SQLException {
DBSelector dbSelector = this.dbSelectors.get(dbSelectorID);
// retringContext.setDbSelector(dbSelector);
createConnection(dbSelector, dbSelectorID);
}
/**
* ��ȡ�µ�Connection������Ӧ��Datasource
*
* datasource��Ҫ��������������Ե�ʱ���ų��Ѿ��ҵ�������Դ
*
* ���ṩ����
* @param ds
* @return
* @throws SQLException
*/
private ConnectionAndDatasource getNewConnectionAndDataSource(DataSource ds,
DBSelector dbSelector)
throws SQLException {
ConnectionAndDatasource connectionAndDatasource = new ConnectionAndDatasource();
connectionAndDatasource.parentDataSource = ds;
connectionAndDatasource.dbSelector = dbSelector;
long begin = System.currentTimeMillis();
Connection conn = ds.getConnection();
conn.setAutoCommit(autoCommit);
long elapsed = System.currentTimeMillis() - begin;
if (log.isDebugEnabled()) {
log.debug("get the connection, elapsed time=" + elapsed + ",thread="
+ Thread.currentThread().getName());
}
connectionAndDatasource.connection = conn;
return connectionAndDatasource;
}
protected SqlDispatcher selectSqlDispatcher(boolean autoCommit, SqlType sqlType)
throws SQLException {
SqlDispatcher sqlDispatcher;
if (sqlType != SqlType.SELECT) {
if (this.writeDispatcher == null) {
throw new SQLException("�ֿⲻ֧��д�롣�������û�SQL");
}
sqlDispatcher = this.writeDispatcher;
} else {
if (autoCommit) {
String rc = (String) ThreadLocalMap.get(ThreadLocalString.SELECT_DATABASE);
if (rc != null) {
if (log.isDebugEnabled()) {
log.debug("rc=" + rc);
}
sqlDispatcher = this.writeDispatcher;
} else {
sqlDispatcher = this.readDispatcher != null ? this.readDispatcher
: this.writeDispatcher;
}
} else {
sqlDispatcher = this.writeDispatcher;
if (sqlDispatcher == null) {
throw new SQLException("�ֿⲻ֧��д�롣�벻Ҫʹ������");
}
}
}
if (sqlDispatcher == null) {
throw new SQLException("û�зֿ�����sqlDispatcherΪnull���������û�SQL");
}
if (sqlDispatcher == this.writeDispatcher) {
this.setOperation_type(DB_OPERATION_TYPE.WRITE_INTO_DB);
} else if (sqlDispatcher == this.readDispatcher) {
this.setOperation_type(DB_OPERATION_TYPE.READ_FROM_DB);
} else {
throw new SQLException("�������ͷ����쳣������������Χ��");
}
return sqlDispatcher;
}
/**
* 1. ֻ֧�ֵ��������, ������ͬʱĿ���Ϊ���ʱ����
* 2. ֻ֧�ֵ��������, �������ҵ�ǰ�������Ѿ������ķֿ�ͱ��ν�����Ŀ��ⲻ��ͬһ����ʱ����
*/
protected SqlExecutionContext getExecutionContext1(String originalSql, List<Object> parameters)
throws SQLException {
SqlExecutionContext context = new SqlExecutionContext();
SqlType sqlType = getSqlType(originalSql);
RouteCondition rc = (RouteCondition) ThreadLocalMap.get(ThreadLocalString.ROUTE_CONDITION);
ThreadLocalMap.put(ThreadLocalString.ROUTE_CONDITION, null);
DispatcherResult metaData = null;
List<TargetDB> targets = null;
SqlDispatcher sqlDispatcher = selectSqlDispatcher(autoCommit, sqlType);
/*
* �鿴sqlDispatcher�Ƿ�ΪwriteDispatcher.
*
* writeDispatcher��Ҫ����insert ,update ,select for update,������select��
* 4�������������Ҫ�����ԡ�
*
* ��Ϊ����������֣���һ�����ڴ��ж���״̬��һ��������Ӧ����readDispatcher��
* �������dispatcher��������read��writeDispatcher��������������
* ��selectSqlDispatcher������֤��
*
* ����һ����writeDispatcherΪnull ��ʱ�� ������Ϊ�������
* dispatcherֻ����Ϊ read��write������Ҳ���Ա�֤��ȷ�Ľ����
*/
boolean ruleReturnNullValue = false;
/**
* ������autoCommit����ֵ�������ڸ�������ԴkeyȨ���������ѡ������dbʱ���ж��Dz�����������
* ����������У��ͽ��˴μ����ֵ����������Ȼ��������е�����sqlִ������㷨ʱ��ֱ�ӽ���ֵ����
* �Դﵽ��һ�������н�ֹ����������п���ѡ��ͬ��db
*/
// ThreadLocalMap.put(ThreadLocalString.GET_AUTOCOMMIT_PROPERTY, autoCommit);
try {
metaData = getExecutionMetaData(originalSql, parameters, rc, sqlDispatcher);
targets = metaData.getTarget();
} catch (EmptySetRuntimeException e) {
ruleReturnNullValue = true;
}
if (targets == null || targets.isEmpty()) {
if (!ruleReturnNullValue) {
throw new SQLException("�Ҳ���Ŀ��⣬��������,the originalSql = " + originalSql
+ " ,the parameters = " + parameters);
} else {
//�����mapping rule���ڼ������������null��ֱ�ӷ���emptyResultSet
context.setRuleReturnNullValue(ruleReturnNullValue);
}
} else {
buildExecutionContext(originalSql, context, sqlType, metaData, targets);
}
return context;
}
@SuppressWarnings("unchecked")
protected final ResultSet getEmptyResultSet() {
return new EmptySimpleTResultSet(this, Collections.EMPTY_LIST);
}
private void buildExecutionContext(String originalSql, SqlExecutionContext context,
SqlType sqlType, DispatcherResult metaData,
List<TargetDB> targets) throws SQLException {
// ֻ֧�ֵ��������
if (!autoCommit && targets.size() != 1 && sqlType != SqlType.SELECT) {
throw new SQLException("ֻ֧�ֵ��������target.size=" + targets.size());
}
// ��������
setTransaction(targets, originalSql);
for (TargetDB target : targets) {
String dbIndex = target.getDbIndex();
Set<String> actualTables = target.getTableNames();
if (dbIndex == null || dbIndex.length() < 1) {
throw new SQLException("invalid dbIndex:" + dbIndex);
}
if (actualTables == null || actualTables.isEmpty()) {
throw new SQLException("�Ҳ���Ŀ���");
}
// ��������
// ���ﴦ���ȡ���������ӵ�����
DBSelector dbselector = dbSelectors.get(dbIndex);
if (dbselector == null) {
throw new IllegalStateException("û��ΪdbIndex[" + dbIndex + "]��������Դ");
}
createConnection(dbselector, dbIndex); //������������Ժ�ɹ�����������ʱû���ų��������Թ���ds
if (sqlType == SqlType.INSERT) {
if (actualTables.size() != 1) {
if (actualTables.isEmpty()) {
throw new SQLException(
"Zdal need at least one table, but There is none selected ");
}
throw new SQLException("mapping many actual tables");
}
}
if (!autoCommit && !dbIndex.equals(getConnectionProxy().getTxTarget())
&& sqlType != SqlType.SELECT) {
throw new SQLException("zdalֻ֧�ֵ��������dbIndex=" + dbIndex + ",txTarget="
+ getConnectionProxy().getTxTarget() + ",originalSql="
+ originalSql);
}
SqlAndTable[] targetSqls = new SqlAndTable[actualTables.size()];
if (!metaData.allowReverseOutput()) {
int i = 0;
for (String tab : actualTables) {
SqlAndTable sqlAndTable = new SqlAndTable();
targetSqls[i] = sqlAndTable;
sqlAndTable.sql = replaceTableName(originalSql, metaData.getVirtualTableName(),
tab);
//���metaData(Ҳ����DispatcherResult)������join��������ô���滻��;
sqlAndTable.sql = replaceJoinTableName(metaData.getVirtualTableName(), metaData
.getVirtualJoinTableNames(), tab, sqlAndTable.sql);
sqlAndTable.table = tab;
i++;
}
} else {
int i = 0;
for (String tab : actualTables) {
SqlAndTable targetSql = new SqlAndTable();
targetSql.sql = replaceTableName(originalSql, metaData.getVirtualTableName(),
tab);
targetSql.table = tab;
targetSqls[i] = targetSql;
i++;
}
// ��Ϊ����SQL������һ��������ֻҪȡ��һ����
context.setChangedParameters(target.getChangedParams());
}
context.getTargetSqls().put(dbIndex, targetSqls);
}
context.setOrderByColumns(metaData.getOrderByMessages().getOrderbyList());
context.setSkip(metaData.getSkip() == DefaultSqlParserResult.DEFAULT_SKIP_MAX ? 0
: metaData.getSkip());
context.setMax(metaData.getMax() == DefaultSqlParserResult.DEFAULT_SKIP_MAX ? -1 : metaData
.getMax());
context.setGroupFunctionType(metaData.getGroupFunctionType());
context.setVirtualTableName(metaData.getVirtualTableName());
//������Ҫע���
// boolean needRetry = SqlType.SELECT.equals(sqlType) && this.autoCommit;
boolean needRetry = this.autoCommit;
//boolean isMySQL = sqlDispatcher.getDBType() == DBType.MYSQL?true:false;
//RetringContext retringContext = new RetringContext(isMySQL);
//retringContext.setNeedRetry(needRetry);
//context.setRetringContext(retringContext);
Map<DataSource, SQLException> failedDataSources = needRetry ? new LinkedHashMap<DataSource, SQLException>(
0)
: null;
context.setFailedDataSources(failedDataSources);
}
/**
* @param tab ʵ�ʱ���
* @param vtab �������
* @return
*/
private String getSuffix(String tab, String vtab) {
String result = tab.substring(vtab.length());
return result;
}
/**
* ��ȡsqlִ����Ϣ
* @param originalSql
* @param parameters
* @param rc
* @param metaData
* @param sqlDispatcher
* @return
* @throws SQLException
*/
protected DispatcherResult getExecutionMetaData(String originalSql, List<Object> parameters,
RouteCondition rc, SqlDispatcher sqlDispatcher)
throws SQLException {
DispatcherResult metaData;
if (rc != null) {
//���߽���SQL����ThreadLocal�����ָ������RouteCondition�����������Ŀ�ĵ�
metaData = sqlDispatcher.getDBAndTables(rc);
} else {
// ͨ������SQL���ֿ�ֱ�
try {
metaData = sqlDispatcher.getDBAndTables(originalSql, parameters);
} catch (ZdalCheckedExcption e) {
throw new SQLException(e.getMessage());
}
}
return metaData;
}
/**
* ��������
* @param targets
*/
private void setTransaction(List<TargetDB> targets, String originalSql) {
if (!autoCommit && getConnectionProxy().getTxStart()) {
getConnectionProxy().setTxStart(false);
//getConnectionProxy().setTxTarget(targets.get(0).getWritePool()[0]);
getConnectionProxy().setTxTarget(targets.get(0).getDbIndex());
if (log.isDebugEnabled()) {
log.debug("�����������ݿ��ʶ:Set the txStart property to false, and the dbIndex="
+ targets.get(0).getDbIndex() + ",originalSql=" + originalSql);
}
}
}
private final DataSourceTryer<ConnectionAndDatasource> createConnectionTryer = new AbstractDataSourceTryer<ConnectionAndDatasource>() {
public ConnectionAndDatasource tryOnDataSource(
DataSource ds,
String name,
Object... args)
throws SQLException {
DBSelector dbSelector = (DBSelector) args[0];
dbSelector
.setSelectedDSName(name);
return getNewConnectionAndDataSource(
ds,
dbSelector);
}
@Override
public ConnectionAndDatasource onSQLException(
java.util.List<SQLException> exceptions,
ExceptionSorter exceptionSorter,
Object... args)
throws SQLException {
int size = exceptions
.size();
if (size <= 0) {
throw new IllegalArgumentException(
"should not be here");
} else {
//��������µĴ���
int lastElementIndex = size - 1;
//ȡ���һ��exception.�ж��Ƿ������ݿⲻ�����쳣.����ǣ�ȥ�����һ���쳣������ͷ�쳣��װΪZdalCommunicationException�׳�
SQLException lastSQLException = exceptions
.get(lastElementIndex);
if (exceptionSorter
.isExceptionFatal(lastSQLException)) {
exceptions
.remove(lastElementIndex);
exceptions
.add(
0,
new ZdalCommunicationException(
"zdal communicationException ",
lastSQLException));
}
}
return super
.onSQLException(
exceptions,
exceptionSorter,
args);
};
};
/**
* ����������� ����������autoCommitȻ�����͡�
*
* ������������ӣ����Datasource����ѡ��һ��Datasource��������
*
* Ȼ�����ӷ���parentConnection�Ŀ���������map��(getConnectionProxy.getAuctualConnections)
* @param dbIndex
* @return
* @throws SQLException
*/
protected void createConnection(DBSelector dbSelector, String dbIndex) throws SQLException {
this.createConnection(dbSelector, dbIndex, new LinkedHashMap<DataSource, SQLException>(0));
}
protected void createConnection(DBSelector dbSelector, String dbIndex,
Map<DataSource, SQLException> failedDataSources)
throws SQLException {
// Map<String, ConnectionAndDatasource> connections = getConnectionProxy().getActualConnections();
ConnectionAndDatasource connectionAndDatasource = getConnectionProxy()
.getConnectionAndDatasourceByDBSelectorID(dbIndex);
//DataSource datasource = null;
//datasource = dbSelector.select();
if (connectionAndDatasource == null) {
if (dbIndex == null) {
throw new SQLException(new StringBuilder("data source ").append(dbIndex).append(
" does not exist").toString());
}
//û��connection
//try {
//connectionAndDatasource = getNewConnectionAndDataSource(datasource,dbSelector);
connectionAndDatasource = dbSelector.tryExecute(failedDataSources,
createConnectionTryer, retryingTimes, operation_type, dbSelector);
getConnectionProxy().put(dbIndex, connectionAndDatasource);
//} catch (NullPointerException e) {
// throw new SQLException(new StringBuilder("data source ").append(dbIndex).append(" does not exist")
// .toString());
//}catch (SQLException e) {
// throw new RetrySQLException(e, datasource);
//}
} else {
//������connection
//datasource = connectionAndDatasource.parentDataSource;
try {
connectionAndDatasource.connection.setAutoCommit(autoCommit);
} catch (SQLException e) {
//throw new RetrySQLException(e, datasource);
failedDataSources.put(connectionAndDatasource.parentDataSource, e);
getConnectionProxy().removeConnectionAndDatasourceByID(dbIndex);
createConnection(dbSelector, dbIndex, failedDataSources);
}
}
}
/**
* �õ�ǰ���ӽ���statementd
*
* @param connection ��ǰ�����õ�connection,�������Դ�map��ȡ����ΪЧ���ϵĿ������Ի��Dz�������
* @param connections
* @param dbIndex
* @param retringContext
* @return
* @throws SQLException
*/
protected Statement createStatementInternal(Connection connection, String dbIndex,
Map<DataSource, SQLException> failedDataSources)
throws SQLException {
Statement stmt;
if (this.resultSetType != -1 && this.resultSetConcurrency != -1
&& this.resultSetHoldability != -1) {
stmt = connection.createStatement(this.resultSetType, this.resultSetConcurrency,
this.resultSetHoldability);
} else if (this.resultSetType != -1 && this.resultSetConcurrency != -1) {
stmt = connection.createStatement(this.resultSetType, this.resultSetConcurrency);
} else {
stmt = connection.createStatement();
}
return stmt;
}
public Connection getConnection() throws SQLException {
return connectionProxy;
}
private boolean executeInternal(String sql, int autoGeneratedKeys, int[] columnIndexes,
String[] columnNames) throws SQLException {
SqlType sqlType = getSqlType(sql);
if (sqlType == SqlType.SELECT || sqlType == SqlType.SELECT_FROM_DUAL
|| sqlType == SqlType.SELECT_FOR_UPDATE) {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
executeQuery0(sql, sqlType);
} else {
executeQuery(sql);
}
return true;
} else if (sqlType == SqlType.INSERT || sqlType == SqlType.UPDATE
|| sqlType == SqlType.DELETE) {
if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
executeUpdate(sql);
} else if (autoGeneratedKeys != -1) {
executeUpdate(sql, autoGeneratedKeys);
} else if (columnIndexes != null) {
executeUpdate(sql, columnIndexes);
} else if (columnNames != null) {
executeUpdate(sql, columnNames);
} else {
executeUpdate(sql);
}
return false;
} else {
throw new SQLException("only select, insert, update, delete sql is supported");
}
}
public boolean execute(String sql) throws SQLException {
return executeInternal(sql, -1, null, null);
}
public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
return executeInternal(sql, autoGeneratedKeys, null, null);
}
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return executeInternal(sql, -1, columnIndexes, null);
}
public boolean execute(String sql, String[] columnNames) throws SQLException {
return executeInternal(sql, -1, null, columnNames);
}
private ResultSet executeQuery0(String sql, SqlType sqlType) throws SQLException {
checkClosed();
this.setOperation_type(DB_OPERATION_TYPE.READ_FROM_DB);
//��ȡ����
DBSelector dbselector = getGroupDBSelector(sqlType);
if (dbselector == null) {
throw new IllegalStateException("load balance����Դ�������ʹ���");
}
//����ִ�н��
return dbselector.tryExecute(new LinkedHashMap<DataSource, SQLException>(0),
this.executeQueryTryer, retryingTimes, operation_type, sql, sqlType);
}
protected DataSourceTryer<ResultSet> executeQueryTryer = new AbstractDataSourceTryer<ResultSet>() {
public ResultSet tryOnDataSource(
DataSource ds,
String name,
Object... args)
throws SQLException {
String sql = (String) args[0];
SqlType sqlType = (SqlType) args[1];
//��ȡ����
Connection conn = ZdalStatement.this
.getGroupConnection(ds,
sqlType, name);
return ZdalStatement.this
.executeQueryOnConnection(
conn, sql);
}
};
private ResultSet executeQueryOnConnection(Connection conn, String sql) throws SQLException {
Statement stmt = createStatementInternal(conn, null, null);
actualStatements.add(stmt);
List<ResultSet> actualResultSets = new ArrayList<ResultSet>();
actualResultSets.add(stmt.executeQuery(sql));
DummyTResultSet currentResultSet = new SimpleTResultSet(this, actualResultSets);
openResultSets.add(currentResultSet);
this.results = currentResultSet;
this.moreResults = false;
this.updateCount = -1;
return this.results;
}
/**
* �����������Ӳ��գ�ֱ�ӷ��ظ����ӣ�����������
* @param ds
* @return
* @throws SQLException
*/
public Connection getGroupConnection(DataSource ds, SqlType sqlType, String name)
throws SQLException {
Connection conn = null;
//�����������ж��selectʱ���������ͷţ����Թ���ͬһ��select������.
if (this.getConnectionProxy().get(name) != null) {
conn = this.getConnectionProxy().get(name).connection;
} else {
ConnectionAndDatasource connectionAndDatasource = new ConnectionAndDatasource();
connectionAndDatasource.parentDataSource = ds;
connectionAndDatasource.dbSelector = null;
conn = ds.getConnection();
connectionAndDatasource.connection = conn;
this.getConnectionProxy().put(name, connectionAndDatasource);
}
conn.setAutoCommit(autoCommit);
return conn;
}
/**
* ��dbSelector�DZ�loadbalance�����õ���
* @return
*/
public DBSelector getGroupDBSelector(SqlType sqlType) {
DBSelector rGroupDBSelector = null, wGroupDBSelector = null;
for (Map.Entry<String, DBSelector> dbSelector : dbSelectors.entrySet()) {
if (dbSelector.getKey().endsWith(AppRule.DBINDEX_SUFFIX_READ)
&& dbSelector.getValue() != null) {
rGroupDBSelector = dbSelector.getValue();
} else if (dbSelector.getKey().endsWith(AppRule.DBINDEX_SUFFIX_WRITE)
&& dbSelector.getValue() != null) {
wGroupDBSelector = dbSelector.getValue();
} else {
throw new IllegalArgumentException("The dbSelector set error!");
}
}
//�����������ֱ�ӵ�д��
if (sqlType != SqlType.SELECT) {
return wGroupDBSelector;
} else if (!autoCommit) {
return wGroupDBSelector;
} else {
return rGroupDBSelector;
}
}
/**
* ��dbSelector�DZ�loadbalance�����õ���
* @return
*/
public String getGroupDBSelectorID(SqlType sqlType) {
String rGroupDBSelectorID = null, wGroupDBSelectorID = null;
for (Map.Entry<String, DBSelector> dbSelector : dbSelectors.entrySet()) {
if (dbSelector.getKey().endsWith(AppRule.DBINDEX_SUFFIX_READ)
&& dbSelector.getValue() != null) {
rGroupDBSelectorID = dbSelector.getKey();
} else if (dbSelector.getKey().endsWith(AppRule.DBINDEX_SUFFIX_WRITE)
&& dbSelector.getValue() != null) {
wGroupDBSelectorID = dbSelector.getKey();
} else {
throw new IllegalArgumentException("The dbSelector set error!");
}
}
if (sqlType != SqlType.SELECT) {
return wGroupDBSelectorID;
} else {
return rGroupDBSelectorID;
}
}
public ResultSet executeQuery(String sql) throws SQLException {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
SqlType sqlType = getSqlType(sql);
return this.executeQuery0(sql, sqlType);
}
checkClosed();
SqlExecutionContext context = getExecutionContext(sql, null);
/*
* modified by shenxun:
* ������Ҫ�Ǵ���mappingRule���ؿյ�����£�Ӧ�÷��ؿս����
*/
if (context.mappingRuleReturnNullValue()) {
ResultSet emptyResultSet = getEmptyResultSet();
this.results = emptyResultSet;
return emptyResultSet;
}
// int tablesSize = 0;
dumpSql(sql, context.getTargetSqls());
List<SQLException> exceptions = null;
List<ResultSet> actualResultSets = new ArrayList<ResultSet>();
// int databaseSize = context.getTargetSqls().size();
for (Entry<String/*dbSelectorID*/, SqlAndTable[]> entry : context.getTargetSqls()
.entrySet()) {
for (SqlAndTable targetSql : entry.getValue()) {
// tablesSize++;
try {
//RetringContext retringContext = context.getRetringContext();
String dbSelectorId = entry.getKey();
Statement stmt = createStatementByDataSourceSelectorID(dbSelectorId, context
.getFailedDataSources());
//��������
actualStatements.add(stmt);
queryAndAddResultToCollection(dbSelectorId, actualResultSets, targetSql, stmt,
context.getFailedDataSources());
} catch (SQLException e) {
//�쳣����
if (exceptions == null) {
exceptions = new ArrayList<SQLException>();
}
exceptions.add(e);
ExceptionUtils.throwSQLException(exceptions, sql, Collections.emptyList()); //ֱ���׳�
}
}
}
ExceptionUtils.throwSQLException(exceptions, sql, Collections.emptyList());
DummyTResultSet resultSet = mergeResultSets(context, actualResultSets);
openResultSets.add(resultSet);
this.results = resultSet;
this.moreResults = false;
this.updateCount = -1;
return this.results;
}
protected void queryAndAddResultToCollection(String dbSelectorId,
List<ResultSet> actualResultSets,
SqlAndTable targetSql, Statement stmt,
Map<DataSource, SQLException> failedDataSources)
throws SQLException {
//added by fanzeng.
//����dbSelectorId��ȡ��Ӧ������Դ�ı�ʶ���Լ�����Դ��Ȼ��ŵ�threadlocal��
try {
actualResultSets.add(stmt.executeQuery(targetSql.sql));
} finally {
Map<String, DataSource> map = getActualIdAndDataSource(dbSelectorId);
ThreadLocalMap.put(ThreadLocalString.GET_ID_AND_DATABASE, map);
}
}
protected Connection getActualConnection(String key) {
ConnectionAndDatasource connectionAndDatasource = getConnectionProxy()
.getConnectionAndDatasourceByDBSelectorID(key);
Connection conn = connectionAndDatasource.connection;
return conn;
}
// ��ȡ������ ����Դ�ı�ʶ�Լ�����Դ
protected Map<String, DataSource> getActualIdAndDataSource(String key) {
ConnectionAndDatasource connectionAndDatasource = getConnectionProxy()
.getConnectionAndDatasourceByDBSelectorID(key);
Map<String, DataSource> map = new HashMap<String, DataSource>();
if (connectionAndDatasource != null) {
DBSelector dbSelector = connectionAndDatasource.dbSelector;
DataSource ds = connectionAndDatasource.parentDataSource;
if (dbSelector == null || ds == null) {
throw new IllegalArgumentException("����Դ����Ϊ�գ����飡");
}
//��������Դ����ǰ��by���� 20130903
map.put(appDsName + "." + dbSelector.getSelectedDSName(), ds);
}
return map;
}
protected DummyTResultSet mergeResultSets(SqlExecutionContext context,
List<ResultSet> actualResultSets) throws SQLException {
if (context.getOrderByColumns() != null && !context.getOrderByColumns().isEmpty()
&& context.getGroupFunctionType() != GroupFunctionType.NORMAL) {
throw new SQLException("'group function' and 'order by' can't be together!");
}
if (context.getGroupFunctionType() == GroupFunctionType.AVG) {
throw new SQLException("The group function 'AVG' is not supported now!");
} else if (context.getGroupFunctionType() == GroupFunctionType.COUNT) {
return new CountTResultSet(this, actualResultSets);
} else if (context.getGroupFunctionType() == GroupFunctionType.MAX) {
return new MaxTResultSet(this, actualResultSets);
} else if (context.getGroupFunctionType() == GroupFunctionType.MIN) {
return new MinTResultSet(this, actualResultSets);
} else if (context.getGroupFunctionType() == GroupFunctionType.SUM) {
return new SumTResultSet(this, actualResultSets);
} else if (context.getOrderByColumns() != null && !context.getOrderByColumns().isEmpty()) {
OrderByColumn[] orderByColumns = new OrderByColumn[context.getOrderByColumns().size()];
int i = 0;
for (OrderByEle element : context.getOrderByColumns()) {
orderByColumns[i] = new OrderByColumn();
orderByColumns[i].setColumnName(element.getName());
orderByColumns[i++].setAsc(element.isASC());
}
OrderByTResultSet orderByTResultSet = new OrderByTResultSet(this, actualResultSets);
orderByTResultSet.setOrderByColumns(orderByColumns);
orderByTResultSet.setLimitFrom(context.getSkip());
orderByTResultSet.setLimitTo(context.getMax());
return orderByTResultSet;
} else {
SimpleTResultSet simpleTResultSet = new SimpleTResultSet(this, actualResultSets);
simpleTResultSet.setLimitFrom(context.getSkip());
simpleTResultSet.setLimitTo(context.getMax());
return simpleTResultSet;
}
}
protected Statement createStatementByDataSourceSelectorID(
String id,
Map<DataSource, SQLException> failedDataSources)
throws SQLException {
Connection connection = getActualConnection(id);
Statement stmt = createStatementInternal(connection, id, failedDataSources);
return stmt;
}
private int executeUpdateInternal0(String sql, int autoGeneratedKeys, int[] columnIndexes,
String[] columnNames) throws SQLException {
checkClosed();
//��ȡ����Դ
this.setOperation_type(DB_OPERATION_TYPE.WRITE_INTO_DB);
//��ȡ����
DBSelector dbselector = getGroupDBSelector(SqlType.DEFAULT_SQL_TYPE);
if (dbselector == null) {
throw new IllegalStateException("load balance����Դ�������ʹ���");
}
this.autoGeneratedKeys = autoGeneratedKeys;
this.columnIndexes = columnIndexes;
this.columnNames = columnNames;
boolean needRetry = this.autoCommit;
Map<DataSource, SQLException> failedDataSources = needRetry ? new LinkedHashMap<DataSource, SQLException>(
0)
: null;
//����ִ�н��
return dbselector.tryExecute(failedDataSources, this.executeUpdateTryer, retryingTimes,
operation_type, sql, SqlType.DEFAULT_SQL_TYPE);
}
private DataSourceTryer<Integer> executeUpdateTryer = new AbstractDataSourceTryer<Integer>() {
public Integer tryOnDataSource(
DataSource ds,
String name,
Object... args)
throws SQLException {
SqlType sqlType = (SqlType) args[1];
//��ȡ����
Connection conn = ZdalStatement.this
.getGroupConnection(ds,
sqlType, name);
return ZdalStatement.this
.executeUpdateOnConnection(
conn, args);
}
};
private int executeUpdateOnConnection(Connection conn, Object... args) throws SQLException {
Statement stmt = createStatementInternal(conn, null, null);
String sql = (String) args[0];
int affectedRows = 0;
if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
affectedRows += stmt.executeUpdate(sql);
} else if (autoGeneratedKeys != -1) {
affectedRows += stmt.executeUpdate(sql, autoGeneratedKeys);
} else if (columnIndexes != null) {
affectedRows += stmt.executeUpdate(sql, columnIndexes);
} else if (columnNames != null) {
affectedRows += stmt.executeUpdate(sql, columnNames);
} else {
affectedRows += stmt.executeUpdate(sql);
}
return affectedRows;
}
private int executeUpdateInternal(String sql, int autoGeneratedKeys, int[] columnIndexes,
String[] columnNames) throws SQLException {
checkClosed();
SqlExecutionContext context = getExecutionContext(sql, null);
if (context.mappingRuleReturnNullValue()) {
return 0;
}
dumpSql(sql, context.getTargetSqls());
int affectedRows = 0;
List<SQLException> exceptions = null;
for (Entry<String, SqlAndTable[]> entry : context.getTargetSqls().entrySet()) {
for (SqlAndTable targetSql : entry.getValue()) {
// tablesSize++;
try {
String dbSelectorId = entry.getKey();
Statement stmt = createStatementByDataSourceSelectorID(dbSelectorId, context
.getFailedDataSources());
actualStatements.add(stmt);
//added by fanzeng.
//����dbSelectorId��ȡ��Ӧ������Դ�ı�ʶ���Լ�����Դ��Ȼ��ŵ�threadlocal��
Map<String, DataSource> map = getActualIdAndDataSource(dbSelectorId);
ThreadLocalMap.put(ThreadLocalString.GET_ID_AND_DATABASE, map);
if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
affectedRows += stmt.executeUpdate(targetSql.sql);
} else if (autoGeneratedKeys != -1) {
affectedRows += stmt.executeUpdate(targetSql.sql, autoGeneratedKeys);
} else if (columnIndexes != null) {
affectedRows += stmt.executeUpdate(targetSql.sql, columnIndexes);
} else if (columnNames != null) {
affectedRows += stmt.executeUpdate(targetSql.sql, columnNames);
} else {
affectedRows += stmt.executeUpdate(targetSql.sql);
}
} catch (SQLException e) {
if (exceptions == null) {
exceptions = new ArrayList<SQLException>();
}
exceptions.add(e);
}
}
}
this.results = null;
this.moreResults = false;
this.updateCount = affectedRows;
ExceptionUtils.throwSQLException(exceptions, sql, Collections.emptyList());
return affectedRows;
}
public int executeUpdate(String sql) throws SQLException {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
return executeUpdateInternal0(sql, -1, null, null);
}
return executeUpdateInternal(sql, -1, null, null);
}
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
return executeUpdateInternal0(sql, autoGeneratedKeys, null, null);
}
return executeUpdateInternal(sql, autoGeneratedKeys, null, null);
}
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
return executeUpdateInternal0(sql, -1, columnIndexes, null);
}
return executeUpdateInternal(sql, -1, columnIndexes, null);
}
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
return executeUpdateInternal0(sql, -1, null, columnNames);
}
return executeUpdateInternal(sql, -1, null, columnNames);
}
public void addBatch(String sql) throws SQLException {
checkClosed();
if (batchedArgs == null) {
batchedArgs = new ArrayList<Object>();
}
if (sql != null) {
batchedArgs.add(sql);
}
}
/**
* @param targetSqls: key:��������ԴID; value:��������Դ��ִ�е����������SQL
* @throws ZdalCheckedExcption
*/
protected void sortBatch0(String originalSql, Map<String, List<String>> targetSqls)
throws SQLException {
SqlType sqlType = getSqlType(originalSql);
String dbselectorID = getGroupDBSelectorID(sqlType);
if (!targetSqls.containsKey(dbselectorID)) {
targetSqls.put(dbselectorID, new ArrayList<String>());
}
List<String> sqls = targetSqls.get(dbselectorID);
sqls.add(originalSql);
}
/**
* @param targetSqls: key:��������ԴID; value:��������Դ��ִ�е����������SQL
* @throws ZdalCheckedExcption
*/
protected void sortBatch(String originalSql, Map<String, List<String>> targetSqls)
throws SQLException {
//TODO:batch�����ʹ����ӳ�����ӳ�����û�з��ؽ��ʱ�����д���
try {
List<TargetDB> targets;
String virtualTableName;
List<String> virtualJoinTableNames;
if (ruleController != null) {
TargetDBMeta metaData = ruleController.getDBAndTables(originalSql, null);
targets = metaData.getTarget();
virtualTableName = metaData.getVirtualTableName();
virtualJoinTableNames = metaData.getVirtualJoinTableNames();
} else {
SqlType sqlType = getSqlType(originalSql);
SqlDispatcher sqlDispatcher = selectSqlDispatcher(autoCommit, sqlType);
DispatcherResult dispatcherResult = getExecutionMetaData(originalSql, Collections
.emptyList(), null, sqlDispatcher);
targets = dispatcherResult.getTarget();
virtualTableName = dispatcherResult.getVirtualTableName();
virtualJoinTableNames = dispatcherResult.getVirtualJoinTableNames();
}
for (TargetDB target : targets) {
//���������¾ɹ������
String targetName = ruleController != null ? target.getWritePool()[0] : target
.getDbIndex();
if (!targetSqls.containsKey(targetName)) {
targetSqls.put(targetName, new ArrayList<String>());
}
List<String> sqls = targetSqls.get(targetName);
Set<String> actualTables = target.getTableNames();
for (String tab : actualTables) {
String targetSql = replaceTableName(originalSql, virtualTableName, tab);
//���metaData(Ҳ����DispatcherResult)������join��������ô���滻��;
targetSql = replaceJoinTableName(virtualTableName, virtualJoinTableNames, tab,
targetSql);
sqls.add(targetSql);
}
}
} catch (ZdalCheckedExcption e) {
throw new SQLException(e.getMessage());
}
}
/**
* @param virtualTableName
* @param virtualJoinTableNames
* @param realTableName
* @param targetSql
* @return
*/
private String replaceJoinTableName(String virtualTableName,
List<String> virtualJoinTableNames, String realTableName,
String targetSql) {
if (virtualJoinTableNames.size() > 0) {
String suffix = getSuffix(realTableName, virtualTableName);
for (String vtab : virtualJoinTableNames) {
//��ʵ����������,ָ��
String repTab = vtab + suffix;
String[] tabs = vtab.split(",");
if (tabs.length == 2) {
vtab = tabs[0];
repTab = tabs[1];
}
targetSql = replaceTableName(targetSql, vtab, repTab);
}
}
return targetSql;
}
public int[] executeBatch() throws SQLException {
checkClosed();
if (batchedArgs == null || batchedArgs.isEmpty()) {
return new int[0];
}
List<SQLException> exceptions = null;
try {
Map<String/*����ԴID*/, List<String>/*����Դ��ִ�е�SQL*/> targetSqls = new HashMap<String, List<String>>();
for (Object arg : batchedArgs) {
if (this.dbConfigType == DataSourceConfigType.GROUP) {
sortBatch0((String) arg, targetSqls);
} else {
sortBatch((String) arg, targetSqls);
}
}
//Map<String, ConnectionAndDatasource> connections = getConnectionProxy().getActualConnections();
for (Entry<String, List<String>> entry : targetSqls.entrySet()) {
//���ûȡ������Դ
String dbSelectorID = entry.getKey();
//У���Ƿ�����batch����
checkBatchDataBaseID(dbSelectorID);
//retryContextΪnull��ʱ���ֱ���׳��쳣��
createConnectionByID(dbSelectorID);
try {
Statement stmt = createStatementByDataSourceSelectorID(dbSelectorID, null);
actualStatements.add(stmt);
for (String targetSql : entry.getValue()) {
stmt.addBatch(targetSql);
}
// TODO: ���Է���ֵ
stmt.executeBatch();
stmt.clearBatch();
} catch (SQLException e) {
if (exceptions == null) {
exceptions = new ArrayList<SQLException>();
}
exceptions.add(e);
}
}
} finally {
batchedArgs.clear();
}
ExceptionUtils.throwSQLException(exceptions, null, Collections.emptyList());
// TODO: ���Է���ֵ
return new int[0];
}
public void clearBatch() throws SQLException {
checkClosed();
if (batchedArgs != null) {
batchedArgs.clear();
}
}
public ResultSet getResultSet() throws SQLException {
return results;
}
/**
* ��֧�ֶ�������ѯ�����Ƿ���false
*/
public boolean getMoreResults() throws SQLException {
return moreResults;
}
public boolean getMoreResults(int current) throws SQLException {
throw new UnsupportedOperationException("getMoreResults");
}
public int getUpdateCount() throws SQLException {
return updateCount;
}
public ResultSet getGeneratedKeys() throws SQLException {
throw new UnsupportedOperationException("getGeneratedKeys");
}
public void cancel() throws SQLException {
throw new UnsupportedOperationException("cancel");
}
protected void checkClosed() throws SQLException {
if (closed) {
throw new SQLException("No operations allowed after statement closed.");
}
}
public void closeInternal(boolean removeThis) throws SQLException {
if (log.isDebugEnabled()) {
log.debug("invoke close");
}
if (closed) {
return;
}
List<SQLException> exceptions = null;
try {
for (ResultSet resultSet : openResultSets) {
try {
//bug fix by shenxun :�ڲ�������remove,��TStatment��ͳһclear������
((DummyTResultSet) resultSet).closeInternal(false);
} catch (SQLException e) {
if (exceptions == null) {
exceptions = new ArrayList<SQLException>();
}
exceptions.add(e);
}
}
for (Statement stmt : actualStatements) {
try {
stmt.close();
} catch (SQLException e) {
if (exceptions == null) {
exceptions = new ArrayList<SQLException>();
}
exceptions.add(e);
}
}
} finally {
closed = true;
openResultSets.clear();
actualStatements.clear();
results = null;
if (removeThis) {
if (!getConnectionProxy().getOpenStatements().remove(this)) {
log.warn("open statement does not exist");
}
}
}
ExceptionUtils.throwSQLException(exceptions, null, Collections.emptyList());
}
public void close() throws SQLException {
closeInternal(true);
}
/**
* ��batch�������ÿ�ζ�Ҫ����Ƿ�ͬһ������Դ��ʶ,ֻ����������ж�
* added by fanzeng����֧��batch�ĵ�������
* @param dbSelectorID ������Դ��ʶ
* @throws SQLException
*/
public void checkBatchDataBaseID(String dbSelectorID) throws SQLException {
if (StringUtil.isBlank(dbSelectorID)) {
throw new SQLException("The dbSelectorID can't be null!");
}
//����������У���һ�ξ�����batchDataBaseId��ֵ,Ȼ��ֱ�ӷ���
if (!isAutoCommit() && getBatchDataBaseId() == null) {
setBatchDataBaseId(dbSelectorID);
return;
}
//����������У����ҵ�ǰ��dbId�ͻ����dbId��ͬ�����׳��쳣��
if (!isAutoCommit() && !dbSelectorID.equals(getBatchDataBaseId())) {
throw new SQLException("batch����ֻ֧�ֵ��������,��ǰdbSelectorID=" + dbSelectorID + ",�����dbId="
+ getBatchDataBaseId());
}
}
/**
* ����Ϊ��֧�ֵķ���
*/
public int getFetchDirection() throws SQLException {
throw new UnsupportedOperationException("getFetchDirection");
}
public void setFetchDirection(int fetchDirection) throws SQLException {
throw new UnsupportedOperationException("setFetchDirection");
}
public int getFetchSize() throws SQLException {
return this.fetchSize;
//throw new UnsupportedOperationException("getFetchSize");
}
public void setFetchSize(int fetchSize) throws SQLException {
this.fetchSize = fetchSize;
//throw new UnsupportedOperationException("setFetchSize");
}
public int getMaxFieldSize() throws SQLException {
throw new UnsupportedOperationException("getMaxFieldSize");
}
public void setMaxFieldSize(int maxFieldSize) throws SQLException {
throw new UnsupportedOperationException("setMaxFieldSize");
}
public int getMaxRows() throws SQLException {
throw new UnsupportedOperationException("getMaxRows");
}
public void setMaxRows(int maxRows) throws SQLException {
throw new UnsupportedOperationException("setMaxRows");
}
public int getQueryTimeout() throws SQLException {
throw new UnsupportedOperationException("getQueryTimeout");
}
public void setQueryTimeout(int queryTimeout) throws SQLException {
throw new UnsupportedOperationException("setQueryTimeout");
}
public void setCursorName(String cursorName) throws SQLException {
throw new UnsupportedOperationException("setCursorName");
}
public void setEscapeProcessing(boolean escapeProcessing) throws SQLException {
throw new UnsupportedOperationException("setEscapeProcessing");
}
public SQLWarning getWarnings() throws SQLException {
return null;
}
public void clearWarnings() throws SQLException {
}
/**
* ����������getter/setter
*/
public int getResultSetType() throws SQLException {
return resultSetType;
}
public void setResultSetType(int resultSetType) {
this.resultSetType = resultSetType;
}
public int getResultSetConcurrency() throws SQLException {
return resultSetConcurrency;
}
public void setResultSetConcurrency(int resultSetConcurrency) {
this.resultSetConcurrency = resultSetConcurrency;
}
public int getResultSetHoldability() throws SQLException {
return resultSetHoldability;
}
public void setResultSetHoldability(int resultSetHoldability) {
this.resultSetHoldability = resultSetHoldability;
}
public Map<String, DBSelector> getDataSourcePool() {
return dbSelectors;
}
public void setDataSourcePool(Map<String, DBSelector> dbSelectors) {
this.dbSelectors = dbSelectors;
}
public ZdalConnection getConnectionProxy() {
return connectionProxy;
}
public void setConnectionProxy(ZdalConnection connectionProxy) {
this.connectionProxy = connectionProxy;
}
public boolean isAutoCommit() {
return autoCommit;
}
public void setAutoCommit(boolean autoCommit) {
this.autoCommit = autoCommit;
}
public Set<ResultSet> getTResultSets() {
return openResultSets;
}
public boolean isReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public long getTimeoutForEachTable() {
return timeoutForEachTable;
}
public void setTimeoutForEachTable(long timeoutForEachTable) {
this.timeoutForEachTable = timeoutForEachTable;
}
public int getRetryingTimes() {
return retryingTimes;
}
public void setRetryingTimes(int retryingTimes) {
this.retryingTimes = retryingTimes;
}
public void setOperation_type(DB_OPERATION_TYPE operation_type) {
this.operation_type = operation_type;
}
public DB_OPERATION_TYPE getOperation_type() {
return operation_type;
}
public String getBatchDataBaseId() {
return batchDataBaseId;
}
public void setBatchDataBaseId(String batchDataBaseId) {
this.batchDataBaseId = batchDataBaseId;
}
public boolean isHintReplaceSupport() {
return isHintReplaceSupport;
}
public void setHintReplaceSupport(boolean isHintReplaceSupport) {
this.isHintReplaceSupport = isHintReplaceSupport;
}
public DataSourceConfigType getDbConfigType() {
return dbConfigType;
}
public void setDbConfigType(DataSourceConfigType dbConfigType) {
this.dbConfigType = dbConfigType;
}
@Override
public boolean isClosed() throws SQLException {
return false;
}
@Override
public boolean isPoolable() throws SQLException {
return false;
}
@Override
public void setPoolable(boolean poolable) throws SQLException {
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return false;
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
return null;
}
public String getAppDsName() {
return appDsName;
}
public void setAppDsName(String appDsName) {
this.appDsName = appDsName;
}
}