ShardingJdbc2.X学习总结系列(八):源码解析—SQL执行

本文深入探讨ShardingJdbc SQL执行的内部机制,从生成PreparedStatementExecutor对象到执行execute方法,详细解析执行流程,包括同步执行、异步执行、事件发布等关键步骤,以及异常处理和重试机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面几篇文章介绍了SQL解析,SQL路由,SQL改写的过程,包括组装成最后的Collection<PreparedStatementUnit>

这篇文章就开始介绍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();
        }
    }

①也就是我们之前文章介绍的过程

下面我们重点介绍②,我们看到这里其实分为两步:

1.生成PreparedStatementExecutor对象,也就是执行sql的对象

2.调用PreparedStatementExecutor的execute, executeUpdate, executeQuery 方法,进行SQL的执行

这里我们主要以execute方法进行详细展开

 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);
    }

在方法中我们看到,其实最终调用的是executorEngine.executePreparedStatement方法,而在这个方法上我们看到,最后一个参数传递的是一个回调对象,其中的主要方法调用的是PreparedStatement的execute方法,这个大家应该都很熟悉了,是底层SQL的执行方法。

我们对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;
    }

最终调用的是内部的execute方法,这里我们分步奏进行详细解析

1.获取到第一个BaseStatementUnit 即PreparedStatementUnit

2.异步执行后面的所有BaseStatementUnit

3.同步执行第一个BaseStatementUnit

4.获取第二步异步执行的结果(阻塞获取)

5.组合第四步和第3步获取的结果LIST

在这里我们主要看第2,和3两步的执行过程

首先看第三步的同步方法

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;
        }

最终调用的是executeInternal方法,在这个方法里面我们看到最终真正跟数据库有交互的是调用了我们传过来的回调类,在①这里有标注,这里就是我们类中的方法,PreparedStatement的execute方法。

在这里有一个特殊的处理,大家可以看到标注②③④的位置,在SQL的执行前,执行成功,执行失败,都会通过eventBus进行post。并且在执行失败的时候,抛出相应的异常。

接下来看一下第二步异步执行的过程:

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);
    }

这里主要是循环多线程调用executeInternal方法,这里跟同步调用的方法是一样的,就不在贴代码了。

并且在大方法中,会捕获到2,3步执行的异常,如果有异常会给eventBus发送失败消息,成功的话发送成功消息。

这里的eventBus主要是后续可对失败进行重试的。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值