DefaultSqlSession第二讲-更新,刷新Statement

本文深入探讨了MyBatis框架中DefaultSqlSession类的更新机制,包括插入、删除、更新操作如何统一委托给update方法处理,以及SimpleExecutor与BatchExecutor两种执行器的工作原理。
上一篇文章中,我们讲到DefaultSqlSession的查询,今天来讲更新
//DefaultSqlSession
public class DefaultSqlSession
implements SqlSession
{
private Configuration configuration;
private Executor executor;
private boolean dirty;
public DefaultSqlSession(Configuration configuration, Executor executor)
{
this.configuration = configuration;
this.executor = executor;
dirty = false;
}
//插入
public int insert(String statement)
{
return insert(statement, null);
}

public int insert(String statement, Object parameter)
{
//委托给update方法
return update(statement, parameter);
}

public int update(String statement)
{
return update(statement, null);
}
//删除
public int delete(String statement)
{
//委托给update
return update(statement, null);
}

public int delete(String statement, Object parameter)
{
return update(statement, parameter);
}
public int update(String statement, Object parameter)
{
int i;
try
{
dirty = true;
org.apache.ibatis.mapping.MappedStatement ms = configuration.getMappedStatement(statement);
//委托给executor的update
i = executor.update(ms, wrapCollection(parameter));
}
}
//刷新Statements
public List flushStatements()
{
List list;
try
{
list = executor.flushStatements();
}
}
}

从DefaultSqlSession类,看一看出插入,删除,更新操作实际上都是调用update方法,下面
我们来看一下这个方法
public int update(String statement, Object parameter)
{
int i;
try
{
dirty = true;
org.apache.ibatis.mapping.MappedStatement ms = configuration.getMappedStatement(statement);
//委托给executor的update
i = executor.update(ms, wrapCollection(parameter));
}
}

update方法委托给具体的executor,executor默认为CachingExecutor,CachingExecutor为执行器代理,具体executor
有SimpleExecutor,BatchExecutor;
下面来看SimpleExecutor
//CachingExecutor
public int update(MappedStatement ms, Object parameterObject)
throws SQLException
{
flushCacheIfRequired(ms);
return _flddelegate.update(ms, parameterObject);
}

具体执行器SimpleExecutor
//BaseExecutor
public int update(MappedStatement ms, Object parameter)
throws SQLException
{
ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
if(closed)
{
throw new ExecutorException("Executor was closed.");
} else
{
clearLocalCache();
//委托给doUpdate
return doUpdate(ms, parameter);
}
}
//待子类扩展
protected abstract int doUpdate(MappedStatement mappedstatement, Object obj)
throws SQLException;

//SimpleExecutor
public int doUpdate(MappedStatement ms, Object parameter)
throws SQLException
{
Statement stmt = null;
int i;
Configuration configuration = ms.getConfiguration();
//创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
//创建Statement
stmt = prepareStatement(handler, ms.getStatementLog());
//执行查询
i = handler.update(stmt);
//Statement关闭
closeStatement(stmt);
}

