diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/PreparedStatementExecutor.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/PreparedStatementExecutor.java index 00376c1dd8b9f..3da52d273279e 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/PreparedStatementExecutor.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/PreparedStatementExecutor.java @@ -23,6 +23,7 @@ import com.dangdang.ddframe.rdb.sharding.executor.event.DQLExecutionEvent; import com.dangdang.ddframe.rdb.sharding.executor.event.DQLExecutionEventBus; import com.dangdang.ddframe.rdb.sharding.executor.event.EventExecutionType; +import com.dangdang.ddframe.rdb.sharding.executor.wrapper.BatchPreparedStatementExecutorWrapper; import com.dangdang.ddframe.rdb.sharding.executor.wrapper.PreparedStatementExecutorWrapper; import com.dangdang.ddframe.rdb.sharding.metrics.MetricsContext; import com.google.common.base.Optional; @@ -194,6 +195,80 @@ private boolean executeInternal(final PreparedStatementExecutorWrapper preparedS return result; } + + /** + * 执行批量接口. + * + * @return 每个 + */ + public int[] executeBatch() { + Context context = MetricsContext.start("ShardingPreparedStatement-executeUpdate"); + postExecutionEvents(); + final boolean isExceptionThrown = ExecutorExceptionHandler.isExceptionThrown(); + final Map dataMap = ExecutorDataMap.getDataMap(); + try { + if (1 == preparedStatementExecutorWrappers.size()) { + return executeBatchInternal((BatchPreparedStatementExecutorWrapper) preparedStatementExecutorWrappers.iterator().next(), isExceptionThrown, dataMap); + } + return executorEngine.execute(preparedStatementExecutorWrappers, new ExecuteUnit() { + + @Override + public int[] execute(final PreparedStatementExecutorWrapper input) throws Exception { + return executeBatchInternal((BatchPreparedStatementExecutorWrapper) input, isExceptionThrown, dataMap); + } + }, new MergeUnit() { + + @Override + public int[] merge(final List results) { + if (null == results) { + return new int[]{0}; + } + int length = 0; + for (int[] array : results) { + length += array.length; + } + int[] result = new int[length]; + int pos = 0; + for (int[] array : results) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + }); + } finally { + MetricsContext.stop(context); + } + } + + private int[] executeBatchInternal(final BatchPreparedStatementExecutorWrapper batchPreparedStatementExecutorWrapper, final boolean isExceptionThrown, final Map dataMap) { + int[] result; + ExecutorExceptionHandler.setExceptionThrown(isExceptionThrown); + ExecutorDataMap.setDataMap(dataMap); + try { + result = batchPreparedStatementExecutorWrapper.getPreparedStatement().executeBatch(); + } catch (final SQLException ex) { + postBatchExecutionEventsAfterExecution(batchPreparedStatementExecutorWrapper, EventExecutionType.EXECUTE_FAILURE, Optional.of(ex)); + ExecutorExceptionHandler.handleException(ex); + return null; + } + postBatchExecutionEventsAfterExecution(batchPreparedStatementExecutorWrapper); + return result; + } + + private void postBatchExecutionEventsAfterExecution(final BatchPreparedStatementExecutorWrapper batchPreparedStatementExecutorWrapper) { + postBatchExecutionEventsAfterExecution(batchPreparedStatementExecutorWrapper, EventExecutionType.EXECUTE_SUCCESS, Optional.absent()); + } + + private void postBatchExecutionEventsAfterExecution( + final BatchPreparedStatementExecutorWrapper batchPreparedStatementExecutorWrapper, final EventExecutionType eventExecutionType, final Optional exp) { + for (DMLExecutionEvent each : batchPreparedStatementExecutorWrapper.getDmlExecutionEvents()) { + each.setEventExecutionType(eventExecutionType); + each.setExp(exp); + DMLExecutionEventBus.post(each); + } + } + private void postExecutionEvents() { for (PreparedStatementExecutorWrapper each : preparedStatementExecutorWrappers) { if (each.getDMLExecutionEvent().isPresent()) { diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/BatchPreparedStatementExecutorWrapper.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/BatchPreparedStatementExecutorWrapper.java new file mode 100644 index 0000000000000..31ce953ddc6e1 --- /dev/null +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/BatchPreparedStatementExecutorWrapper.java @@ -0,0 +1,42 @@ +/* + * Copyright 1999-2015 dangdang.com. + *

+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + *

+ */ + +package com.dangdang.ddframe.rdb.sharding.executor.wrapper; + +import com.dangdang.ddframe.rdb.sharding.executor.event.DMLExecutionEvent; +import com.dangdang.ddframe.rdb.sharding.router.SQLExecutionUnit; +import lombok.Getter; + +import java.sql.PreparedStatement; +import java.util.LinkedList; +import java.util.List; + +/** + * 批量操作执行上下文. + * 批量操作只支持DML语句,故只包含DML操作的事件. + * + * @author gaohongtao + */ +@Getter +public class BatchPreparedStatementExecutorWrapper extends PreparedStatementExecutorWrapper { + + private final List dmlExecutionEvents = new LinkedList<>(); + + public BatchPreparedStatementExecutorWrapper(final PreparedStatement preparedStatement, final List parameters, final SQLExecutionUnit sqlExecutionUnit) { + super(preparedStatement, parameters, sqlExecutionUnit); + } +} diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/PreparedStatementExecutorWrapper.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/PreparedStatementExecutorWrapper.java index a5930f4090bea..3a486705326a0 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/PreparedStatementExecutorWrapper.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/executor/wrapper/PreparedStatementExecutorWrapper.java @@ -21,6 +21,7 @@ import com.dangdang.ddframe.rdb.sharding.executor.event.DQLExecutionEvent; import com.dangdang.ddframe.rdb.sharding.router.SQLExecutionUnit; import com.google.common.base.Optional; +import com.google.common.collect.Lists; import lombok.Getter; import java.sql.PreparedStatement; @@ -31,7 +32,7 @@ * * @author zhangliang */ -public final class PreparedStatementExecutorWrapper extends AbstractExecutorWrapper { +public class PreparedStatementExecutorWrapper extends AbstractExecutorWrapper { @Getter private final PreparedStatement preparedStatement; @@ -45,10 +46,10 @@ public PreparedStatementExecutorWrapper(final PreparedStatement preparedStatemen super(sqlExecutionUnit); this.preparedStatement = preparedStatement; if (isDML()) { - dmlExecutionEvent = Optional.of(new DMLExecutionEvent(getSqlExecutionUnit().getDataSource(), getSqlExecutionUnit().getSql(), parameters)); + dmlExecutionEvent = Optional.of(new DMLExecutionEvent(getSqlExecutionUnit().getDataSource(), getSqlExecutionUnit().getSql(), Lists.newArrayList(parameters))); dqlExecutionEvent = Optional.absent(); } else if (isDQL()) { - dqlExecutionEvent = Optional.of(new DQLExecutionEvent(getSqlExecutionUnit().getDataSource(), getSqlExecutionUnit().getSql(), parameters)); + dqlExecutionEvent = Optional.of(new DQLExecutionEvent(getSqlExecutionUnit().getDataSource(), getSqlExecutionUnit().getSql(), Lists.newArrayList(parameters))); dmlExecutionEvent = Optional.absent(); } else { dmlExecutionEvent = Optional.absent(); diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatement.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatement.java index f26602689ca55..4bbe4fe6e7380 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatement.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatement.java @@ -18,24 +18,23 @@ package com.dangdang.ddframe.rdb.sharding.jdbc; import com.dangdang.ddframe.rdb.sharding.executor.PreparedStatementExecutor; +import com.dangdang.ddframe.rdb.sharding.executor.wrapper.BatchPreparedStatementExecutorWrapper; import com.dangdang.ddframe.rdb.sharding.executor.wrapper.PreparedStatementExecutorWrapper; import com.dangdang.ddframe.rdb.sharding.jdbc.adapter.AbstractPreparedStatementAdapter; import com.dangdang.ddframe.rdb.sharding.merger.ResultSetFactory; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.MergeContext; +import com.dangdang.ddframe.rdb.sharding.router.PreparedSQLRouter; import com.dangdang.ddframe.rdb.sharding.router.SQLExecutionUnit; import com.dangdang.ddframe.rdb.sharding.router.SQLRouteResult; -import com.google.common.base.Function; -import com.google.common.collect.Lists; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import java.sql.Statement; import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * 支持分片的预编译语句对象. @@ -45,9 +44,9 @@ */ public final class ShardingPreparedStatement extends AbstractPreparedStatementAdapter { - private final String sql; + private final PreparedSQLRouter preparedSQLRouter; - private final List cachedRoutedPreparedStatements = new LinkedList<>(); + private final Map cachedRoutePreparedStatementMap = new HashMap<>(); private Integer autoGeneratedKeys; @@ -55,10 +54,6 @@ public final class ShardingPreparedStatement extends AbstractPreparedStatementAd private String[] columnNames; - private boolean hasExecuted; - - private final List> batchParameters = new ArrayList<>(); - public ShardingPreparedStatement(final ShardingConnection shardingConnection, final String sql) throws SQLException { this(shardingConnection, sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY, ResultSet.HOLD_CURSORS_OVER_COMMIT); } @@ -69,9 +64,9 @@ public ShardingPreparedStatement(final ShardingConnection shardingConnection, } public ShardingPreparedStatement(final ShardingConnection shardingConnection, - final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) throws SQLException { + final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability) { super(shardingConnection, resultSetType, resultSetConcurrency, resultSetHoldability); - this.sql = sql; + preparedSQLRouter = shardingConnection.getShardingContext().getSqlRouteEngine().prepareSQL(sql); } public ShardingPreparedStatement(final ShardingConnection shardingConnection, final String sql, final int autoGeneratedKeys) throws SQLException { @@ -91,82 +86,84 @@ public ShardingPreparedStatement(final ShardingConnection shardingConnection, fi @Override public ResultSet executeQuery() throws SQLException { - hasExecuted = true; - setCurrentResultSet(ResultSetFactory.getResultSet( - new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), getRoutedPreparedStatements()).executeQuery(), getMergeContext())); - return getCurrentResultSet(); + ResultSet rs; + try { + rs = ResultSetFactory.getResultSet( + new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), routeSQL()).executeQuery(), getMergeContext()); + } finally { + clearRouteContext(); + } + setCurrentResultSet(rs); + return rs; } @Override public int executeUpdate() throws SQLException { - hasExecuted = true; - return new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), getRoutedPreparedStatements()).executeUpdate(); + try { + return new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), routeSQL()).executeUpdate(); + } finally { + clearRouteContext(); + } } @Override public boolean execute() throws SQLException { - hasExecuted = true; - return new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), getRoutedPreparedStatements()).execute(); + try { + return new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), routeSQL()).execute(); + } finally { + clearRouteContext(); + } + } + + private void clearRouteContext() throws SQLException { + clearParameters(); + setCurrentResultSet(null); } @Override public void addBatch() throws SQLException { - batchParameters.add(Lists.newArrayList(getParameters())); - getParameters().clear(); + try { + for (PreparedStatementExecutorWrapper each : routeSQL()) { + each.getPreparedStatement().addBatch(); + BatchPreparedStatementExecutorWrapper wrapper; + if (cachedRoutePreparedStatementMap.containsKey(each.getPreparedStatement())) { + wrapper = (BatchPreparedStatementExecutorWrapper) cachedRoutePreparedStatementMap.get(each.getPreparedStatement()); + } else { + wrapper = new BatchPreparedStatementExecutorWrapper(each.getPreparedStatement(), getParameters(), each.getSqlExecutionUnit()); + cachedRoutePreparedStatementMap.put(each.getPreparedStatement(), wrapper); + } + if (each.getDMLExecutionEvent().isPresent()) { + wrapper.getDmlExecutionEvents().add(each.getDMLExecutionEvent().get()); + } + } + } finally { + clearRouteContext(); + } } @Override public void clearBatch() throws SQLException { - batchParameters.clear(); + cachedRoutePreparedStatementMap.clear(); + clearRouteContext(); } @Override public int[] executeBatch() throws SQLException { - hasExecuted = true; - int[] result = new int[batchParameters.size()]; - int i = 0; - for (List each : batchParameters) { - List routePreparedStatements = routeSQL(each); - cachedRoutedPreparedStatements.addAll(routePreparedStatements); - result[i++] = new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), routePreparedStatements).executeUpdate(); - } - return result; - } - - private List getRoutedPreparedStatements() throws SQLException { - if (!hasExecuted) { - return Collections.emptyList(); - } - routeIfNeed(); - return cachedRoutedPreparedStatements; - } - - - @Override - public List getRoutedStatements() throws SQLException { - return Lists.transform(getRoutedPreparedStatements(), new Function() { - - @Override - public Statement apply(final PreparedStatementExecutorWrapper input) { - return input.getPreparedStatement(); - } - }); - } - - private void routeIfNeed() throws SQLException { - if (!cachedRoutedPreparedStatements.isEmpty()) { - return; + try { + return new PreparedStatementExecutor(getShardingConnection().getShardingContext().getExecutorEngine(), cachedRoutePreparedStatementMap.values()).executeBatch(); + } finally { + clearBatch(); } - cachedRoutedPreparedStatements.addAll(routeSQL(getParameters())); } - private List routeSQL(final List parameters) throws SQLException { + private List routeSQL() throws SQLException { + List parameters = getParameters(); List result = new ArrayList<>(); - SQLRouteResult sqlRouteResult = getShardingConnection().getShardingContext().getSqlRouteEngine().route(sql, parameters); + SQLRouteResult sqlRouteResult = preparedSQLRouter.route(parameters); MergeContext mergeContext = sqlRouteResult.getMergeContext(); setMergeContext(mergeContext); for (SQLExecutionUnit each : sqlRouteResult.getExecutionUnits()) { - PreparedStatement preparedStatement = generatePrepareStatement(getShardingConnection().getConnection(each.getDataSource(), sqlRouteResult.getSqlStatementType()), each.getSql()); + PreparedStatement preparedStatement = (PreparedStatement) getStatement(getShardingConnection().getConnection(each.getDataSource(), sqlRouteResult.getSqlStatementType()), each.getSql()); replayMethodsInvocation(preparedStatement); setParameters(preparedStatement, parameters); result.add(new PreparedStatementExecutorWrapper(preparedStatement, parameters, each)); @@ -174,7 +171,7 @@ private List routeSQL(final List param return result; } - private PreparedStatement generatePrepareStatement(final Connection conn, final String shardingSql) throws SQLException { + protected PreparedStatement generateStatement(final Connection conn, final String shardingSql) throws SQLException { if (null != autoGeneratedKeys) { return conn.prepareStatement(shardingSql, autoGeneratedKeys); } diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingStatement.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingStatement.java index 795604bcde5eb..97f64fae71e37 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingStatement.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingStatement.java @@ -22,7 +22,6 @@ import com.dangdang.ddframe.rdb.sharding.jdbc.adapter.AbstractStatementAdapter; import com.dangdang.ddframe.rdb.sharding.merger.ResultSetFactory; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.MergeContext; -import com.dangdang.ddframe.rdb.sharding.parser.result.router.SQLStatementType; import com.dangdang.ddframe.rdb.sharding.router.SQLExecutionUnit; import com.dangdang.ddframe.rdb.sharding.router.SQLRouteResult; import com.google.common.base.Charsets; @@ -62,13 +61,13 @@ public class ShardingStatement extends AbstractStatementAdapter { @Getter private final int resultSetHoldability; + @Getter(AccessLevel.PROTECTED) private final Map cachedRoutedStatements = new HashMap<>(); @Getter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED) private MergeContext mergeContext; - @Getter(AccessLevel.PROTECTED) @Setter(AccessLevel.PROTECTED) private ResultSet currentResultSet; @@ -147,25 +146,30 @@ private StatementExecutor generateExecutor(final String sql) throws SQLException SQLRouteResult sqlRouteResult = shardingConnection.getShardingContext().getSqlRouteEngine().route(sql); mergeContext = sqlRouteResult.getMergeContext(); for (SQLExecutionUnit each : sqlRouteResult.getExecutionUnits()) { - result.addStatement(new StatementExecutorWrapper(generateStatement(each.getSql(), each.getDataSource(), sqlRouteResult.getSqlStatementType()), each)); + Statement statement = getStatement(shardingConnection.getConnection(each.getDataSource(), sqlRouteResult.getSqlStatementType()), each.getSql()); + replayMethodsInvocation(statement); + result.addStatement(new StatementExecutorWrapper(statement, each)); } return result; } - private Statement generateStatement(final String sql, final String dataSourceName, final SQLStatementType sqlStatementType) throws SQLException { - HashCode hashCode = Hashing.md5().newHasher().putString(sql, Charsets.UTF_8).putString(dataSourceName, Charsets.UTF_8).hash(); + protected Statement getStatement(final Connection connection, final String sql) throws SQLException { + HashCode hashCode = Hashing.md5().newHasher().putInt(connection.hashCode()).putString(sql, Charsets.UTF_8).hash(); if (cachedRoutedStatements.containsKey(hashCode)) { return cachedRoutedStatements.get(hashCode); } - Connection connection = shardingConnection.getConnection(dataSourceName, sqlStatementType); + Statement statement = generateStatement(connection, sql); + cachedRoutedStatements.put(hashCode, statement); + return statement; + } + + protected Statement generateStatement(final Connection connection, final String sql) throws SQLException { Statement result; if (0 == resultSetHoldability) { result = connection.createStatement(resultSetType, resultSetConcurrency); } else { result = connection.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); } - replayMethodsInvocation(result); - cachedRoutedStatements.put(hashCode, result); return result; } diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/Condition.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/Condition.java index 17fd30545fb87..587f9921095a0 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/Condition.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/Condition.java @@ -17,14 +17,14 @@ package com.dangdang.ddframe.rdb.sharding.parser.result.router; -import java.util.ArrayList; -import java.util.List; - import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; +import java.util.ArrayList; +import java.util.List; + /** * 条件对象. * @@ -42,6 +42,8 @@ public final class Condition { private final List> values = new ArrayList<>(); + private final List valueIndices = new ArrayList<>(); + /** * 列对象. * diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/ConditionContext.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/ConditionContext.java index 04fb525041a05..565d08c649bda 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/ConditionContext.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/result/router/ConditionContext.java @@ -17,16 +17,16 @@ package com.dangdang.ddframe.rdb.sharding.parser.result.router; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; - import com.dangdang.ddframe.rdb.sharding.parser.result.router.Condition.BinaryOperator; import com.dangdang.ddframe.rdb.sharding.parser.result.router.Condition.Column; import com.google.common.base.Optional; - import lombok.ToString; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + /** * 条件对象上下文. * @@ -85,4 +85,20 @@ public void clear() { public Collection getAllConditions() { return conditions.values(); } + + public void setNewConditionValue(final List parameters) { + for (Condition each : conditions.values()) { + if (each.getValueIndices().isEmpty()) { + continue; + } + for (int i = 0; i < each.getValueIndices().size(); i++) { + Object value = parameters.get(each.getValueIndices().get(i)); + if (value instanceof Comparable) { + each.getValues().set(i, (Comparable) value); + } else { + each.getValues().set(i, ""); + } + } + } + } } diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/ParseContext.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/ParseContext.java index 07cc625490f0d..d3e8679ff27b1 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/ParseContext.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/ParseContext.java @@ -24,7 +24,9 @@ import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr; import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr; import com.alibaba.druid.sql.ast.statement.SQLExprTableSource; +import com.alibaba.druid.sql.visitor.SQLEvalVisitor; import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; +import com.alibaba.druid.util.JdbcUtils; import com.dangdang.ddframe.rdb.sharding.constants.DatabaseType; import com.dangdang.ddframe.rdb.sharding.parser.result.SQLParsedResult; import com.dangdang.ddframe.rdb.sharding.parser.result.merger.AggregationColumn; @@ -37,9 +39,11 @@ import com.dangdang.ddframe.rdb.sharding.parser.result.router.Condition.Column; import com.dangdang.ddframe.rdb.sharding.parser.result.router.ConditionContext; import com.dangdang.ddframe.rdb.sharding.parser.result.router.Table; +import com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql.MySQLEvalVisitor; import com.dangdang.ddframe.rdb.sharding.util.SQLUtil; import com.google.common.base.Optional; import lombok.Getter; +import lombok.RequiredArgsConstructor; import lombok.Setter; import java.util.ArrayList; @@ -136,9 +140,9 @@ public void addCondition(final SQLExpr expr, final BinaryOperator operator, fina if (!column.isPresent() || !shardingColumns.contains(column.get().getColumnName())) { return; } - List> values = new ArrayList<>(valueExprList.size()); + List values = new ArrayList<>(valueExprList.size()); for (SQLExpr each : valueExprList) { - Comparable evalValue = evalExpression(databaseType, each, parameters); + ValuePair evalValue = evalExpression(databaseType, each, parameters); if (null != evalValue) { values.add(evalValue); } @@ -164,13 +168,13 @@ public void addCondition(final String columnName, final String tableName, final if (!shardingColumns.contains(column.getColumnName())) { return; } - Comparable value = evalExpression(databaseType, valueExpr, parameters); + ValuePair value = evalExpression(databaseType, valueExpr, parameters); if (null != value) { - addCondition(column, operator, Collections.>singletonList(value)); + addCondition(column, operator, Collections.singletonList(value)); } } - private void addCondition(final Column column, final BinaryOperator operator, final List> values) { + private void addCondition(final Column column, final BinaryOperator operator, final List valuePairs) { Optional optionalCondition = currentConditionContext.find(column.getTableName(), column.getColumnName(), operator); Condition condition; // TODO 待讨论 @@ -180,23 +184,48 @@ private void addCondition(final Column column, final BinaryOperator operator, fi condition = new Condition(column, operator); currentConditionContext.add(condition); } - condition.getValues().addAll(values); + for (ValuePair each : valuePairs) { + condition.getValues().add(each.value); + if (each.paramIndex > -1) { + condition.getValueIndices().add(each.paramIndex); + } + } } - private Comparable evalExpression(final DatabaseType databaseType, final SQLObject sqlObject, final List parameters) { + private ValuePair evalExpression(final DatabaseType databaseType, final SQLObject sqlObject, final List parameters) { if (sqlObject instanceof SQLMethodInvokeExpr) { // TODO 解析函数中的sharingValue不支持 return null; } - Object result = SQLEvalVisitorUtils.eval(databaseType.name().toLowerCase(), sqlObject, parameters, false); - if (null == result) { + SQLEvalVisitor visitor; + switch (databaseType.name().toLowerCase()) { + case JdbcUtils.MYSQL: + case JdbcUtils.H2: + visitor = new MySQLEvalVisitor(); + break; + default: + visitor = SQLEvalVisitorUtils.createEvalVisitor(databaseType.name()); + } + visitor.setParameters(parameters); + sqlObject.accept(visitor); + + Object value = SQLEvalVisitorUtils.getValue(sqlObject); + if (null == value) { + // TODO 对于NULL目前解析为空字符串,此处待考虑解决方法 return null; } - if (result instanceof Comparable) { - return (Comparable) result; + + Comparable finalValue; + if (value instanceof Comparable) { + finalValue = (Comparable) value; + } else { + finalValue = ""; } - // TODO 对于NULL目前解析为空字符串,此处待考虑解决方法 - return ""; + Integer index = (Integer) sqlObject.getAttribute(MySQLEvalVisitor.EVAL_VAR_INDEX); + if (null == index) { + index = -1; + } + return new ValuePair(finalValue, index); } private Optional getColumn(final SQLExpr expr) { @@ -375,4 +404,12 @@ public void registerSelectItem(final String selectItem) { } selectItems.add(rawItemExpr); } + + @RequiredArgsConstructor + private static class ValuePair { + + private final Comparable value; + + private final Integer paramIndex; + } } diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitor.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitor.java new file mode 100644 index 0000000000000..c0d1493c236e4 --- /dev/null +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitor.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2015 dangdang.com. + *

+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + *

+ */ + +package com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql; + +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlEvalVisitorImpl; + +import java.util.Map; + +/** + * MySQL变量中提取参数值与编号. + * + * @author gaohongtao. + */ +public class MySQLEvalVisitor extends MySqlEvalVisitorImpl { + + public static final String EVAL_VAR_INDEX = "EVAL_VAR_INDEX"; + + @Override + public boolean visit(final SQLVariantRefExpr x) { + if (!"?".equals(x.getName())) { + return false; + } + + Map attributes = x.getAttributes(); + + int varIndex = x.getIndex(); + + if (varIndex == -1 || getParameters().size() <= varIndex) { + return false; + } + if (attributes.containsKey(EVAL_VALUE)) { + return false; + } + Object value = getParameters().get(varIndex); + if (value == null) { + value = EVAL_VALUE_NULL; + } + attributes.put(EVAL_VALUE, value); + attributes.put(EVAL_VAR_INDEX, varIndex); + return false; + } +} diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/PreparedSQLRouter.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/PreparedSQLRouter.java new file mode 100644 index 0000000000000..e0a584ccf4f5c --- /dev/null +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/PreparedSQLRouter.java @@ -0,0 +1,58 @@ +/* + * Copyright 1999-2015 dangdang.com. + *

+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + *

+ */ + +package com.dangdang.ddframe.rdb.sharding.router; + +import com.dangdang.ddframe.rdb.sharding.parser.result.SQLParsedResult; +import com.dangdang.ddframe.rdb.sharding.parser.result.router.ConditionContext; +import lombok.RequiredArgsConstructor; + +import java.util.List; + +/** + * 预解析功能的SQL路由器. + * + * @author gaohongtao + */ +@RequiredArgsConstructor +public class PreparedSQLRouter { + + private final String logicSql; + + private final SQLRouteEngine engine; + + private SQLParsedResult sqlParsedResult; + + /** + * 使用参数进行SQL路由. + * 当第一次路由时进行SQL解析,之后的路由复用第一次的解析结果. + * + * @param parameters SQL中的参数 + * @return 路由结果 + */ + public SQLRouteResult route(final List parameters) { + if (null == sqlParsedResult) { + sqlParsedResult = engine.parseSQL(logicSql, parameters); + } else { + for (ConditionContext each : sqlParsedResult.getConditionContexts()) { + each.setNewConditionValue(parameters); + } + } + return engine.routeSQL(sqlParsedResult); + } +} + diff --git a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/SQLRouteEngine.java b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/SQLRouteEngine.java index 0e6aac21e5dd8..db6644fe617d1 100644 --- a/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/SQLRouteEngine.java +++ b/sharding-jdbc-core/src/main/java/com/dangdang/ddframe/rdb/sharding/router/SQLRouteEngine.java @@ -80,14 +80,24 @@ public SQLRouteResult route(final String logicSql, final List parameters return routeSQL(parseSQL(logicSql, parameters)); } - private SQLParsedResult parseSQL(final String logicSql, final List parameters) { + /** + * 预解析SQL路由. + * + * @param logicSql 逻辑SQL + * @return 预解析SQL路由器 + */ + public PreparedSQLRouter prepareSQL(final String logicSql) { + return new PreparedSQLRouter(logicSql, this); + } + + SQLParsedResult parseSQL(final String logicSql, final List parameters) { Context context = MetricsContext.start("Parse SQL"); SQLParsedResult result = SQLParserFactory.create(databaseType, logicSql, parameters, shardingRule.getAllShardingColumns()).parse(); MetricsContext.stop(context); return result; } - private SQLRouteResult routeSQL(final SQLParsedResult parsedResult) { + SQLRouteResult routeSQL(final SQLParsedResult parsedResult) { Context context = MetricsContext.start("Route SQL"); SQLRouteResult result = new SQLRouteResult(parsedResult.getRouteContext().getSqlStatementType(), parsedResult.getMergeContext()); for (ConditionContext each : parsedResult.getConditionContexts()) { diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/AbstractDBUnitTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/AbstractDBUnitTest.java index 6204dfca6449a..e680e45714dfc 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/AbstractDBUnitTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/AbstractDBUnitTest.java @@ -17,17 +17,6 @@ package com.dangdang.ddframe.rdb.integrate; -import java.io.File; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.sql.DataSource; - import com.dangdang.ddframe.rdb.sharding.constants.DatabaseType; import org.apache.commons.dbcp.BasicDataSource; import org.dbunit.DatabaseUnitException; @@ -42,13 +31,24 @@ import org.h2.tools.RunScript; import org.junit.Before; +import javax.sql.DataSource; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import static org.dbunit.Assertion.assertEquals; public abstract class AbstractDBUnitTest { protected static final DatabaseType CURRENT_DB_TYPE = DatabaseType.H2; - private static final Map DATA_SOURCES = new HashMap<>(); + protected static final Map DATA_SOURCES = new HashMap<>(); private final DataBaseEnvironment dbEnv = new DataBaseEnvironment(CURRENT_DB_TYPE); diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/common/statement/AbstractShardingBothForStatementWithDMLTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/common/statement/AbstractShardingBothForStatementWithDMLTest.java index 43ffc5cacd23d..d9d35d73ef51b 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/common/statement/AbstractShardingBothForStatementWithDMLTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/common/statement/AbstractShardingBothForStatementWithDMLTest.java @@ -77,7 +77,7 @@ public void assertDelete() throws SQLException, DatabaseUnitException { for (int i = 10; i < 20; i++) { for (int j = 0; j < 10; j++) { try (Connection connection = shardingDataSource.getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertThat(stmt.executeUpdate(String.format(sql, i * 100 + j, i)), is(1)); } } diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/statically/statement/StaticShardingBothForStatementWithDMLTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/statically/statement/StaticShardingBothForStatementWithDMLTest.java index 7a0464b679fe5..6e1ac091c84ea 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/statically/statement/StaticShardingBothForStatementWithDMLTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/dbtbl/statically/statement/StaticShardingBothForStatementWithDMLTest.java @@ -41,7 +41,7 @@ protected ShardingDataSource getShardingDataSource() { public void assertUpdateWithoutShardingValue() throws SQLException, DatabaseUnitException { String sql = "UPDATE `t_order` SET `status` = '%s' WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertThat(stmt.executeUpdate(String.format(sql, "updated", "init")), is(100)); } assertDataSet("update", "updated"); @@ -51,7 +51,7 @@ public void assertUpdateWithoutShardingValue() throws SQLException, DatabaseUnit public void assertDeleteWithoutShardingValue() throws SQLException, DatabaseUnitException { String sql = "DELETE `t_order` WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertThat(stmt.executeUpdate(String.format(sql, "init")), is(100)); } assertDataSet("delete", "init"); diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/masterslave/statement/ShardingMasterSlaveForStatementWithDMLTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/masterslave/statement/ShardingMasterSlaveForStatementWithDMLTest.java index 25e216851ecd4..6f4dfdd17722b 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/masterslave/statement/ShardingMasterSlaveForStatementWithDMLTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/integrate/masterslave/statement/ShardingMasterSlaveForStatementWithDMLTest.java @@ -47,7 +47,7 @@ public void assertUpdateWithoutShardingValue() throws SQLException, DatabaseUnit assertSelectBeforeUpdate(); String sql = "UPDATE `t_order` SET `status` = '%s' WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertThat(stmt.executeUpdate(String.format(sql, "updated", "init_master")), is(100)); } assertDataSet("update", "updated"); @@ -57,7 +57,7 @@ public void assertUpdateWithoutShardingValue() throws SQLException, DatabaseUnit private void assertSelectBeforeUpdate() throws SQLException, DatabaseUnitException { String sql = "SELECT * FROM `t_order` WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertFalse(stmt.executeQuery(String.format(sql, "updated")).next()); } } @@ -65,7 +65,7 @@ private void assertSelectBeforeUpdate() throws SQLException, DatabaseUnitExcepti private void assertSelectAfterUpdate() throws SQLException, DatabaseUnitException { String sql = "SELECT * FROM `t_order` WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertTrue(stmt.executeQuery(String.format(sql, "updated")).next()); } } @@ -74,7 +74,7 @@ private void assertSelectAfterUpdate() throws SQLException, DatabaseUnitExceptio public void assertDeleteWithoutShardingValue() throws SQLException, DatabaseUnitException { String sql = "DELETE `t_order` WHERE `status` = '%s'"; try (Connection connection = getShardingDataSource().getConnection()) { - Statement stmt = connection.prepareStatement(sql); + Statement stmt = connection.createStatement(); assertThat(stmt.executeUpdate(String.format(sql, "init_master")), is(100)); } assertDataSet("delete", "init"); diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatementTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatementTest.java index 8e84ff560dcbf..53d081bc821be 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatementTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/jdbc/ShardingPreparedStatementTest.java @@ -17,21 +17,20 @@ package com.dangdang.ddframe.rdb.sharding.jdbc; -import static org.hamcrest.CoreMatchers.is; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; +import com.dangdang.ddframe.rdb.integrate.db.AbstractShardingDataBasesOnlyDBUnitTest; +import com.mysql.jdbc.Statement; +import org.junit.Before; +import org.junit.Test; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; -import org.junit.Before; -import org.junit.Test; - -import com.dangdang.ddframe.rdb.integrate.db.AbstractShardingDataBasesOnlyDBUnitTest; -import com.mysql.jdbc.Statement; +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; public final class ShardingPreparedStatementTest extends AbstractShardingDataBasesOnlyDBUnitTest { @@ -52,6 +51,14 @@ public void assertExecuteQueryWithParameter() throws SQLException { ResultSet resultSet = preparedStatement.executeQuery(); assertTrue(resultSet.next()); assertThat(resultSet.getLong(1), is(40L)); + preparedStatement.setString(1, "null"); + resultSet = preparedStatement.executeQuery(); + assertTrue(resultSet.next()); + assertThat(resultSet.getLong(1), is(0L)); + preparedStatement.setString(1, "init"); + resultSet = preparedStatement.executeQuery(); + assertTrue(resultSet.next()); + assertThat(resultSet.getLong(1), is(40L)); } } @@ -64,6 +71,12 @@ public void assertExecuteQueryWithoutParameter() throws SQLException { ResultSet resultSet = preparedStatement.executeQuery(); assertTrue(resultSet.next()); assertThat(resultSet.getLong(1), is(40L)); + resultSet = preparedStatement.executeQuery(); + assertTrue(resultSet.next()); + assertThat(resultSet.getLong(1), is(40L)); + resultSet = preparedStatement.executeQuery(); + assertTrue(resultSet.next()); + assertThat(resultSet.getLong(1), is(40L)); } } @@ -75,6 +88,10 @@ public void assertExecuteUpdateWithParameter() throws SQLException { PreparedStatement preparedStatement = connection.prepareStatement(sql)) { preparedStatement.setString(1, "init"); assertThat(preparedStatement.executeUpdate(), is(40)); + preparedStatement.setString(1, "null"); + assertThat(preparedStatement.executeUpdate(), is(0)); + preparedStatement.setString(1, "init"); + assertThat(preparedStatement.executeUpdate(), is(0)); } } @@ -85,6 +102,8 @@ public void assertExecuteUpdateWithoutParameter() throws SQLException { Connection connection = shardingDataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { assertThat(preparedStatement.executeUpdate(), is(40)); + assertThat(preparedStatement.executeUpdate(), is(0)); + assertThat(preparedStatement.executeUpdate(), is(0)); } } @@ -98,6 +117,14 @@ public void assertExecuteWithParameter() throws SQLException { assertTrue(preparedStatement.execute()); assertTrue(preparedStatement.getResultSet().next()); assertThat(preparedStatement.getResultSet().getLong(1), is(40L)); + preparedStatement.setString(1, "null"); + assertTrue(preparedStatement.execute()); + assertTrue(preparedStatement.getResultSet().next()); + assertThat(preparedStatement.getResultSet().getLong(1), is(0L)); + preparedStatement.setString(1, "init"); + assertTrue(preparedStatement.execute()); + assertTrue(preparedStatement.getResultSet().next()); + assertThat(preparedStatement.getResultSet().getLong(1), is(40L)); } } @@ -108,6 +135,8 @@ public void assertExecuteWithoutParameter() throws SQLException { Connection connection = shardingDataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql)) { assertFalse(preparedStatement.execute()); + assertFalse(preparedStatement.execute()); + assertFalse(preparedStatement.execute()); } } @@ -203,6 +232,14 @@ public void assertAddBatch() throws SQLException { preparedStatement.setInt(2, 12); preparedStatement.setString(3, "BATCH"); preparedStatement.addBatch(); + preparedStatement.setInt(1, 3111); + preparedStatement.setInt(2, 21); + preparedStatement.setString(3, "BATCH"); + preparedStatement.addBatch(); + preparedStatement.setInt(1, 3112); + preparedStatement.setInt(2, 22); + preparedStatement.setString(3, "BATCH"); + preparedStatement.addBatch(); int[] result = preparedStatement.executeBatch(); for (int each : result) { assertThat(each, is(1)); @@ -210,7 +247,7 @@ public void assertAddBatch() throws SQLException { } } - @Test(expected = IllegalStateException.class) + @Test public void assertClearBatch() throws SQLException { String sql = "INSERT INTO `t_order`(`order_id`, `user_id`, `status`) VALUES (?,?,?)"; try ( @@ -221,7 +258,8 @@ public void assertClearBatch() throws SQLException { preparedStatement.setString(3, "BATCH"); preparedStatement.addBatch(); preparedStatement.clearBatch(); - preparedStatement.executeBatch(); + int[] result = preparedStatement.executeBatch(); + assertThat(result.length, is(0)); } } } diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AbstractBaseParseTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AbstractBaseParseTest.java index 2dcba615e699b..a434936a353c1 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AbstractBaseParseTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AbstractBaseParseTest.java @@ -146,6 +146,9 @@ public Comparable apply(final Value input) { return input.getValueWithType(); } })); + if (null != each.getValueIndices()) { + condition.getValueIndices().addAll(each.getValueIndices()); + } result.add(condition); } return result; diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AllParserTests.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AllParserTests.java index 240e8cd79c49a..9415115a776f8 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AllParserTests.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/AllParserTests.java @@ -22,6 +22,7 @@ import com.dangdang.ddframe.rdb.sharding.parser.mysql.MySQLStatementTest; import com.dangdang.ddframe.rdb.sharding.parser.mysql.OrParseTest; import com.dangdang.ddframe.rdb.sharding.parser.result.SQLParsedResultTest; +import com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql.MySQLEvalVisitorTest; import org.junit.runner.RunWith; import org.junit.runners.Suite; @@ -32,7 +33,8 @@ MySQLPreparedStatementForOneParameterTest.class, MySQLPreparedStatementForTowParametersTest.class, OrParseTest.class, - UnsupportedParseTest.class + UnsupportedParseTest.class, + MySQLEvalVisitorTest.class, }) public class AllParserTests { } diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/jaxb/Condition.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/jaxb/Condition.java index 3cdd2dc87fdf5..a7b4f32b7f51c 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/jaxb/Condition.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/jaxb/Condition.java @@ -17,15 +17,14 @@ package com.dangdang.ddframe.rdb.sharding.parser.jaxb; -import java.util.List; +import lombok.Getter; +import lombok.Setter; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; - -import lombok.Getter; -import lombok.Setter; +import java.util.List; @Getter @Setter @@ -43,4 +42,7 @@ public final class Condition { @XmlElement(name = "value") private List values; + + @XmlElement(name = "valueIndices") + private List valueIndices; } diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/result/SQLParsedResultTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/result/SQLParsedResultTest.java index 06e5e93b4ff1e..ba7d87fe79639 100644 --- a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/result/SQLParsedResultTest.java +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/result/SQLParsedResultTest.java @@ -54,7 +54,7 @@ public void assertToString() throws IOException { + "sqlStatementType=null, " + "sqlBuilder=SELECT * FROM [Token(order)]), " + "conditionContexts=[ConditionContext(conditions={Condition.Column(columnName=id, tableName=order)=Condition(column=Condition.Column(columnName=id, tableName=order), " - + "operator=IN, values=[1, 2, 3])})], " + + "operator=IN, values=[1, 2, 3], valueIndices=[])})], " + "mergeContext=MergeContext(" + "orderByColumns=[OrderByColumn(super=AbstractSortableColumn(owner=Optional.absent(), " + "name=Optional.of(id), alias=Optional.of(a), orderByType=DESC), index=Optional.absent(), columnIndex=0)], " diff --git a/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitorTest.java b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitorTest.java new file mode 100644 index 0000000000000..9302059d06632 --- /dev/null +++ b/sharding-jdbc-core/src/test/java/com/dangdang/ddframe/rdb/sharding/parser/visitor/basic/mysql/MySQLEvalVisitorTest.java @@ -0,0 +1,82 @@ +/* + * Copyright 1999-2015 dangdang.com. + *

+ * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 + * + * 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. + *

+ */ + +package com.dangdang.ddframe.rdb.sharding.parser.visitor.basic.mysql; + +import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr; +import com.alibaba.druid.sql.visitor.SQLEvalVisitorUtils; +import com.google.common.collect.Lists; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.nullValue; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +public class MySQLEvalVisitorTest { + + @Test + public void testVisit() throws Exception { + SQLVariantRefExpr expr = new SQLVariantRefExpr("?"); + expr.setIndex(1); + MySQLEvalVisitor visitor = new MySQLEvalVisitor(); + visitor.setParameters(Lists.newArrayList(1 ,2)); + expr.accept(visitor); + assertThat((Integer) SQLEvalVisitorUtils.getValue(expr), is(2)); + assertThat((Integer) expr.getAttribute(MySQLEvalVisitor.EVAL_VAR_INDEX), is(1)); + } + + @Test + public void testVisitErrorIndex() throws Exception { + SQLVariantRefExpr expr = new SQLVariantRefExpr("?"); + expr.setIndex(2); + MySQLEvalVisitor visitor = new MySQLEvalVisitor(); + visitor.setParameters(Lists.newArrayList(1 ,2)); + expr.accept(visitor); + assertThat(SQLEvalVisitorUtils.getValue(expr), nullValue()); + } + + @Test + public void testVisitNotOverride() throws Exception { + SQLVariantRefExpr expr = new SQLVariantRefExpr("?"); + expr.setIndex(1); + expr.getAttributes().put(MySQLEvalVisitor.EVAL_VALUE, "test"); + MySQLEvalVisitor visitor = new MySQLEvalVisitor(); + visitor.setParameters(Lists.newArrayList(1 ,2)); + expr.accept(visitor); + assertThat((String) SQLEvalVisitorUtils.getValue(expr), is("test")); + } + + @Test + public void testVisitWrongName() throws Exception { + SQLVariantRefExpr expr = new SQLVariantRefExpr(""); + expr.setIndex(1); + MySQLEvalVisitor visitor = new MySQLEvalVisitor(); + visitor.setParameters(Lists.newArrayList(1 ,2)); + expr.accept(visitor); + assertThat(SQLEvalVisitorUtils.getValue(expr), nullValue()); + } + + @Test + public void testVisitNullValue() throws Exception { + SQLVariantRefExpr expr = new SQLVariantRefExpr("?"); + expr.setIndex(1); + MySQLEvalVisitor visitor = new MySQLEvalVisitor(); + visitor.setParameters(Lists.newArrayList(1 ,null)); + expr.accept(visitor); + assertThat(SQLEvalVisitorUtils.getValue(expr), is(MySQLEvalVisitor.EVAL_VALUE_NULL)); + } +} \ No newline at end of file diff --git a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/delete.xml b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/delete.xml index 5ac03b7fc8432..3f1772000208e 100644 --- a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/delete.xml +++ b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/delete.xml @@ -8,6 +8,7 @@ + 0 diff --git a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/insert.xml b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/insert.xml index 759bf7ab5eac9..ffca74b669ef4 100644 --- a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/insert.xml +++ b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/insert.xml @@ -8,6 +8,7 @@ + 0 diff --git a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/select.xml b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/select.xml index 61f20aea6e791..299640a951c12 100644 --- a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/select.xml +++ b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/select.xml @@ -10,6 +10,7 @@ + 0 diff --git a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/update.xml b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/update.xml index b6f92cd1ba8c5..1e420921fe977 100644 --- a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/update.xml +++ b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/one_param/update.xml @@ -8,6 +8,7 @@ + 0 diff --git a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/two_params/select.xml b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/two_params/select.xml index d693b5f5b0981..ad7e99b720dbd 100644 --- a/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/two_params/select.xml +++ b/sharding-jdbc-core/src/test/resources/com/dangdang/ddframe/rdb/sharding/parser/mysql/prepared_statement/two_params/select.xml @@ -9,6 +9,8 @@ + 0 + 1 diff --git a/sharding-jdbc-doc/content/post/release_notes.md b/sharding-jdbc-doc/content/post/release_notes.md index 9de5403a71141..3e81c8f997ae8 100644 --- a/sharding-jdbc-doc/content/post/release_notes.md +++ b/sharding-jdbc-doc/content/post/release_notes.md @@ -8,6 +8,11 @@ weight = 1 ## 1.3.2-SNAPSHOT +### 缺陷修正 + +1. [ISSUE #36](https://github.com/dangdangdotcom/sharding-jdbc/issues/36) ShardingPreparedStatement无法反复设置参数 +1. [ISSUE #114](https://github.com/dangdangdotcom/sharding-jdbc/issues/114) ShardingPreparedStatement执行批处理任务时,反复解析sql导致oom + ## 1.3.1 ### 功能提升