org.tinygroup.dbrouterjdbc3.jdbc.TinyStatement Maven / Gradle / Ivy
The newest version!
/**
* Copyright (c) 1997-2013, tinygroup.org ([email protected]).
*
* Licensed under the GPL, Version 3.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.gnu.org/licenses/gpl.html
*
* 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.
* --------------------------------------------------------------------------
* 版权 (c) 1997-2013, tinygroup.org ([email protected]).
*
* 本开源软件遵循 GPL 3.0 协议;
* 如果您不遵循此协议,则不被允许使用此文件。
* 你可以从下面的地址获取完整的协议文本
*
* http://www.gnu.org/licenses/gpl.html
*/
package org.tinygroup.dbrouterjdbc3.jdbc;
import org.tinygroup.commons.cpu.MonitorUtil;
import org.tinygroup.commons.tools.Assert;
import org.tinygroup.dbrouter.RouterManager;
import org.tinygroup.dbrouter.StatementProcessor;
import org.tinygroup.dbrouter.config.Partition;
import org.tinygroup.dbrouter.config.Router;
import org.tinygroup.dbrouter.config.Shard;
import org.tinygroup.dbrouter.factory.RouterManagerBeanFactory;
import org.tinygroup.dbrouter.util.DbRouterUtil;
import org.tinygroup.dbrouterjdbc3.thread.ExecuteQueryCallBack;
import org.tinygroup.dbrouterjdbc3.thread.ExecuteUpdateCallBack;
import org.tinygroup.dbrouterjdbc3.thread.MultiThreadStatementProcessor;
import org.tinygroup.dbrouterjdbc3.thread.StatementProcessorCallBack;
import org.tinygroup.jsqlparser.statement.select.Select;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.threadgroup.MultiThreadProcessor;
import java.sql.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* 功能说明:
*
*
* 开发人员: renhui
* 开发时间: 2013-12-24
*
*/
public class TinyStatement implements Statement {
protected Map statementMap = new ConcurrentHashMap();
protected final TinyConnection tinyConnection;
protected RouterManager routerManager = RouterManagerBeanFactory
.getManager();
protected StatementProcessor statementProcessor = null;
protected final Router router;
protected boolean isClosed;
protected int maxRows;
protected boolean escapeProcessing = true;
protected int queryTimeout = 5;
protected ResultSet resultSet;
protected int updateCount;
protected final boolean closedByResultSet;
protected final int resultSetType;
protected final int resultSetConcurrency;
protected boolean cancelled;
protected int fetchSize = 100;
protected boolean autoCommit = true;
protected Logger logger = LoggerFactory.getLogger(TinyStatement.class);
public TinyStatement(Router router, TinyConnection tinyConnection,
int resultSetType, int resultSetConcurrency,
boolean closedByResultSet, boolean autoCommit) {
Assert.assertNotNull(tinyConnection, "tinyConnection must not null");
this.router = router;
this.tinyConnection = tinyConnection;
this.closedByResultSet = closedByResultSet;
this.resultSetType = resultSetType;
this.resultSetConcurrency = resultSetConcurrency;
this.autoCommit = autoCommit;
}
public ResultSet executeQuery(String sql) throws SQLException {
statementProcessor = null;
checkClosed();
closeOldResultSet();
Partition partition = routerManager.getPartition(router, sql);
List statements = getStatementsBySql(partition,
sql);
if (statements.size() == 1) {
resultSet = statements.get(0).executeQuery();
return new TinyResultSetWrapper(sql, resultSet, this,
tinyConnection);
} else if (statements.size() > 1) {
double cpuRatio = MonitorUtil.getCpuUsage();
List resultSetList = new ArrayList();
List resultSetExecutors = new ArrayList();
if (cpuRatio < router.getCpuRatio()) {
MultiThreadProcessor processors = new MultiThreadProcessor(
"executeQuery-threads");
int threadSize = statements.size();
List> threadProcessors = new ArrayList>();
for (int j = 0; j < threadSize; j++) {
RealStatementExecutor realStatementExecutor = statements
.get(j);
MultiThreadStatementProcessor processor = new MultiThreadStatementProcessor(
String.format("statement-processor-thread-%d", j),
realStatementExecutor);
StatementProcessorCallBack callBack = new ExecuteQueryCallBack();
processor.setCallBack(callBack);
processors.addProcessor(processor);
threadProcessors.add(processor);
}
long startTime = System.currentTimeMillis();
processors.start();
long endTime = System.currentTimeMillis();
logger.logMessage(LogLevel.INFO, "线程组:<{}>执行时间:{}",
"executeQuery-threads", endTime - startTime);
for (MultiThreadStatementProcessor threadProcessor : threadProcessors) {
ResultSetExecutor resultSetExecutor = threadProcessor
.getResult();
resultSetExecutors.add(resultSetExecutor);
resultSetList.add(resultSetExecutor.getResultSet());
}
} else {
for (RealStatementExecutor statement : statements) {
ResultSet realResultSet = statement.executeQuery();
resultSetExecutors.add(new ResultSetExecutor(realResultSet,
statement.getExecuteSql(), statement
.getOriginalSql(), statement.getShard(),statement.getPartition()));
resultSetList.add(realResultSet);
}
}
if (statementProcessor != null) {
return statementProcessor.combineResult(statements.get(0)
.getExecuteSql(), resultSetList);
} else {
resultSet = new TinyResultSetMultiple(sql, router,
resultSetExecutors, this, tinyConnection);
}
return resultSet;
}
return null;
}
public int executeUpdate(String sql) throws SQLException {
checkClosed();
closeOldResultSet();
Partition partition = routerManager.getPartition(router, sql);
List statements = getStatementsBySql(partition,
sql);
if (statements.size() == 1) {
updateCount = statements.get(0).executeUpdate();
return updateCount;
} else if (statements.size() > 1) {
if (partition.getMode() == Partition.MODE_PRIMARY_SLAVE
&& tinyConnection.getAutoCommit()) {
throw new RuntimeException(
"primary slave mode exist one more write database,the connection autocommit must set false");
}
double cpuRatio = MonitorUtil.getCpuUsage();
if (cpuRatio < router.getCpuRatio()) {
MultiThreadProcessor processors = new MultiThreadProcessor(
"executeUpdate-threads");
int threadSize = statements.size();
List> threadProcessors = new ArrayList>();
for (int j = 0; j < threadSize; j++) {
RealStatementExecutor realStatementExecutor = statements
.get(j);
MultiThreadStatementProcessor processor = new MultiThreadStatementProcessor(
String.format("statement-processor-thread-%d", j),
realStatementExecutor);
StatementProcessorCallBack callBack = new ExecuteUpdateCallBack();
processor.setCallBack(callBack);
processors.addProcessor(processor);
threadProcessors.add(processor);
}
long startTime = System.currentTimeMillis();
processors.start();
long endTime = System.currentTimeMillis();
logger.logMessage(LogLevel.INFO, "线程组:<{}>执行时间:{}",
"executeUpdate-threads", endTime - startTime);
for (MultiThreadStatementProcessor threadProcessor : threadProcessors) {
updateCount += threadProcessor.getResult();
}
} else {
for (RealStatementExecutor statement : statements) {
updateCount += statement.executeUpdate();
}
}
return updateCount;
}
return 0;
}
protected Statement getStatement(Shard shard) throws SQLException {
Statement statement = statementMap.get(shard);
if (tinyConnection.getAutoCommit() != autoCommit) {// 有调用过tinyconnection.setAutoCommit(),重写创建statement
logger.logMessage(
LogLevel.DEBUG,
"autoCommit has change,original:{0},now:{1},create new statement",
autoCommit, tinyConnection.getAutoCommit());
statement = shard.getConnection(tinyConnection).createStatement(
resultSetType, resultSetConcurrency,
getResultSetHoldability());
setStatementProperties(statement);
statementMap.put(shard, statement);
} else {
if (statement == null) {
statement = shard.getConnection(tinyConnection)
.createStatement(resultSetType, resultSetConcurrency,
getResultSetHoldability());
setStatementProperties(statement);
statementMap.put(shard, statement);
}
}
return statement;
}
protected Statement getNewStatement(String sql,Shard shard) throws SQLException {
Statement statement = shard.getConnection(tinyConnection)
.createStatement(resultSetType, resultSetConcurrency,
getResultSetHoldability());
setStatementProperties(statement);
return statement;
}
protected void setStatementProperties(Statement statement)
throws SQLException {
statement.setMaxRows(maxRows);
statement.setEscapeProcessing(escapeProcessing);
statement.setQueryTimeout(queryTimeout);
statement.setFetchSize(fetchSize);
}
public void close() throws SQLException {
StringBuffer buffer = new StringBuffer();
boolean noError = true;
for (Statement statement : statementMap.values()) {
try {
statement.close();
} catch (SQLException e) {
buffer.append(String
.format("statement close error,errorcode:%s,sqlstate:%s,message:%s \n",
e.getErrorCode(), e.getSQLState(),
e.getMessage()));
noError = false;
logger.errorMessage("statement close error", e);
}
}
statementMap.clear();
this.isClosed = true;
if (!noError) {
throw new SQLException(buffer.toString());
}
}
/**
* INTERNAL. Close and old result set if there is still one open.
*/
protected void closeOldResultSet() throws SQLException {
try {
if (!closedByResultSet) {
if (resultSet != null) {
resultSet.close();
}
}
} finally {
cancelled = false;
resultSet = null;
updateCount = 0;
}
}
/**
* Check whether the statement was cancelled.
*
* @return true if yes
*/
public boolean wasCancelled() {
return cancelled;
}
protected void checkClosed() throws SQLException {
tinyConnection.checkClosed();
if (isClosed) {
throw new SQLException("statement is closed");
}
}
public int getMaxFieldSize() throws SQLException {
checkClosed();
return 0;
}
public void setMaxFieldSize(int max) throws SQLException {
checkClosed();
}
public int getMaxRows() throws SQLException {
return maxRows;
}
public void setMaxRows(int max) throws SQLException {
checkClosed();
if (maxRows < 0) {
throw new SQLException("not valid value for maxRows:" + maxRows);
}
this.maxRows = max;
}
public void setEscapeProcessing(boolean enable) throws SQLException {
checkClosed();
this.escapeProcessing = enable;
}
public int getQueryTimeout() throws SQLException {
checkClosed();
return queryTimeout;
}
public void setQueryTimeout(int seconds) throws SQLException {
checkClosed();
this.queryTimeout = seconds;
}
public void cancel() throws SQLException {
checkClosed();
for (Statement statement : statementMap.values()) {
statement.cancel();
}
cancelled = true;
}
public SQLWarning getWarnings() throws SQLException {
checkClosed();
return null;
}
public void clearWarnings() throws SQLException {
checkClosed();
}
/**
* Sets the name of the cursor. This call is ignored.
*/
public void setCursorName(String name) throws SQLException {
checkClosed();
}
public boolean execute(String sql) throws SQLException {
org.tinygroup.jsqlparser.statement.Statement statement = routerManager
.getSqlStatement(sql);
boolean returnsResultSet = false;
if (statement instanceof Select) {
returnsResultSet = true;
executeQuery(sql);
} else {
returnsResultSet = false;
executeUpdate(sql);
}
return returnsResultSet;
}
private Shard getShard(String sql, Partition partition) throws SQLException {
Collection shards = routerManager.getShards(partition, sql,
getPreparedParams());
if (shards.size() == 0) {
throw new SQLException("没有可用的数据库连接。");
}
Iterator iterator = shards.iterator();
Shard shard = iterator.next();
return shard;
}
private List getPrimarySlaveShard(String sql, Partition partition)
throws SQLException {
org.tinygroup.jsqlparser.statement.Statement statement = routerManager
.getSqlStatement(sql);
List shards = new ArrayList();
if (!(statement instanceof Select)) {
shards.addAll(routerManager.getShardBalance().getWritableShard(
partition));
} else {
if (!tinyConnection.getAutoCommit()) {// 从写的列表中随机选择一个进行读取
Shard shard = routerManager.getShardBalance()
.getReadShardWithTransaction(partition);
shards.add(shard);
} else {
Shard shard = routerManager.getShardBalance().getReadableShard(
partition);
shards.add(shard);
}
}
return shards;
}
public ResultSet getResultSet() throws SQLException {
checkClosed();
return resultSet;
}
public int getUpdateCount() throws SQLException {
checkClosed();
return updateCount;
}
public boolean getMoreResults() throws SQLException {
checkClosed();
closeOldResultSet();
return false;
}
public void setFetchDirection(int direction) throws SQLException {
checkClosed();
}
public int getFetchDirection() throws SQLException {
checkClosed();
return ResultSet.FETCH_FORWARD;
}
public void setFetchSize(int rows) throws SQLException {
checkClosed();
if (rows < 0 || (rows > 0 && maxRows > 0 && rows > maxRows)) {
throw new SQLException("invalid value for rows:" + rows);
}
if (rows == 0) {
rows = 100;
}
this.fetchSize = rows;
}
public int getFetchSize() throws SQLException {
checkClosed();
return fetchSize;
}
public int getResultSetConcurrency() throws SQLException {
checkClosed();
return resultSetConcurrency;
}
public int getResultSetType() throws SQLException {
checkClosed();
return resultSetType;
}
public void addBatch(String sql) throws SQLException {
checkClosed();
Partition partition = routerManager.getPartition(router, sql);
List statements = getStatementsBySql(partition,
sql);
for (RealStatementExecutor statement : statements) {
statement.addBatch();
}
}
/**
* 根据sql获取需要执行的statements列表
*
* @param sql
* @return
* @throws SQLException
*/
protected List getStatementsBySql(
Partition partition, String sql) throws SQLException {
List statements = new ArrayList();
if (partition.getMode() == Partition.MODE_PRIMARY_SLAVE) {
List shards = getPrimarySlaveShard(sql, partition);
for (Shard shard : shards) {
Statement realStatement = getStatement(shard);
String realSql = routerManager.getSql(partition, shard, sql,
getPreparedParams());
statements.add(new RealStatementExecutor(realStatement,
realSql, sql, shard, partition));
}
} else {
Shard firstShard = partition.getShards().get(0);// 获取第一个分片
// 获取实际的表名
String transSql = DbRouterUtil.transformInsertSql(sql, router,
firstShard.getTableMappingMap(),
tinyConnection.getMetaData());// 如果是insert语句,那么检测是否有主键字段。
Collection shards = routerManager.getShards(partition,
transSql, getPreparedParams());
for (StatementProcessor processor : routerManager
.getStatementProcessorList()) {
if (processor.isMatch(transSql)) {
statementProcessor = processor;
transSql = processor.getSql(transSql);
break;
}
}
if (shards.size() == 0) {// 如果一个都没有符合的,就匹配所有的shard。
shards = partition.getShards();
}
Iterator iterator = shards.iterator();
while (iterator.hasNext()) {
Shard shard = iterator.next();
Statement realStatement = getStatement(shard);
String realSql = routerManager.getSql(partition, shard,
transSql, getPreparedParams());// 变化表名等
statements.add(new RealStatementExecutor(realStatement,
realSql, sql, shard, partition));
}
}
return statements;
}
protected Object[] getPreparedParams() {
return new Object[0];
}
public void clearBatch() throws SQLException {
checkClosed();
for (Statement statement : statementMap.values()) {
statement.clearBatch();
}
}
public int[] executeBatch() throws SQLException {
checkClosed();
List affectList = new ArrayList();
for (Statement statement : statementMap.values()) {
int[] affects = statement.executeBatch();
for (int affect : affects) {
affectList.add(affect);
}
}
int[] results = new int[affectList.size()];
for (int i = 0; i < results.length; i++) {
results[i] = affectList.get(i);
}
return results;
}
public Connection getConnection() throws SQLException {
return tinyConnection;
}
public boolean getMoreResults(int current) throws SQLException {
switch (current) {
case Statement.CLOSE_CURRENT_RESULT:
case Statement.CLOSE_ALL_RESULTS:
checkClosed();
closeOldResultSet();
break;
case Statement.KEEP_CURRENT_RESULT:
break;
default:
throw new SQLException("invalid value for current:" + current);
}
return false;
}
public ResultSet getGeneratedKeys() throws SQLException {
// TODO
routerManager.getPrimaryKey(router, "IDENTITY");// 键值
throw new SQLException("not support generatedKeys");
}
public int executeUpdate(String sql, int autoGeneratedKeys)
throws SQLException {
return executeUpdate(sql);
}
public int executeUpdate(String sql, int[] columnIndexes)
throws SQLException {
return executeUpdate(sql);
}
public int executeUpdate(String sql, String[] columnNames)
throws SQLException {
return executeUpdate(sql);
}
public boolean execute(String sql, int autoGeneratedKeys)
throws SQLException {
return execute(sql);
}
public boolean execute(String sql, int[] columnIndexes) throws SQLException {
return execute(sql);
}
public boolean execute(String sql, String[] columnNames)
throws SQLException {
return execute(sql);
}
public int getResultSetHoldability() throws SQLException {
checkClosed();
return ResultSet.HOLD_CURSORS_OVER_COMMIT;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy