Mybatis StatementHandler的使用小結(jié)
一、從Executor說起
之前著重講解了Executor實(shí)現(xiàn)的一二級(jí)緩存功能,現(xiàn)在我們回到Executor的核心的執(zhí)行SQL的能力,以SimpleExecutor為例:
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
// 創(chuàng)建 StatementHandler
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
// 準(zhǔn)備 Statement
stmt = prepareStatement(handler, ms.getStatementLog());
// 執(zhí)行查詢
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
// 調(diào)用 StatementHandler 的 prepare 方法創(chuàng)建 Statement
stmt = handler.prepare(connection, transaction.getTimeout());
// 調(diào)用 StatementHandler 的 parameterize 方法設(shè)置參數(shù)
handler.parameterize(stmt);
return stmt;
}
通過源碼可以看到,SQL執(zhí)行的代碼非常簡(jiǎn)單:
- 從configuration中,構(gòu)造StatementHandler。
- 調(diào)用StatementHandler,構(gòu)造JDBC的Statement。
- 調(diào)用StatementHandler的parameterize,設(shè)置參數(shù)。
- 調(diào)用StatementHandler的query方法(更新則調(diào)用update方法)。
簡(jiǎn)而言之,Executor通過StatementHandler,實(shí)現(xiàn)SQL執(zhí)行的功能。
二、StatementHandler接口方法
StatementHandler 接口定義了與數(shù)據(jù)庫交互的核心方法,覆蓋了從SQL準(zhǔn)備、參數(shù)設(shè)置到執(zhí)行的完整流程:
| 方法簽名 | 作用描述 |
|---|---|
| Statement prepare(Connection connection, Integer transactionTimeout) | 創(chuàng)建并初始化數(shù)據(jù)庫執(zhí)行語句(Statement/PreparedStatement),完成 SQL 預(yù)編譯 |
| void parameterize(Statement statement) | 為預(yù)編譯 SQL 設(shè)置參數(shù)(將 Java 對(duì)象映射到 SQL 占位符?) |
| int batch(Statement statement) | 執(zhí)行批量 SQL 操作(如批量插入 / 更新),返回批處理數(shù)量 |
| int update(Statement statement) | 執(zhí)行增刪改操作,返回影響的行數(shù) |
| List<Object> query(Statement statement, ResultHandler resultHandler) | 執(zhí)行查詢操作,通過 ResultHandler 將結(jié)果集映射為 Java 對(duì)象 |
| <T> Cursor<T> queryCursor(Statement statement) | 執(zhí)行查詢并返回 Cursor 對(duì)象,用于流式處理大數(shù)據(jù)集 |
| BoundSql getBoundSql() | 獲取封裝 SQL 語句和參數(shù)信息的 BoundSql 對(duì)象 |
| ParameterHandler getParameterHandler() | 獲取參數(shù)處理器 ParameterHandler(用于參數(shù)設(shè)置) |
三、代碼實(shí)現(xiàn)
3.1 概述
classDiagram
direction TB
class StatementHandler {
<<interface>>
+prepare(Connection, Integer) Statement
+parameterize(Statement) void
+batch(Statement) int
+update(Statement) int
+query(Statement, ResultHandler) List~Object~
+queryCursor(Statement) Cursor~T~
+getBoundSql() BoundSql
+getParameterHandler() ParameterHandler
}
class BaseStatementHandler {
<<abstract>>
-configuration: Configuration
-executor: Executor
-mappedStatement: MappedStatement
-parameterHandler: ParameterHandler
-resultSetHandler: ResultSetHandler
-boundSql: BoundSql
+prepare(Connection, Integer) Statement
+getBoundSql() BoundSql
+getParameterHandler() ParameterHandler
}
class SimpleStatementHandler
class PreparedStatementHandler
class CallableStatementHandler
class RoutingStatementHandler
StatementHandler <|-- BaseStatementHandler
BaseStatementHandler <|-- SimpleStatementHandler
BaseStatementHandler <|-- PreparedStatementHandler
BaseStatementHandler <|-- CallableStatementHandler
StatementHandler <|-- RoutingStatementHandler
RoutingStatementHandler *-- StatementHandler : delegatesStatementHandler有四個(gè)核心實(shí)現(xiàn)類,關(guān)系與作用如下:
- BaseStatementHandler:模板模式的抽象基類,定義了一些基礎(chǔ)屬性&方法,核心實(shí)現(xiàn)了prepare,定義了抽象的instantiateStatement方法,留給子類是實(shí)現(xiàn)。有三個(gè)子類:
- SimpleStatementHandler 對(duì)應(yīng) JDBC 的 Statement,處理無參數(shù)靜態(tài) SQL;
- PreparedStatementHandler 對(duì)應(yīng) PreparedStatement,處理帶參數(shù)預(yù)編譯 SQL(默認(rèn)使用);
- CallableStatementHandler 對(duì)應(yīng) CallableStatement,用于調(diào)用存儲(chǔ)過程。
- RoutingStatementHandler 是路由處理器,自身不處理邏輯,而是根據(jù)MappedStatement的statementType動(dòng)態(tài)選擇上述三個(gè)實(shí)現(xiàn)類之一進(jìn)行委派,簡(jiǎn)化了客戶端調(diào)用。
3.2RoutingStatementHandler- Handler路由器
RoutingStatementHandler是典型的代理模式,在構(gòu)造函數(shù)中,根據(jù)MappedStatement的statementType選擇具體的StatementHandler:
private final StatementHandler delegate;
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
// 根據(jù)statementType選擇具體的StatementHandler
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
}
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
return delegate.prepare(connection, transactionTimeout);
}
// 其他代理方法忽略。。。PS:MappedStatement的statementType默認(rèn)是PREPARED,可以在xml或者注解中配置statementType屬性。
3.3 BaseStatementHandler 模板模式的基類
BaseStatementHandler源碼中,比較重要的是這個(gè)prepare方法,實(shí)現(xiàn)了通用流程,具體的JDBC的Statement創(chuàng)建邏輯,由子類通過instantiateStatement方法實(shí)現(xiàn)。
@Override
public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
// 由子類實(shí)現(xiàn)具體的Statement創(chuàng)建
statement = instantiateStatement(connection);
// 設(shè)置超時(shí)時(shí)間
setStatementTimeout(statement, transactionTimeout);
// 設(shè)置fetchSize
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
}
}
// 需要子類實(shí)現(xiàn)
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;3.4 instantiateStatement方法
各個(gè)子類的instantiateStatement發(fā)放,就是使用不同的姿勢(shì)調(diào)用JDBC的API:
- SimpleStatementHandler 對(duì)應(yīng) JDBC 的 Statement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.createStatement();
} else {
return connection.createStatement(mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}- PreparedStatementHandler 對(duì)應(yīng) PreparedStatement
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
// 處理自增主鍵
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyColumnNames == null) {
return connection.prepareStatement(sql, PreparedStatement.RETURN_GENERATED_KEYS);
} else {
return connection.prepareStatement(sql, keyColumnNames);
}
} else if (mappedStatement.getResultSetType() != null) {
// 處理結(jié)果集類型
return connection.prepareStatement(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
} else {
// 普通PreparedStatement創(chuàng)建
return connection.prepareStatement(sql);
}
}
- CallableStatementHandler 對(duì)應(yīng) CallableStatement:
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
if (mappedStatement.getResultSetType() == ResultSetType.DEFAULT) {
return connection.prepareCall(sql);
} else {
return connection.prepareCall(sql, mappedStatement.getResultSetType().getValue(), ResultSet.CONCUR_READ_ONLY);
}
}
3.5 處理參數(shù)&結(jié)果
StatementHandler的參數(shù)處理&結(jié)果處理,分別委托給ParameterHandler&ResultSetHandler(注意不是ResultHandler)??梢哉f,Mybatis把單一職責(zé)進(jìn)行到底。
PreparedStatementHandler參數(shù)&結(jié)果處理:
@Override
public void parameterize(Statement statement) throws SQLException {
// 委托ParameterHandler設(shè)置參數(shù)
parameterHandler.setParameters((PreparedStatement) statement);
}
@Override
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
// 委托ResultSetHandler處理結(jié)果集
return resultSetHandler.handleResultSets(ps);
}四、小結(jié)
StatementHandler是MyBatis中負(fù)責(zé)與數(shù)據(jù)庫交互的核心組件,封裝了JDBC的Statement相關(guān)操作,提供了統(tǒng)一的接口:
- SQL 語句的準(zhǔn)備(預(yù)編譯)
- 參數(shù)綁定
- SQL執(zhí)行(查詢、更新、批量操作等),這里也包含了結(jié)果處理。
StatementHandler實(shí)現(xiàn)過程中,使用了模板、策略以及代理模式。并且很好的體現(xiàn)了單一職責(zé)原則,參數(shù)處理&結(jié)果處理,分別委托給ParameterHandler&ResultSetHandler(注意不是ResultHandler),這兩個(gè)Handler我們后續(xù)再講。
到此這篇關(guān)于Mybatis StatementHandler的使用小結(jié)的文章就介紹到這了,更多相關(guān)Mybatis StatementHandler內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Mybatis-Plus3.2.0 MetaObjectHandler 無法進(jìn)行公共字段全局填充
- MyBatis-Plus中MetaObjectHandler沒生效完美解決
- Mybatis實(shí)現(xiàn)自定義的typehandler三步曲
- MyBatis自定義typeHandler的完整實(shí)例
- 解決mybatisplus MetaObjectHandler 失效的問題
- Mybatis實(shí)現(xiàn)自定義類型轉(zhuǎn)換器TypeHandler的方法
- mybatis中resulthandler的用法
- 解決Mybatis-plus自定義TypeHandler查詢映射結(jié)果一直為null問題
- Mybatis中自定義TypeHandler處理枚舉詳解
- Mybatis自定義TypeHandler解決特殊類型轉(zhuǎn)換問題詳解
- Mybatis實(shí)戰(zhàn)之TypeHandler高級(jí)進(jìn)階
相關(guān)文章
Spring @Primary和@Qualifier注解原理解析
這篇文章主要介紹了Spring @Primary和@Qualifier注解原理解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
使用chatgpt實(shí)現(xiàn)微信聊天小程序的代碼示例
這篇文章主要介紹了使用chatgpt實(shí)現(xiàn)微信聊天小程序(秒回復(fù)),文中有詳細(xì)的代碼示例,對(duì)大家了解chatgpt聊天有一定的幫助,感興趣的同學(xué)可以參考閱讀2023-05-05
如何解決IDEA中JSP頁面部分出現(xiàn)綠色背景色問題
這篇文章主要介紹了如何解決IDEA中JSP頁面部分出現(xiàn)綠色背景色問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12
maven關(guān)于pom文件中的relativePath標(biāo)簽使用
在Maven項(xiàng)目中,子工程通過<relativePath>標(biāo)簽指定父工程的pom.xml位置,以確保正確繼承父工程的配置,這個(gè)標(biāo)簽可以配置為默認(rèn)值、空值或自定義值,默認(rèn)情況下,Maven會(huì)向上一級(jí)目錄尋找父pom;若配置為空值2024-09-09
使用Netty快速實(shí)現(xiàn)一個(gè)群聊功能的示例詳解
這篇文章主要為大家詳細(xì)介紹了如何利用?Netty?框架開發(fā)一個(gè)?WebSocket?服務(wù)端,從而實(shí)現(xiàn)一個(gè)簡(jiǎn)單的在線聊天功能,感興趣的小伙伴可以了解下2023-11-11
快速解決idea打開某個(gè)項(xiàng)目卡住的問題
這篇文章主要介紹了解決idea打開某個(gè)項(xiàng)目卡住的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08
Springboot項(xiàng)目集成SkyWalking鏈路追蹤系統(tǒng)實(shí)戰(zhàn)指南
本文詳解SpringBoot集成SkyWalking鏈路追蹤的步驟,涵蓋Agent部署、配置參數(shù)設(shè)置(含服務(wù)名、后端地址、認(rèn)證token)、多啟動(dòng)方式及日志對(duì)接方法,但未充分說明環(huán)境變量配置細(xì)節(jié)和gRPC認(rèn)證參數(shù)的使用場(chǎng)景,需要的朋友跟隨小編一起看看吧2025-08-08

