All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.tinygroup.dbrouterjdbc4.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.dbrouterjdbc4.jdbc;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import org.tinygroup.cache.Cache;
import org.tinygroup.commons.tools.Assert;
import org.tinygroup.commons.tools.CollectionUtil;
import org.tinygroup.dbrouter.RouterKeyGenerator;
import org.tinygroup.dbrouter.RouterManager;
import org.tinygroup.dbrouter.StatementProcessor;
import org.tinygroup.dbrouter.cache.CacheKey;
import org.tinygroup.dbrouter.config.Partition;
import org.tinygroup.dbrouter.config.Router;
import org.tinygroup.dbrouter.config.Shard;
import org.tinygroup.dbrouter.context.RealStatementExecutor;
import org.tinygroup.dbrouter.context.ResultSetExecutor;
import org.tinygroup.dbrouter.context.StatementExecuteContext;
import org.tinygroup.dbrouter.exception.DbrouterRuntimeException;
import org.tinygroup.dbrouter.factory.RouterManagerBeanFactory;
import org.tinygroup.dbrouter.impl.InsertSqlTransform;
import org.tinygroup.dbrouter.impl.InsertSqlTransform.ColumnInfo;
import org.tinygroup.dbrouter.util.DbRouterUtil;
import org.tinygroup.dbrouter.util.ParamObjectBuilder;
import org.tinygroup.dbrouterjdbc4.thread.ExecuteQueryCallBack;
import org.tinygroup.dbrouterjdbc4.thread.ExecuteResultCallable;
import org.tinygroup.dbrouterjdbc4.thread.ExecuteUpdateCallBack;
import org.tinygroup.jsqlparser.expression.Expression;
import org.tinygroup.jsqlparser.expression.LongValue;
import org.tinygroup.jsqlparser.expression.StringValue;
import org.tinygroup.jsqlparser.expression.operators.relational.ExpressionList;
import org.tinygroup.jsqlparser.expression.operators.relational.ItemsList;
import org.tinygroup.jsqlparser.schema.Column;
import org.tinygroup.jsqlparser.statement.insert.Insert;
import org.tinygroup.jsqlparser.statement.select.Select;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;