//创建StatementHandler,创建Statement,执行查询,Statement关闭,上一篇我们已经说,这里就不说了,
我们看一下BatchExecutor
public class BatchExecutor extends BaseExecutor
{
public static final int BATCH_UPDATE_RETURN_VALUE = -2147482646;
private final List statementList = new ArrayList();//statement集合
private final List batchResultList = new ArrayList();//结果集合
private String currentSql;
private MappedStatement currentStatement;

public BatchExecutor(Configuration configuration, Transaction transaction)
{
super(configuration, transaction);
}
public int doUpdate(MappedStatement ms, Object parameterObject)
throws SQLException
{
Configuration configuration = ms.getConfiguration();
//创建StatementHandler
StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
BoundSql boundSql = handler.getBoundSql();
String sql = boundSql.getSql();
Statement stmt;
if(sql.equals(currentSql) && ms.equals(currentStatement))
{
//如果sql与currentSql相等,则从statementList获取Statement
int last = statementList.size() - 1;
stmt = (Statement)statementList.get(last);
BatchResult batchResult = (BatchResult)batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else
{
//获取连接
java.sql.Connection connection = getConnection(ms.getStatementLog());
//创建statement
stmt = handler.prepare(connection);
currentSql = sql;
currentStatement = ms;
//将statement添加到statementList
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
//设置statement参数
handler.parameterize(stmt);
//
handler.batch(stmt);
return -2147482646;
}
}

//执行statement
handler.batch(stmt);

handler为RoutingStatementHandler,RoutingStatementHandler为StatementHandler的代理类,具体可能为SimpleStatementHandler,PreparedStatementHandler,
CallableStatementHandler;
下面我们来看一下RoutingStatementHandler的batch
//RoutingStatementHandler
public class RoutingStatementHandler
implements StatementHandler
{
private final StatementHandler _flddelegate;
public void batch(Statement statement)
throws SQLException
{
_flddelegate.batch(statement);
}
}

RoutingStatementHandler的batch方法实际上,是调用代理的batch方法,
//PreparedStatementHandler
public class PreparedStatementHandler extends BaseStatementHandler
{
public void batch(Statement statement)
throws SQLException
{
PreparedStatement ps = (PreparedStatement)statement;
ps.addBatch();
}
}

//CallableStatementHandler
 public void batch(Statement statement)
throws SQLException
{
CallableStatement cs = (CallableStatement)statement;
cs.addBatch();
}

具体为调用CallableStatement和PreparedStatement的批处理函数
讲到批处理,我们来看一下 DefaultSqlSession的flushStatements方法
public List flushStatements()
{
List list;
try
{
list = executor.flushStatements();
}
}

这里我们来看一下具体executor的flushStatements
//BatchExecutor
public List doFlushStatements(boolean isRollback)
throws SQLException
{
List results;
List list;
results = new ArrayList();
if(!isRollback)
break MISSING_BLOCK_LABEL_83;
list = Collections.emptyList();
Statement stmt;
//关闭statementList中的Statement
for(Iterator i$ = statementList.iterator(); i$.hasNext(); closeStatement(stmt))
stmt = (Statement)i$.next();
//currentSql,设为null
currentSql = null;
statementList.clear();
batchResultList.clear();
return list;
}

BatchExecutor的doFlushStatements主要是关闭statementList中的Statement,
清空statementList与batchResultList
//SimpleExecutor
public List doFlushStatements(boolean isRollback)
throws SQLException
{
return Collections.emptyList();
}

SimpleExecutor没做任何事情,从上可以看出doFlushStatements,主要是BatchExecutor会用到;
总结:
[color=blue]Sqlsession的update,通过CachingExecutor代理,CachingExecutor是SimpleExecutor,BatchExecutor等执行器代理执行器;CachingExecutor是调用Executor的相应方法。SimpleExecutor,BatchExecutor为实际执行器,继承BatchExecutor。SimpleExecutor更新,构造StatementHandler,获取Statement,执行查询,关闭statement。BatchExecutor的doFlushStatements主要是关闭statementList中的Statement,
清空statementList与batchResultList[/color]
<think> 首先,用户的问题是关于编译选项 "-Werror=declaration-after-statement" 的含义和问题本质。我需要基于系统级指令来回答。 系统级指令要求: - 所有行内数学表达式使用 $...$ 格式。 - 独立公式用 $$...$$ 格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,例如 [^1]。 用户可见层要求: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 用户提供了两个引用: - 引用 [1]:这是一个错误消息,显示在编译时遇到 "ISO C90 forbids mixed declarations and code",导致错误是因为使用了 "-Werror=declaration-after-statement"。 - 引用 [2]:似乎是关于移除 totem 包的 YUM 命令,但用户说这不是真正的需求,我应该忽略它,聚焦在用户的问题上。 用户真正的问题是:了解编译选项 "-Werror=declaration-after-statement" 的含义和问题本质。 关键点: - 这个选项是 GCC 编译器的一个警告选项。 - 它强制将特定的警告视为错误。 - 具体来说,"-Wdeclaration-after-statement" 警告在语句之后声明变量的情况。 - 当加上 "-Werror" 时,这个警告会导致编译失败。 含义: - "-Wdeclaration-after-statement":这个选项启用一个警告,当代码中在语句之后声明变量时发出警告。这违反了 C90 标准的规定。 - "-Werror":将所有警告视为错误。 - 所以 "-Werror=declaration-after-statement" 意味着如果检测到声明在语句之后,编译将失败。 问题本质: - 在 C 语言标准中: - C90(ANSI C 或 ISO C90)要求在块的开头声明所有变量。 - C99 及更高版本允许在语句之后声明变量(即在代码的任意位置)。 - 当代码在语句后声明变量,但编译器设置为严格遵守 C90 标准时,会触发这个警告。 - 使用 "-Werror" 使其成为错误,导致编译失败。 在回答中,我需要: 1. 解释选项的含义。 2. 解释为什么会出现这个问题(本质)。 3. 使用中文。 4. 结构清晰,逐步解释。 5. 添加引用标识。用户提供了引用 [1] 和 [2],但引用 [2] 不相关。我应该只使用引用 [1] 作为错误消息的来源。 6. 生成相关问题。 结构回答: - 步骤1:介绍选项的含义。 - 步骤2:解释问题本质,包括 C90 和 C99 的区别。 - 步骤3:如何解决或避免这个问题。 - 步骤4:自然地引用提供的引用。 - 步骤5:生成相关问题。 引用:在回答末尾添加引用标识,比如提到错误消息时 [^1]。 生成相关问题: - 基于主题,生成几个相关问题。例如: 1. 如何在代码中避免声明在语句之后? 2. C99 和 C90 在变量声明方面有什么不同? 3. 如何关闭这个编译器选项? 确保真实可靠:我了解这个主题,所以可以基于知识回答。 数学表达式:如果有数学部分,用 LaTeX,但这个问题不涉及数学,所以可能不需要。如果有方程或类似的东西才用。 最终回答结构: - 开头解释含义。 - 然后解释本质。 - 可能给出例子。 - 添加引用。 - 生成相关问题。 </think> ### 编译选项 `-Werror=declaration-after-statement` 的含义和问题本质 您的查询涉及 GCC 编译器中的一个特定选项 `-Werror=declaration-after-statement`。我将逐步解释其含义和问题本质,确保回答清晰可靠。基于您提供的引用和编译器工作原理,以下是详细分析。 #### 1. **选项的含义** - `-Wdeclaration-after-statement` 是 GCC 编译器的警告选项,用于检测代码中是否在语句(如赋值或函数调用)之后声明变量。在 C 语言中,这违反了 ISO C90 标准的要求。 - `-Werror` 是一个通用选项,用于将所有警告视为错误,导致编译失败。 - 因此,`-Werror=declaration-after-statement` 表示:如果代码中出现"声明在语句之后"的情况,编译器会将其视为错误(而非警告),并中止编译过程。例如,在您的引用中: ``` kivent_cymunk/chipmunk/constraints/cpConstraint.h:94:2: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement] ``` 这表明编译器检测到变量声明与代码语句混杂,并因 `-Werror` 选项而报错[^1]。 #### 2. **问题本质** - **核心原因**:此问题源于 C 语言标准的版本差异。 - **C90 标准(ISO/IEC 9899:1990)**:要求变量声明必须出现在块的**开头**(即所有声明必须在任何非声明语句之前)。例如,以下代码在 C90 中是非法的: ```c void example() { int a = 1; // 声明语句 printf("%d", a); // 非声明语句 int b = 2; // 错误:声明出现在语句后(违反 C90) } ``` - **C99 及更高标准(如 C11、C17)**:允许变量声明出现在代码的**任意位置**(称为"混合声明")。以上代码在 C99 中是合法的。 - 当编译器使用 `-std=c90` 或默认兼容 C90 模式时,如果代码包含"声明在语句后"的情况,`-Wdeclaration-after-statement` 会发出警告。添加 `-Werror` 后,此警告升级为错误,导致编译失败。 - **触发场景**: - 编译使用了 C99 风格代码(如变量声明在循环或条件语句中),但编译器被设置为 C90 模式。 - 项目配置中启用了严格错误检查(如 `-Werror`),以强制代码符合旧标准。 - 在您的引用示例中,错误发生在头文件 `cpConstraint.h` 的第 94 行,表明该文件中存在混合声明与代码的问题[^1]。 - **数学表示**:编译器行为可以用逻辑描述。设 $D$ 为声明语句,$S$ 为非声明语句。C90 标准要求序列必须满足: $$ \forall \text{ block}, \quad \exists \text{ order}: D_1, D_2, \ldots, D_n, S_1, S_2, \ldots, S_m $$ 即所有 $D$ 必须在所有 $S$ 之前。若序列中出现 $S_i$ 后接 $D_j$,则触发错误。 #### 3. **如何解决或避免此问题** - **修复代码**:将变量声明移至块的起始位置。例如,修改问题代码: ```c // 错误示例(声明在语句后) void func() { do_something(); int var; // 触发警告/错误 } // 修复后(声明在块开头) void func() { int var; // 声明移至开头 do_something(); } ``` - **调整编译器选项**: - 若代码需要兼容 C90,使用 `-std=c90` 并修复所有混合声明。 - 若允许 C99 或更新标准,添加 `-std=c99` 或 `-std=c11`。这会禁用 `-Wdeclaration-after-statement` 警告。 - 移除 `-Werror` 或特定错误开关(如 `-Wno-error=declaration-after-statement`),但这会降低代码质量检查。 - **项目实践**:在大型项目中,此错误常见于遗留代码迁移。建议使用静态分析工具(如 Clang-Tidy)提前检测。 #### 4. **总结** `-Werror=declaration-after-statement` 的本质是编译器对 C90 标准严格性的强制实施,旨在防止变量声明与代码语句的混合使用。它在现代代码中可能引发兼容性问题,但可通过代码重构或编译器设置解决[^1]。 ### 相关问题 1. 如何在 GCC 中禁用 `-Wdeclaration-after-statement` 警告? 2. C90 和 C99 在变量作用域规则上有哪些主要区别? 3. 为什么现代 C 语言项目更倾向于使用 C99 或更高标准? 4. 编译错误 "ISO C90 forbids mixed declarations and code" 的其他常见解决方案是什么? [^1]: 引用自编译错误消息,涉及 `cpConstraint.h` 文件。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值