package com.taobao.tddl.atom.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import com.taobao.tddl.atom.TAtomDbStatusEnum;
import com.taobao.tddl.common.jdbc.SqlTypeParser;
import com.taobao.tddl.common.model.SqlMetaData;
import com.taobao.tddl.monitor.Monitor;
import com.taobao.tddl.monitor.eagleeye.EagleeyeHelper;
import com.taobao.tddl.monitor.unit.UnitDeployProtect;
import com.taobao.tddl.common.utils.logger.Logger;
import com.taobao.tddl.common.utils.logger.LoggerFactory;
/**
* Statement 包装类
*
* @author shenxun
*/
public class TStatementWrapper implements TStatement {
private static Logger log = LoggerFactory.getLogger(TStatementWrapper.class);
protected static final String UPDATE = "UPDATE";
protected static final String QUERY = "QUERY";
protected Statement targetStatement;
protected final TConnectionWrapper connectionWrapper;
protected final TDataSourceWrapper datasourceWrapper;
/**
* 经过计算后的结果集,允许使用 getResult函数调用. 一个statement只允许有一个结果集
*/
protected TResultSetWrapper currentResultSet;
/**
* sql元信息持有
*/
protected SqlMetaData sqlMetaData = null;
public TStatementWrapper(Statement targetStatement, TConnectionWrapper connectionWrapper,
TDataSourceWrapper tdsWrapper){
this.targetStatement = targetStatement;
this.connectionWrapper = connectionWrapper;
this.datasourceWrapper = tdsWrapper;
}
public void addBatch(String sql) throws SQLException {
this.targetStatement.addBatch(sql);
}
public void cancel() throws SQLException {
this.targetStatement.cancel();
}
public void clearBatch() throws SQLException {
this.targetStatement.clearBatch();
}
public void clearWarnings() throws SQLException {
this.targetStatement.clearWarnings();
}
public void close() throws SQLException {
close(true);
}
void close(boolean removeThis) throws SQLException {
try {
if (currentResultSet != null) currentResultSet.close();
} catch (SQLException e) {
log.warn("Close currentResultSet failed.", e);
} finally {
currentResultSet = null;
}
try {
if (this.targetStatement != null) this.targetStatement.close();
} finally {
this.targetStatement = null; // 端口与物理statement的引用,底下可能会有ps
// cache,导致节点无法被gc
if (removeThis) {
// 关闭之后,移除
connectionWrapper.removeOpenedStatements(this);
}
}
}
protected void recordReadTimes() throws SQLException {
TAtomDbStatusEnum status = datasourceWrapper.connectionProperties.dbStatus;
if (status != TAtomDbStatusEnum.R_STATUS && status != TAtomDbStatusEnum.RW_STATUS) {
throw new SQLException("db do not allow to execute read ! dbStatus is " + status);
}
/*
* int readRestrictionTimes =
* datasourceWrapper.connectionProperties.readRestrictionTimes; int
* currentReadTimes = datasourceWrapper.readTimes.incrementAndGet(); if
* (readRestrictionTimes != 0) { if (currentReadTimes >
* readRestrictionTimes) {
* datasourceWrapper.readTimesReject.incrementAndGet(); throw new
* SQLException("max read times ," + currentReadTimes); } }
*/
if (!datasourceWrapper.readFlowControl.allow()) {
throw new SQLException(datasourceWrapper.readFlowControl.reportExceed());
}
}
protected void recordWriteTimes() throws SQLException {
TAtomDbStatusEnum status = datasourceWrapper.connectionProperties.dbStatus;
if (status != TAtomDbStatusEnum.W_STATUS && status != TAtomDbStatusEnum.RW_STATUS) {
throw new SQLException("db do not allow to execute write ! dbStatus is " + status);
}
/*
* int writeRestrictionTimes =
* datasourceWrapper.connectionProperties.writeRestrictionTimes; int
* currentWriteTimes = datasourceWrapper.writeTimes.incrementAndGet();
* if (writeRestrictionTimes != 0) { if (currentWriteTimes >
* writeRestrictionTimes) {
* datasourceWrapper.writeTimesReject.incrementAndGet(); throw new
* SQLException("max write times , " + currentWriteTimes); } }
*/
if (!datasourceWrapper.writeFlowControl.allow()) {
throw new SQLException(datasourceWrapper.writeFlowControl.reportExceed());
}
}
// 增加并发读计数并判断阀值
protected void increaseConcurrentRead() throws SQLException {
int maxConcurrentReadRestrict = datasourceWrapper.connectionProperties.maxConcurrentReadRestrict;
int concurrentReadCount = datasourceWrapper.concurrentReadCount.incrementAndGet();
if (maxConcurrentReadRestrict != 0) {
if (concurrentReadCount > maxConcurrentReadRestrict) {
datasourceWrapper.readTimesReject.incrementAndGet();
throw new SQLException("maxConcurrentReadRestrict reached , " + maxConcurrentReadRestrict);
}
}
}
// 增加并发写计数并判断阀值
protected void increaseConcurrentWrite() throws SQLException {
int maxConcurrentWriteRestrict = datasourceWrapper.connectionProperties.maxConcurrentWriteRestrict;
int concurrentWriteCount = datasourceWrapper.concurrentWriteCount.incrementAndGet();
if (maxConcurrentWriteRestrict != 0) {
if (concurrentWriteCount > maxConcurrentWriteRestrict) {
datasourceWrapper.writeTimesReject.incrementAndGet();
throw new SQLException("maxConcurrentWriteRestrict reached , " + maxConcurrentWriteRestrict);
}
}
}
// 减少并发读计数
protected void decreaseConcurrentRead() throws SQLException {
datasourceWrapper.concurrentReadCount.decrementAndGet();
}
// 减少并发写计数
protected void decreaseConcurrentWrite() throws SQLException {
datasourceWrapper.concurrentWriteCount.decrementAndGet();
}
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 boolean executeInternal(String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames)
throws SQLException {
if (SqlTypeParser.isQuerySql(sql)) {
executeQuery(sql);
return true;
} else {
executeUpdateInternal(sql, autoGeneratedKeys, columnIndexes, columnNames);
return false;
}
}
private int executeUpdateInternal(String sql, int autoGeneratedKeys, int[] columnIndexes, String[] columnNames)
throws SQLException {
// if (sqlMetaData == null) throw new
// NullPointerException("miss sql meta data.");
ensureResultSetIsEmpty();
recordWriteTimes();
increaseConcurrentWrite();
long time0 = System.currentTimeMillis();
Exception e0 = null;
startRpc(UPDATE);
try {
UnitDeployProtect.unitDeployProtect();
if (autoGeneratedKeys == -1 && columnIndexes == null && columnNames == null) {
return this.targetStatement.executeUpdate(sql);
} else if (autoGeneratedKeys != -1) {
return this.targetStatement.executeUpdate(sql, autoGeneratedKeys);
} else if (columnIndexes != null) {
return this.targetStatement.executeUpdate(sql, columnIndexes);
} else if (columnNames != null) {
return this.targetStatement.executeUpdate(sql, columnNames);
} else {
return this.targetStatement.executeUpdate(sql);
}
} catch (SQLException e) {
e0 = e;
throw e;
} finally {
endRpc(sql, e0);
decreaseConcurrentWrite();
recordSql(sql, System.currentTimeMillis() - time0, e0);
}
}
public int[] executeBatch() throws SQLException {
ensureResultSetIsEmpty();
recordWriteTimes();
increaseConcurrentWrite();
try {
return this.targetStatement.executeBatch();
} finally {
decreaseConcurrentWrite();
}
}
public ResultSet executeQuery(String sql) throws SQLException {
// if (sqlMetaData == null) throw new
// NullPointerException("miss sql meta data.");
ensureResultSetIsEmpty();
recordReadTimes();
increaseConcurrentRead();
long time0 = System.currentTimeMillis();
Exception e0 = null;
startRpc(QUERY);
try {
currentResultSet = new TResultSetWrapper(this, this.targetStatement.executeQuery(sql));
return currentResultSet;
} catch (SQLException e) {
decreaseConcurrentRead();
e0 = e;
throw e;
} finally {
endRpc(sql, e0);
recordSql(sql, System.currentTimeMillis() - time0, e0);
}
}
protected void startRpc(String sqlType) {
EagleeyeHelper.startRpc(datasourceWrapper.runTimeConf.getIp(),
datasourceWrapper.runTimeConf.getPort(),
datasourceWrapper.runTimeConf.getDbName(),
sqlType);
}
protected void endRpc(String sql, Exception e) {
if (sqlMetaData == null) sqlMetaData = SqlMetaDataFactory.getSqlMetaData(sql);
EagleeyeHelper.endRpc(sqlMetaData, e);
}
protected void recordSql(String sql, long elapsedTime, Exception e) {
// druid只使用druid本身带的atom日志
// 只有允许记录atom的sql以及在采样频率下才能记录
if (!Monitor.isStatAtomSql || !Monitor.isSamplingRecord()) {
return;
}
if (!Monitor.isInclude(sql)) {
return; // 不在白名单中,不输出日志,以减少日志量
}
String dbname = datasourceWrapper.connectionProperties.datasourceName;
String dbIp = datasourceWrapper.connectionProperties.ip;
String dbPort = datasourceWrapper.connectionProperties.port;
String realDbName = datasourceWrapper.connectionProperties.realDbName;
if (e != null) {
// TODO 暂时不使用惩罚超时机制
// if (elapsedTime > 500) {
// this.datasourceWrapper.countTimeOut(); //记录超时
// }
Monitor.atomSqlAdd(dbname,
Monitor.buildExecuteSqlKey2(sql),
Monitor.KEY3_EXECUTE_A_SQL_EXCEPTION,
dbIp,
dbPort,
realDbName,
elapsedTime,
1);
} else if (elapsedTime > Monitor.sqlTimeout) {
// this.datasourceWrapper.countTimeOut(); //记录超时
Monitor.atomSqlAdd(dbname,
Monitor.buildExecuteSqlKey2(sql),
Monitor.KEY3_EXECUTE_A_SQL_TIMEOUT,
dbIp,
dbPort,
realDbName,
elapsedTime,
1);
} else {
Monitor.atomSqlAdd(dbname,
Monitor.buildExecuteSqlKey2(sql),
Monitor.KEY3_EXECUTE_A_SQL_SUCCESS,
dbIp,
dbPort,
realDbName,
elapsedTime,
1);
}
}
public int executeUpdate(String sql) throws SQLException {
return executeUpdateInternal(sql, -1, null, null);
}
public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
return executeUpdateInternal(sql, autoGeneratedKeys, null, null);
}
public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
return executeUpdateInternal(sql, -1, columnIndexes, null);
}
public int executeUpdate(String sql, String[] columnNames) throws SQLException {
return executeUpdateInternal(sql, -1, null, columnNames);
}
public Connection getConnection() throws SQLException {
return connectionWrapper;
}
public int getFetchDirection() throws SQLException {
return this.targetStatement.getFetchDirection();
}
public int getFetchSize() throws SQLException {
return this.targetStatement.getFetchSize();
}
public ResultSet getGeneratedKeys() throws SQLException {
return new TResultSetWrapper(this, this.targetStatement.getGeneratedKeys());
}
public int getMaxFieldSize() throws SQLException {
return this.targetStatement.getMaxFieldSize();
}
public int getMaxRows() throws SQLException {
return this.targetStatement.getMaxRows();
}
public boolean getMoreResults() throws SQLException {
return this.targetStatement.getMoreResults();
}
public boolean getMoreResults(int current) throws SQLException {
return this.targetStatement.getMoreResults(current);
}
public int getQueryTimeout() throws SQLException {
return this.targetStatement.getQueryTimeout();
}
// FIXME
public ResultSet getResultSet() throws SQLException {
/*
* ResultSet targetRS = this.targetStatement.getResultSet(); if
* (targetRS == null) { return null; } return new
* TResultSetWrapper(this, targetRS);
*/
return currentResultSet;
}
public int getResultSetConcurrency() throws SQLException {
return this.targetStatement.getResultSetConcurrency();
}
public int getResultSetHoldability() throws SQLException {
return this.targetStatement.getResultSetHoldability();
}
public int getResultSetType() throws SQLException {
return this.targetStatement.getResultSetType();
}
public int getUpdateCount() throws SQLException {
return this.targetStatement.getUpdateCount();
}
public SQLWarning getWarnings() throws SQLException {
return this.targetStatement.getWarnings();
}
public void setCursorName(String name) throws SQLException {
this.targetStatement.setCursorName(name);
}
public void setEscapeProcessing(boolean enable) throws SQLException {
this.targetStatement.setEscapeProcessing(enable);
}
public void setFetchDirection(int direction) throws SQLException {
this.targetStatement.setFetchDirection(direction);
}
public void setFetchSize(int rows) throws SQLException {
this.targetStatement.setFetchSize(rows);
}
public void setMaxFieldSize(int max) throws SQLException {
this.targetStatement.setMaxFieldSize(max);
}
public void setMaxRows(int max) throws SQLException {
this.targetStatement.setMaxRows(max);
}
public void setQueryTimeout(int seconds) throws SQLException {
this.targetStatement.setQueryTimeout(seconds);
}
/**
* 如果新建了查询,那么上一次查询的结果集应该被显示的关闭掉。这才是符合jdbc规范的
*
* @throws SQLException
*/
protected void ensureResultSetIsEmpty() throws SQLException {
if (currentResultSet != null) {
try {
currentResultSet.close();
} catch (SQLException e) {
log.error("exception on close last result set . can do nothing..", e);
} finally {
currentResultSet = null;
}
}
}
public boolean isWrapperFor(Class<?> iface) throws SQLException {
return this.getClass().isAssignableFrom(iface);
}
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> iface) throws SQLException {
try {
return (T) this;
} catch (Exception e) {
throw new SQLException(e);
}
}
public boolean isClosed() throws SQLException {
return this.targetStatement.isClosed();
}
public void setPoolable(boolean poolable) throws SQLException {
this.targetStatement.setPoolable(poolable);
}
public boolean isPoolable() throws SQLException {
return this.targetStatement.isPoolable();
}
@Override
public void fillMetaData(SqlMetaData sqlMetaData) {
this.sqlMetaData = sqlMetaData;
}
@Override
public SqlMetaData getSqlMetaData() {
return this.sqlMetaData;
}
}