/**
 * 
 * 功能说明:
 * 

* * 开发人员: renhui
* 开发时间: 2013-12-24
*
*/ public class TinyStatement implements Statement { protected ThreadLocal> statementMap = new ThreadLocal>(){ @Override protected Map initialValue() { return new HashMap(); } }; protected final TinyConnection tinyConnection; protected RouterManager routerManager = RouterManagerBeanFactory .getManager(); 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 ParamObjectBuilder builder; protected ThreadPoolExecutor pool; protected static 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; int threadSize = router.getThreadSize(); if (threadSize > 0) { this.pool = new ThreadPoolExecutor(threadSize, threadSize, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadPoolExecutor.CallerRunsPolicy()); } } public ResultSet executeQuery(String sql) throws SQLException { checkClosed(); closeOldResultSet(); StatementExecuteContext context = initStatementContext(sql); resultSet = createResultByContext(sql, context); return resultSet; } private ResultSet createResultByContext(String sql, StatementExecuteContext context) throws SQLException { List statements = getStatementsByContext(context); context.setStatements(statements); int statementSize = statements.size(); if (statementSize == 1) { return createSingleResultSet(sql, statements); } else if (statementSize > 1) { return createMultiResultSet(sql, context); } return null; } private StatementExecuteContext initStatementContext(String sql) throws SQLException { StatementExecuteContext context = new StatementExecuteContext(); context.setOriginalSql(sql); context.setTinyConnection(tinyConnection); context.setTinyStatement(this); context.setRouter(router); context.setBuilder(builder); Partition partition = routerManager.getPartition(router, sql); context.setPartition(partition); return context; } private ResultSet createSingleResultSet(String sql, List statements) throws SQLException { ResultSet resultSet = statements.get(0).executeQuery(); return new TinyResultSetWrapper(sql, resultSet); } /** * 存在多个分片,创建合并多个分片的结果集 * * @param sql * @param statements * @param statementSize * @return * @throws SQLException */ private ResultSet createMultiResultSet(String sql, StatementExecuteContext context) throws SQLException { List statements = context.getRealStatements(); int statementSize = statements.size(); boolean existIdleThread = existIdleThread(statementSize); List resultSetExecutors = new ArrayList(); if (existIdleThread) { resultSetExecutors = createResultsInMultiThread(sql, statements, statementSize); } else { resultSetExecutors = createResultsWithStatements(statements); } context.setResultSetExecutors(resultSetExecutors); StatementProcessor statementProcessor = context.getStatementProcessor(); if (statementProcessor != null) { return statementProcessor.combineResult(statements.get(0) .getExecuteSql(), context); } else { return new TinyResultSetMultiple(context); } } private List createResultsWithStatements( List statements) throws SQLException { List resultSetExecutors = new ArrayList(); for (RealStatementExecutor statement : statements) { ResultSet realResultSet = statement.executeQuery(); resultSetExecutors.add(new ResultSetExecutor(realResultSet, statement.getExecuteSql(), statement.getOriginalSql(), statement.getShard(), statement.getPartition(), statement .getRouter())); } return resultSetExecutors; } private List createResultsInMultiThread(String sql, List statements, int statementSize) { List resultSetExecutors = new ArrayList(); long startTime = System.currentTimeMillis(); List> futures = new ArrayList>(); for (int j = 0; j < statementSize; j++) { RealStatementExecutor realStatementExecutor = statements.get(j); ExecuteResultCallable callable = new ExecuteResultCallable( realStatementExecutor); callable.setCallBack(new ExecuteQueryCallBack()); Future future = pool.submit(callable); futures.add(future); } for (Future future : futures) { try { ResultSetExecutor resultSetExecutor = future.get(); resultSetExecutors.add(resultSetExecutor); } catch (Exception e) { logger.errorMessage("查询sql:<{0}>出错", e, sql); } } long endTime = System.currentTimeMillis(); logger.logMessage(LogLevel.DEBUG, "本次查询总执行时间:{}", endTime - startTime); return resultSetExecutors; } private boolean existIdleThread(int statementSize) { if (pool == null) { return false; } int threadSize = router.getThreadSize(); if (pool.getActiveCount() + statementSize < threadSize) { return true; } return false; } public int executeUpdate(String sql) throws SQLException { checkClosed(); closeOldResultSet(); StatementExecuteContext context = initStatementContext(sql); updateCount = executeByContext(context); return updateCount; } private int executeByContext(StatementExecuteContext context) throws SQLException { List statements = getStatementsByContext(context); int statementSize = statements.size(); if (statementSize == 1) { return statements.get(0).executeUpdate(); } else { checkExecuteStatment(context); boolean existIdleThread = existIdleThread(statementSize); if (existIdleThread) { return executeInMultiThread(statements, context.getOriginalSql()); } return executeByShards(statements); } } private int executeByShards(List statements) throws SQLException { int updateCount = 0; for (RealStatementExecutor statement : statements) { updateCount += statement.executeUpdate(); } return updateCount; } private int executeInMultiThread(List statements, String sql) { int statementSize = statements.size(); int updateCount = 0; long startTime = System.currentTimeMillis(); List> futures = new ArrayList>(); for (int j = 0; j < statementSize; j++) { RealStatementExecutor realStatementExecutor = statements.get(j); ExecuteResultCallable callable = new ExecuteResultCallable( realStatementExecutor); callable.setCallBack(new ExecuteUpdateCallBack()); Future future = pool.submit(callable); futures.add(future); } for (Future future : futures) { try { updateCount += future.get(); } catch (Exception e) { logger.errorMessage("执行sql:<{0}>出错", e, sql); } } long endTime = System.currentTimeMillis(); logger.logMessage(LogLevel.DEBUG, "本次总执行时间:{}", endTime - startTime); return updateCount; } private void checkExecuteStatment(StatementExecuteContext context) throws SQLException { if (context.getPartition().getMode() == Partition.MODE_PRIMARY_SLAVE && tinyConnection.getAutoCommit()) { throw new RuntimeException( "primary slave mode exist one more write database,the connection autocommit must set false"); } } protected Statement getStatement(Shard shard, String executeSql) throws SQLException { Statement statement = statementMap.get().get(shard); if (statement == null) { statement = shard.getConnection(tinyConnection).createStatement( resultSetType, resultSetConcurrency, getResultSetHoldability()); setStatementProperties(statement); statementMap.get().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.get().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.get().clear(); if (pool != null) { pool.shutdown(); } 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.get().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 List getPrimarySlaveShard(StatementExecuteContext context) throws SQLException { Partition partition = context.getPartition(); if (context.isRead()) { return getReadShards(partition); } return getWriteShards(partition); } private List getReadShards(Partition partition) throws SQLException { List shards = new ArrayList(); Shard shard = null; if (isTransaction()) {// 从写的列表中随机选择一个进行读取 shard = getShardWithTransaction(partition); } else { shard = getShardWithNoTransaction(partition); } shards.add(shard); return shards; } private Shard getShardWithTransaction(Partition partition) { return routerManager.getShardBalance().getReadShardWithTransaction( partition); } private Shard getShardWithNoTransaction(Partition partition) { return routerManager.getShardBalance().getReadableShard(partition); } private List getWriteShards(Partition partition) { List shards = new ArrayList(); shards.addAll(routerManager.getShardBalance().getWritableShard( partition)); return shards; } /** * 存在事务的情况 * * @return * @throws SQLException */ private boolean isTransaction() throws SQLException { return !tinyConnection.getAutoCommit(); } 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(); StatementExecuteContext context = initStatementContext(sql); List statements = getStatementsByContext(context); for (RealStatementExecutor statement : statements) { statement.addBatch(); } } /** * 根据sql获取需要执行的statements列表 * * @param sql * @return * @throws SQLException */ protected List getStatementsByContext( StatementExecuteContext context) throws SQLException { Partition partition = context.getPartition(); if (partition.getMode() == Partition.MODE_PRIMARY_SLAVE) { return statementsWithMasterSlaveMode(context); } else { return statementsByShardMode(context); } } private List statementsByShardMode( StatementExecuteContext context) throws SQLException { Partition partition = context.getPartition(); String sql = context.getOriginalSql(); List statements = new ArrayList(); String transSql = insertSpecialHandle(partition, sql);// insert语句需要特殊处理 transSql = transformSqlWithStamentProcessor(transSql, context); Collection shards = routerManager.getShards(partition, transSql, getPreparedParams()); if (shards.size() == 0) {// 如果一个都没有符合的,就匹配所有的shard。 shards = partition.getShards(); } Iterator iterator = shards.iterator(); while (iterator.hasNext()) { Shard shard = iterator.next(); String realSql = routerManager.getSql(partition, shard, transSql, getPreparedParams());// 变化表名等 Statement realStatement = getStatement(shard, realSql); statements.add(new RealStatementExecutor(realStatement, realSql, sql, shard, partition, router, getPreparedParams())); } return statements; } private String transformSqlWithStamentProcessor(String transSql, StatementExecuteContext context) { for (StatementProcessor processor : routerManager .getStatementProcessorList()) { if (processor.isMatch(transSql, getPreparedParams())) { context.setStatementProcessor(processor); return processor.getSql(transSql, context); } } return transSql; } @SuppressWarnings("rawtypes") private String insertSpecialHandle(Partition partition, String sql) throws SQLException { RouterKeyGenerator generator = router.getKeyGenerator(); String transSql = sql; if (generator != null) { org.tinygroup.jsqlparser.statement.Statement statement = RouterManagerBeanFactory .getManager().getSqlStatement(sql); if (statement instanceof Insert) { transSql = handleInsertStatement(transSql, (Insert) statement); } } return transSql; } private List statementsWithMasterSlaveMode( StatementExecuteContext context) throws SQLException { Partition partition = context.getPartition(); String sql = context.getOriginalSql(); List statements = new ArrayList(); List shards = getPrimarySlaveShard(context); for (Shard shard : shards) { String realSql = routerManager.getSql(partition, shard, sql, getPreparedParams()); Statement realStatement = getStatement(shard, realSql); statements.add(new RealStatementExecutor(realStatement, realSql, sql, shard, partition, router, getPreparedParams())); } return statements; } private String handleInsertStatement(String sql, Insert insert) throws SQLException { String transSql = sql; String tableName = insert.getTable().getName(); Cache cache = RouterManagerBeanFactory.getManager().getCache(); CacheKey cacheKey = new CacheKey(); cacheKey.update(tableName); ColumnInfo primaryColumn = null; String keyString = cacheKey.toString(); try { primaryColumn = (ColumnInfo) cache.get(keyString); } catch (Exception e) {// 如果获取缓存的时候出现异常,认为此次操作不存在缓存内容 } if (primaryColumn == null) { InsertSqlTransform transform = new InsertSqlTransform(insert,tinyConnection.getMetaData()); primaryColumn = transform.getPrimaryColumn(); if(primaryColumn==null){ return sql;//不存在主键字段,那么直接返回原来的sql }else{ cache.put(keyString, primaryColumn); } } boolean existPrimary = primaryKeyInColumns( primaryColumn.getColumnName(), insert); if (!existPrimary) { Insert newInsert = createNewInsert(insert, tableName, primaryColumn); transSql = newInsert.toString(); } return transSql; } /** * 创建拥有主键值的insert * * @param insert * @param tableName * @param primaryColumn * @return * @throws SQLException */ private Insert createNewInsert(Insert insert, String tableName, ColumnInfo primaryColumn) throws SQLException { Insert newInsert = null; try { newInsert = (Insert) DbRouterUtil.deepCopy(insert); } catch (Exception e) { throw new DbrouterRuntimeException(e); } newInsert.getColumns().add(primaryColumn.getColumn()); ItemsList itemsList = newInsert.getItemsList(); if (itemsList instanceof ExpressionList) { List expressions = ((ExpressionList) itemsList) .getExpressions(); Expression expression = createExpression(expressions.size() + 1, primaryColumn.getDataType(), tableName); expressions.add(expression); } return newInsert; } protected Expression createExpression(int paramIndex, int dataType, String tableName) throws SQLException { Expression expression = null; switch (dataType) { case Types.CHAR: case Types.VARCHAR: case Types.LONGVARCHAR: String value = RouterManagerBeanFactory.getManager().getPrimaryKey( router, tableName); StringValue stringValue = new StringValue(value); expression = stringValue; break; case Types.NUMERIC: case Types.DECIMAL: case Types.INTEGER: case Types.SMALLINT: case Types.TINYINT: case Types.BIGINT: Object values = RouterManagerBeanFactory.getManager() .getPrimaryKey(router, tableName); expression = new LongValue(values + ""); break; default: } return expression; } private boolean primaryKeyInColumns(String primaryKey, Insert insert) { List columns = insert.getColumns(); boolean exist = false; if (!CollectionUtil.isEmpty(columns)) { for (Column column : columns) { if (column.getColumnName().equalsIgnoreCase(primaryKey)) { exist = true; break; } } } return exist; } protected Object[] getPreparedParams() { return new Object[0]; } public void clearBatch() throws SQLException { checkClosed(); for (Statement statement : statementMap.get().values()) { statement.clearBatch(); } } public int[] executeBatch() throws SQLException { checkClosed(); List affectList = new ArrayList(); for (Statement statement : statementMap.get().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 { 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; } public T unwrap(Class iface) throws SQLException { return null; } public boolean isWrapperFor(Class iface) throws SQLException { return false; } public boolean isClosed() throws SQLException { return isClosed; } public void setPoolable(boolean poolable) throws SQLException { } public boolean isPoolable() throws SQLException { return false; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy