Sharing Jdbc2.X Learning Summary Series: Source Parsing - SQL Execution

Posted by lyonsperf on Sun, 04 Aug 2019 13:48:41 +0200

The previous articles introduced the process of SQL parsing, SQL routing, and SQL rewriting, including assembling the final Collection <Prepared Statement Unit>.

This article begins with an introduction to the execution of SQL

public boolean execute() throws SQLException {
        try {
            ① Collection<PreparedStatementUnit> preparedStatementUnits = route();
            ② return new PreparedStatementExecutor(
                    getConnection().getShardingContext().getExecutorEngine(), routeResult.getSqlStatement().getType(), preparedStatementUnits, getParameters()).execute();
        } finally {
            clearBatch();
        }
    }

That's the process we introduced in our previous article.

Let's focus on the following. We can see that there are actually two steps here:

1. Generate the PreparedStatementExecutor object, that is, the object that executes sql

2. Call PreparedStatementExecutor's execute, executeUpdate, executeQuery methods to execute SQL

Here we mainly use execute method to expand in detail.

 public boolean execute() throws SQLException {
        List<Boolean> result = executorEngine.executePreparedStatement(sqlType, preparedStatementUnits, parameters, new ExecuteCallback<Boolean>() {
            
            @Override
            public Boolean execute(final BaseStatementUnit baseStatementUnit) throws Exception {
                return ((PreparedStatement) baseStatementUnit.getStatement()).execute();
            }
        });
        if (null == result || result.isEmpty() || null == result.get(0)) {
            return false;
        }
        return result.get(0);
    }

In the method we see, in fact, the ultimate call is the executorEngine.executePreparedStatement method, and in this method we see that the last parameter passed a callback object, in which the main method calls the execute method of PreparedStatement, which you should be familiar with, is the bottom. The execution method of layer SQL.

We expand executorEngine.executePreparedStatement

public <T> List<T> executePreparedStatement(
            final SQLType sqlType, final Collection<PreparedStatementUnit> preparedStatementUnits, final List<Object> parameters, final ExecuteCallback<T> executeCallback) throws SQLException {
        return execute(sqlType, preparedStatementUnits, Collections.singletonList(parameters), executeCallback);
    }


private  <T> List<T> execute(
            final SQLType sqlType, final Collection<? extends BaseStatementUnit> baseStatementUnits, 
            final List<List<Object>> parameterSets, final ExecuteCallback<T> executeCallback) throws SQLException {
        if (baseStatementUnits.isEmpty()) {
            return Collections.emptyList();
        }
        OverallExecutionEvent event = new OverallExecutionEvent(sqlType, baseStatementUnits.size());
        EventBusInstance.getInstance().post(event);
        Iterator<? extends BaseStatementUnit> iterator = baseStatementUnits.iterator();
        ① BaseStatementUnit firstInput = iterator.next();
        ② ListenableFuture<List<T>> restFutures = asyncExecute(sqlType, Lists.newArrayList(iterator), parameterSets, executeCallback);
        T firstOutput;
        List<T> restOutputs;
        try {
            ③ firstOutput = syncExecute(sqlType, firstInput, parameterSets, executeCallback);
            ④ restOutputs = restFutures.get();
            //CHECKSTYLE:OFF
        } catch (final Exception ex) {
            //CHECKSTYLE:ON
            event.setException(ex);
            event.setEventExecutionType(EventExecutionType.EXECUTE_FAILURE);
            EventBusInstance.getInstance().post(event);
            ExecutorExceptionHandler.handleException(ex);
            return null;
        }
        event.setEventExecutionType(EventExecutionType.EXECUTE_SUCCESS);
        EventBusInstance.getInstance().post(event);
        ⑤ List<T> result = Lists.newLinkedList(restOutputs);
        result.add(0, firstOutput);
        return result;
    }

The final call is the internal execute method, where we step by step for detailed analysis.

1. Get the first BaseStatementUnit, PreparedStatementUnit

2. All BaseStatementUnit s after Asynchronous Execution

3. Synchronize the first BaseStatementUnit

4. Get the result of the second asynchronous execution (blocking fetch)

5. Combine the results of Step 4 and Step 3 LIST

Here we mainly look at the implementation process of steps 2 and 32.

First, look at the third synchronization method

private <T> T syncExecute(final SQLType sqlType, final BaseStatementUnit baseStatementUnit, final List<List<Object>> parameterSets, final ExecuteCallback<T> executeCallback) throws Exception {
        return executeInternal(sqlType, baseStatementUnit, parameterSets, executeCallback, ExecutorExceptionHandler.isExceptionThrown(), ExecutorDataMap.getDataMap());
    }

private <T> T executeInternal(final SQLType sqlType, final BaseStatementUnit baseStatementUnit, final List<List<Object>> parameterSets, final ExecuteCallback<T> executeCallback, 
                          final boolean isExceptionThrown, final Map<String, Object> dataMap) throws Exception {
        synchronized (baseStatementUnit.getStatement().getConnection()) {
            T result;
            ExecutorExceptionHandler.setExceptionThrown(isExceptionThrown);
            ExecutorDataMap.setDataMap(dataMap);
            List<AbstractExecutionEvent> events = new LinkedList<>();
            if (parameterSets.isEmpty()) {
                events.add(getExecutionEvent(sqlType, baseStatementUnit, Collections.emptyList()));
            }
            for (List<Object> each : parameterSets) {
                events.add(getExecutionEvent(sqlType, baseStatementUnit, each));
            }
            for (AbstractExecutionEvent event : events) {
                ② EventBusInstance.getInstance().post(event);
            }
            try {
                ① result = executeCallback.execute(baseStatementUnit);
            } catch (final SQLException ex) {
                for (AbstractExecutionEvent each : events) {
                    each.setEventExecutionType(EventExecutionType.EXECUTE_FAILURE);
                    each.setException(ex);
                    ③ EventBusInstance.getInstance().post(each);
                    ExecutorExceptionHandler.handleException(ex);
                }
                return null;
            }
            for (AbstractExecutionEvent each : events) {
                each.setEventExecutionType(EventExecutionType.EXECUTE_SUCCESS);
                ④ EventBusInstance.getInstance().post(each);
            }
            return result;
        }

The final call is the executeInternal method. In this method, we see that what really interacts with the database is the callback class we passed back. Here is the annotation. Here is the method in our class, the execute method of PreparedStatement.

There is a special processing here, you can see the location of label (2) and (4). Before the execution of SQL, the successful execution and the failure of execution will be post through EvetBus. And when execution fails, the corresponding exception is thrown.

Next, take a look at the second asynchronous execution process:

private <T> ListenableFuture<List<T>> asyncExecute(
            final SQLType sqlType, final Collection<BaseStatementUnit> baseStatementUnits, final List<List<Object>> parameterSets, final ExecuteCallback<T> executeCallback) {
        List<ListenableFuture<T>> result = new ArrayList<>(baseStatementUnits.size());
        final boolean isExceptionThrown = ExecutorExceptionHandler.isExceptionThrown();
        final Map<String, Object> dataMap = ExecutorDataMap.getDataMap();
        for (final BaseStatementUnit each : baseStatementUnits) {
            result.add(executorService.submit(new Callable<T>() {
                
                @Override
                public T call() throws Exception {
                    return executeInternal(sqlType, each, parameterSets, executeCallback, isExceptionThrown, dataMap);
                }
            }));
        }
        return Futures.allAsList(result);
    }

This is mainly a circular multi-threaded call executeInternal method, here is the same as synchronous call method, no longer paste code.

And in the big method, the exception of 2,3 steps will be caught. If there are exceptions, the failure message will be sent to EvetBus, and the success message will be sent to EvetBus if it succeeds.

EvetBus here is primarily a follow-up attempt to retry failures.

 

 

Topics: SQL Database