日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
SQL執(zhí)行器的定義和實(shí)現(xiàn)

一、前言

為什么,要讀框架源碼?

因?yàn)槭掷锏臉I(yè)務(wù)工程代碼太拉胯了!通常作為業(yè)務(wù)研發(fā),所開(kāi)發(fā)出來(lái)的代碼,大部分都是一連串的流程化處理,缺少功能邏輯的解耦,有著迭代頻繁但可迭代性差的特點(diǎn)。所以這樣的代碼通常只能學(xué)習(xí)業(yè)務(wù)邏輯,卻很難吸收到大型系統(tǒng)設(shè)計(jì)和功能邏輯實(shí)現(xiàn)的成功經(jīng)驗(yàn),往往都是失敗的教訓(xùn)。

創(chuàng)新互聯(lián)公司專(zhuān)業(yè)為企業(yè)提供博樂(lè)網(wǎng)站建設(shè)、博樂(lè)做網(wǎng)站、博樂(lè)網(wǎng)站設(shè)計(jì)、博樂(lè)網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁(yè)設(shè)計(jì)與制作、博樂(lè)企業(yè)網(wǎng)站模板建站服務(wù),十多年博樂(lè)做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

而所有系統(tǒng)的設(shè)計(jì)和實(shí)現(xiàn),核心都在于如何解耦,如果解耦不清晰最后直接倒置的就是再繼續(xù)迭代功能時(shí),會(huì)讓整個(gè)系統(tǒng)的實(shí)現(xiàn)越來(lái)越臃腫,穩(wěn)定性越來(lái)越差。而關(guān)于解耦的實(shí)踐在各類(lèi)框架的源碼中都有非常不錯(cuò)的設(shè)計(jì)實(shí)現(xiàn),所以閱讀這部分源碼,就是在吸收成功的經(jīng)驗(yàn)。把解耦的思想逐步運(yùn)用到實(shí)際的業(yè)務(wù)開(kāi)發(fā)中,才會(huì)讓我們寫(xiě)出更加優(yōu)秀的代碼結(jié)構(gòu)。

二、目標(biāo)

在上一章節(jié)我們實(shí)現(xiàn)了有/無(wú)連接池的數(shù)據(jù)源,可以在調(diào)用執(zhí)行SQL的時(shí)候,通過(guò)我們實(shí)現(xiàn)池化技術(shù)完成數(shù)據(jù)庫(kù)的操作。

那么關(guān)于池化數(shù)據(jù)源的調(diào)用、執(zhí)行和結(jié)果封裝,目前我們還都只是在 DefaultSqlSession 中進(jìn)行發(fā)起 如圖 7-1 所示。那么這樣的把代碼流程寫(xiě)死的方式肯定不合適于我們擴(kuò)展使用,也不利于 SqlSession 中每一個(gè)新增定義的方法對(duì)池化數(shù)據(jù)源的調(diào)用。

圖 7-1 DefaultSqlSession 調(diào)用數(shù)據(jù)源

  • 解耦 DefaultSqlSession#selectOne 方法中關(guān)于對(duì)數(shù)據(jù)源的調(diào)用、執(zhí)行和結(jié)果封裝,提供新的功能模塊替代這部分硬編碼的邏輯處理。
  • 只有提供了單獨(dú)的執(zhí)行方法入口,我們才能更好的擴(kuò)展和應(yīng)對(duì)這部分內(nèi)容里的需求變化,包括了各類(lèi)入?yún)?、結(jié)果封裝、執(zhí)行器類(lèi)型、批處理等,來(lái)滿足不同樣式的用戶需求,也就是配置到 Mapper.xml 中的具體信息。

三、設(shè)計(jì)

從我們對(duì) ORM 框架漸進(jìn)式的開(kāi)發(fā)過(guò)程上,可以分出的執(zhí)行動(dòng)作包括,解析配置、代理對(duì)象、映射方法等,直至我們前面章節(jié)對(duì)數(shù)據(jù)源的包裝和使用,只不過(guò)我們把數(shù)據(jù)源的操作硬捆綁到了 DefaultSqlSession 的執(zhí)行方法上了。

那么現(xiàn)在為了解耦這塊的處理,則需要單獨(dú)提出一塊執(zhí)行器的服務(wù)功能,之后將執(zhí)行器的功能隨著 DefaultSqlSession 創(chuàng)建時(shí)傳入執(zhí)行器功能,之后具體的方法調(diào)用就可以調(diào)用執(zhí)行器來(lái)處理了,從而解耦這部分功能模塊。如圖 7-2 所示。

圖 7-2 引入執(zhí)行器解耦設(shè)計(jì)

  • 首先我們要提取出執(zhí)行器的接口,定義出執(zhí)行方法、事務(wù)獲取和相應(yīng)提交、回滾、關(guān)閉的定義,同時(shí)由于執(zhí)行器是一種標(biāo)準(zhǔn)的執(zhí)行過(guò)程,所以可以由抽象類(lèi)進(jìn)行實(shí)現(xiàn),對(duì)過(guò)程內(nèi)容進(jìn)行模板模式的過(guò)程包裝。在包裝過(guò)程中定義抽象類(lèi),由具體的子類(lèi)來(lái)實(shí)現(xiàn)。這一部分在下文的代碼中會(huì)體現(xiàn)到 SimpleExecutor 簡(jiǎn)單執(zhí)行器實(shí)現(xiàn)中。
  • 之后是對(duì) SQL 的處理,我們都知道在使用 JDBC 執(zhí)行 SQL 的時(shí)候,分為了簡(jiǎn)單處理和預(yù)處理,預(yù)處理中包括準(zhǔn)備語(yǔ)句、參數(shù)化傳遞、執(zhí)行查詢(xún),以及最后的結(jié)果封裝和返回。所以我們這里也需要把 JDBC 這部分的步驟,分為結(jié)構(gòu)化的類(lèi)過(guò)程來(lái)實(shí)現(xiàn),便于功能的拓展。具體代碼主要體現(xiàn)在語(yǔ)句處理器 StatementHandler 的接口實(shí)現(xiàn)中。

四、實(shí)現(xiàn)

1. 工程結(jié)構(gòu)

mybatis-step-06
└── src
├── main
│ └── java
│ └── cn.bugstack.mybatis
│ ├── binding
│ │ ├── MapperMethod.java
│ │ ├── MapperProxy.java
│ │ ├── MapperProxyFactory.java
│ │ └── MapperRegistry.java
│ ├── builder
│ ├── datasource
│ ├── executor
│ │ ├── resultset
│ │ │ ├── DefaultResultSetHandler.java
│ │ │ └── ResultSetHandler.java
│ │ ├── statement
│ │ │ ├── BaseStatementHandler.java
│ │ │ ├── PreparedStatementHandler.java
│ │ │ ├── SimpleStatementHandler.java
│ │ │ └── StatementHandler.java
│ │ ├── BaseExecutor.java
│ │ ├── Executor.java
│ │ └── SimpleExecutor.java
│ ├── io
│ ├── mapping
│ ├── session
│ │ ├── defaults
│ │ │ ├── DefaultSqlSession.java
│ │ │ └── DefaultSqlSessionFactory.java
│ │ ├── Configuration.java
│ │ ├── ResultHandler.java
│ │ ├── SqlSession.java
│ │ ├── SqlSessionFactory.java
│ │ ├── SqlSessionFactoryBuilder.java
│ │ └── TransactionIsolationLevel.java
│ ├── transaction
│ └── type
└── test
├── java
│ └── cn.bugstack.mybatis.test.dao
│ ├── dao
│ │ └── IUserDao.java
│ ├── po
│ │ └── User.java
│ └── ApiTest.java
└── resources
├── mapper
│ └──User_Mapper.xml
└── mybatis-config-datasource.xml

SQL方法執(zhí)行器核心類(lèi)關(guān)系,如圖 7-3 所示

圖 7-3 SQL方法執(zhí)行器核心類(lèi)關(guān)系

  • 以 Executor 接口定義為執(zhí)行器入口,確定出事務(wù)和操作和 SQL 執(zhí)行的統(tǒng)一標(biāo)準(zhǔn)接口。并以執(zhí)行器接口定義實(shí)現(xiàn)抽象類(lèi),也就是用抽象類(lèi)處理統(tǒng)一共用的事務(wù)和執(zhí)行SQL的標(biāo)準(zhǔn)流程,也就是這里定義的執(zhí)行 SQL 的抽象接口由子類(lèi)實(shí)現(xiàn)。
  • 在具體的簡(jiǎn)單 SQL 執(zhí)行器實(shí)現(xiàn)類(lèi)中,處理 doQuery 方法的具體操作過(guò)程。這個(gè)過(guò)程中則會(huì)引入進(jìn)來(lái) SQL 語(yǔ)句處理器的創(chuàng)建,創(chuàng)建過(guò)程仍有 configuration 配置項(xiàng)提供。你會(huì)發(fā)現(xiàn)很多這樣的生成處理,都來(lái)自于配置項(xiàng)
  • 當(dāng)執(zhí)行器開(kāi)發(fā)完成以后,接下來(lái)就是交給 DefaultSqlSessionFactory 開(kāi)啟 openSession 的時(shí)候隨著構(gòu)造函數(shù)參數(shù)傳遞給 DefaultSqlSession 中,這樣在執(zhí)行 DefaultSqlSession#selectOne 的時(shí)候就可以調(diào)用執(zhí)行器進(jìn)行處理了。也就由此完成解耦操作了。

2. 執(zhí)行器的定義和實(shí)現(xiàn)

執(zhí)行器分為接口、抽象類(lèi)、簡(jiǎn)單執(zhí)行器實(shí)現(xiàn)類(lèi)三部分,通常在框架的源碼中對(duì)于一些標(biāo)準(zhǔn)流程的處理,都會(huì)有抽象類(lèi)的存在。它負(fù)責(zé)提供共性功能邏輯,以及對(duì)接口方法的執(zhí)行過(guò)程進(jìn)行定義和處理,并體統(tǒng)抽象接口交由子類(lèi)實(shí)現(xiàn)。這種設(shè)計(jì)模式也被定義為模板模式。

2.1 Executor

源碼詳見(jiàn):cn.bugstack.mybatis.executor.Executor

public interface Executor {

ResultHandler NO_RESULT_HANDLER = null;

List query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);

Transaction getTransaction();

void commit(boolean required) throws SQLException;

void rollback(boolean required) throws SQLException;

void close(boolean forceRollback);

}
  • 在執(zhí)行器中定義的接口包括事務(wù)相關(guān)的處理方法和執(zhí)行SQL查詢(xún)的操作,隨著后續(xù)功能的迭代還會(huì)繼續(xù)補(bǔ)充其他的方法。

2.2 BaseExecutor 抽象基類(lèi)

源碼詳見(jiàn):cn.bugstack.mybatis.executor.BaseExecutor

public abstract class BaseExecutor implements Executor {

protected Configuration configuration;
protected Transaction transaction;
protected Executor wrapper;

private boolean closed;

protected BaseExecutor(Configuration configuration, Transaction transaction) {
this.configuration = configuration;
this.transaction = transaction;
this.wrapper = this;
}

@Override
public List query(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
if (closed) {
throw new RuntimeException("Executor was closed.");
}
return doQuery(ms, parameter, resultHandler, boundSql);
}

protected abstract List doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql);

@Override
public void commit(boolean required) throws SQLException {
if (closed) {
throw new RuntimeException("Cannot commit, transaction is already closed");
}
if (required) {
transaction.commit();
}
}

}
  • 在抽象基類(lèi)中封裝了執(zhí)行器的全部接口,這樣具體的子類(lèi)繼承抽象類(lèi)后,就不用在處理這些共性的方法。與此同時(shí)在 query 查詢(xún)方法中,封裝一些必要的流程處理,如果檢測(cè)關(guān)閉等,在 Mybatis 源碼中還有一些緩存的操作,這里暫時(shí)剔除掉,以核心流程為主。讀者伙伴在學(xué)習(xí)的過(guò)程中可以與源碼進(jìn)行對(duì)照學(xué)習(xí)。

2.3 SimpleExecutor 簡(jiǎn)單執(zhí)行器實(shí)現(xiàn)

源碼詳見(jiàn):cn.bugstack.mybatis.executor.SimpleExecutor

public class SimpleExecutor extends BaseExecutor {

public SimpleExecutor(Configuration configuration, Transaction transaction) {
super(configuration, transaction);
}

@Override
protected List doQuery(MappedStatement ms, Object parameter, ResultHandler resultHandler, BoundSql boundSql) {
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, resultHandler, boundSql);
Connection connection = transaction.getConnection();
Statement stmt = handler.prepare(connection);
handler.parameterize(stmt);
return handler.query(stmt, resultHandler);
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}

}
  • 簡(jiǎn)單執(zhí)行器 SimpleExecutor 繼承抽象基類(lèi),實(shí)現(xiàn)抽象方法 doQuery,在這個(gè)方法中包裝數(shù)據(jù)源的獲取、語(yǔ)句處理器的創(chuàng)建,以及對(duì) Statement 的實(shí)例化和相關(guān)參數(shù)設(shè)置。最后執(zhí)行 SQL 的處理和結(jié)果的返回操作。
  • 關(guān)于 StatementHandler 語(yǔ)句處理器的實(shí)現(xiàn),接下來(lái)介紹。

3. 語(yǔ)句處理器

語(yǔ)句處理器是 SQL 執(zhí)行器中依賴(lài)的部分,SQL 執(zhí)行器封裝事務(wù)、連接和檢測(cè)環(huán)境等,而語(yǔ)句處理器則是準(zhǔn)備語(yǔ)句、參數(shù)化傳遞、執(zhí)行 SQL、封裝結(jié)果的處理。

3.1 StatementHandler

源碼詳見(jiàn):cn.bugstack.mybatis.executor.statement.StatementHandler

public interface StatementHandler {

/** 準(zhǔn)備語(yǔ)句 */
Statement prepare(Connection connection) throws SQLException;

/** 參數(shù)化 */
void parameterize(Statement statement) throws SQLException;

/** 執(zhí)行查詢(xún) */
List query(Statement statement, ResultHandler resultHandler) throws SQLException;

}

語(yǔ)句處理器的核心包括了;準(zhǔn)備語(yǔ)句、參數(shù)化傳遞參數(shù)、執(zhí)行查詢(xún)的操作,這里對(duì)應(yīng)的 Mybatis 源碼中還包括了 update、批處理、獲取參數(shù)處理器等。

3.2 BaseStatementHandler 抽象基類(lèi)

源碼詳見(jiàn):cn.bugstack.mybatis.executor.statement.BaseStatementHandler

public abstract class BaseStatementHandler implements StatementHandler {

protected final Configuration configuration;
protected final Executor executor;
protected final MappedStatement mappedStatement;

protected final Object parameterObject;
protected final ResultSetHandler resultSetHandler;

protected BoundSql boundSql;

public BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, ResultHandler resultHandler, BoundSql boundSql) {
this.configuration = mappedStatement.getConfiguration();
this.executor = executor;
this.mappedStatement = mappedStatement;
this.boundSql = boundSql;

// 參數(shù)和結(jié)果集
this.parameterObject = parameterObject;
this.resultSetHandler = configuration.newResultSetHandler(executor, mappedStatement, boundSql);
}

@Override
public Statement prepare(Connection connection) throws SQLException {
Statement statement = null;
try {
// 實(shí)例化 Statement
statement = instantiateStatement(connection);
// 參數(shù)設(shè)置,可以被抽取,提供配置
statement.setQueryTimeout(350);
statement.setFetchSize(10000);
return statement;
} catch (Exception e) {
throw new RuntimeException("Error preparing statement. Cause: " + e, e);
}
}

protected abstract Statement instantiateStatement(Connection connection) throws SQLException;

}
  • 在語(yǔ)句處理器基類(lèi)中,講參數(shù)信息、結(jié)果信息進(jìn)行封裝處理。不過(guò)暫時(shí)這里我們還不會(huì)做過(guò)多的參數(shù)處理,包括jdbc的字段類(lèi)型轉(zhuǎn)換等。這部分內(nèi)容隨著我們整個(gè)執(zhí)行器的結(jié)構(gòu)建設(shè)完畢后,再進(jìn)行迭代開(kāi)發(fā)。
  • 之后是對(duì) BaseStatementHandler#prepare 方法的處理,包括定義實(shí)例化抽象方法,這個(gè)方法交由各個(gè)具體的實(shí)現(xiàn)子類(lèi)進(jìn)行處理。包括;SimpleStatementHandler 簡(jiǎn)單語(yǔ)句處理器和 PreparedStatementHandler 預(yù)處理語(yǔ)句處理器。

簡(jiǎn)單語(yǔ)句處理器只是對(duì) SQL 的最基本執(zhí)行,沒(méi)有參數(shù)的設(shè)置。

預(yù)處理語(yǔ)句處理器則是我們?cè)?JDBC 中使用的最多的操作方式,PreparedStatement 設(shè)置 SQL,傳遞參數(shù)的設(shè)置過(guò)程。

3.3 PreparedStatementHandler 預(yù)處理語(yǔ)句處理器

源碼詳見(jiàn):cn.bugstack.mybatis.executor.statement.PreparedStatementHandler

public class PreparedStatementHandler extends BaseStatementHandler{

@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();
return connection.prepareStatement(sql);
}

@Override
public void parameterize(Statement statement) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.setLong(1, Long.parseLong(((Object[]) parameterObject)[0].toString()));
}

@Override
public List query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
ps.execute();
return resultSetHandler. handleResultSets(ps);
}

}
  • 在預(yù)處理語(yǔ)句處理器中包括 instantiateStatement 預(yù)處理 SQL、parameterize 設(shè)置參數(shù),以及 query 查詢(xún)的執(zhí)行的操作。
  • 這里需要注意 parameterize 設(shè)置參數(shù)中還是寫(xiě)死的處理,后續(xù)這部分再進(jìn)行完善。
  • query 方法則是執(zhí)行查詢(xún)和對(duì)結(jié)果的封裝,結(jié)果的封裝目前也是比較簡(jiǎn)單的處理,只是把我們前面章節(jié)中對(duì)象的內(nèi)容摘取出來(lái)進(jìn)行封裝,這部分暫時(shí)沒(méi)有改變。都放在后續(xù)進(jìn)行完善處理。

4. 執(zhí)行器創(chuàng)建和使用

執(zhí)行器開(kāi)發(fā)完成以后,則需要在串聯(lián)到 DefaultSqlSession 中進(jìn)行使用,那么這個(gè)串聯(lián)過(guò)程就需要在 創(chuàng)建 DefaultSqlSession 的時(shí)候,構(gòu)建出執(zhí)行器并作為參數(shù)傳遞進(jìn)去。那么這塊就涉及到 DefaultSqlSessionFactory#openSession 的處理。

4.1 開(kāi)啟執(zhí)行器

源碼詳見(jiàn):cn.bugstack.mybatis.session.defaults.DefaultSqlSessionFactory

public class DefaultSqlSessionFactory implements SqlSessionFactory {

private final Configuration configuration;

public DefaultSqlSessionFactory(Configuration configuration) {
this.configuration = configuration;
}

@Override
public SqlSession openSession() {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
TransactionFactory transactionFactory = environment.getTransactionFactory();
tx = transactionFactory.newTransaction(configuration.getEnvironment().getDataSource(), TransactionIsolationLevel.READ_COMMITTED, false);
// 創(chuàng)建執(zhí)行器
final Executor executor = configuration.newExecutor(tx);
// 創(chuàng)建DefaultSqlSession
return new DefaultSqlSession(configuration, executor);
} catch (Exception e) {
try {
assert tx != null;
tx.close();
} catch (SQLException ignore) {
}
throw new RuntimeException("Error opening session. Cause: " + e);
}
}

}
  • 在 openSession 中開(kāi)啟事務(wù)傳遞給執(zhí)行器的創(chuàng)建,關(guān)于執(zhí)行器的創(chuàng)建具體可以參考 configuration.newExecutor 代碼,這部分沒(méi)有太多復(fù)雜的邏輯。讀者可以參考源碼進(jìn)行學(xué)習(xí)。
  • 在執(zhí)行器創(chuàng)建完畢后,則是把參數(shù)傳遞給 DefaultSqlSession,這樣就把整個(gè)過(guò)程串聯(lián)起來(lái)了。

4.2 使用執(zhí)行器

源碼詳見(jiàn):cn.bugstack.mybatis.session.defaults.DefaultSqlSession

public class DefaultSqlSession implements SqlSession {

private Configuration configuration;
private Executor executor;

public DefaultSqlSession(Configuration configuration, Executor executor) {
this.configuration = configuration;
this.executor = executor;
}

@Override
public T selectOne(String statement, Object parameter) {
MappedStatement ms = configuration.getMappedStatement(statement);
List list = executor.query(ms, parameter, Executor.NO_RESULT_HANDLER, ms.getBoundSql());
return list.get(0);
}

}

好了,經(jīng)過(guò)上面執(zhí)行器的所有實(shí)現(xiàn)完成后,接下來(lái)就是解耦后的調(diào)用了。在 DefaultSqlSession#selectOne 中獲取 MappedStatement 映射語(yǔ)句類(lèi)后,則傳遞給執(zhí)行器進(jìn)行處理,那么現(xiàn)在這個(gè)類(lèi)經(jīng)過(guò)設(shè)計(jì)思想的解耦后,就變得更加趕緊整潔了,也就是易于維護(hù)和擴(kuò)展了。

五、測(cè)試

1. 事先準(zhǔn)備

1.1 創(chuàng)建庫(kù)表

創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)名稱(chēng)為 mybatis 并在庫(kù)中創(chuàng)建表 user 以及添加測(cè)試數(shù)據(jù),如下:

CREATE TABLE
USER
(
id bigint NOT NULL AUTO_INCREMENT COMMENT '自增ID',
userId VARCHAR(9) COMMENT '用戶ID',
userHead VARCHAR(16) COMMENT '用戶頭像',
createTime TIMESTAMP NULL COMMENT '創(chuàng)建時(shí)間',
updateTime TIMESTAMP NULL COMMENT '更新時(shí)間',
userName VARCHAR(64),
PRIMARY KEY (id)
)
ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into user (id, userId, userHead, createTime, updateTime, userName) values (1, '10001', '1_04', '2022-04-13 00:00:00', '2022-04-13 00:00:00', '小傅哥');

1.2 配置數(shù)據(jù)源











  • 通過(guò) mybatis-config-datasource.xml 配置數(shù)據(jù)源信息,包括:driver、url、username、password。
  • 在這里 dataSource 可以按需配置成 DRUID、UNPOOLED 和 POOLED 進(jìn)行測(cè)試驗(yàn)證。

1.3 配置Mapper

這部分暫時(shí)不需要調(diào)整,目前還只是一個(gè)入?yún)⒌念?lèi)型的參數(shù),后續(xù)我們?nèi)客晟七@部分內(nèi)容以后,則再提供更多的其他參數(shù)進(jìn)行驗(yàn)證。

2. 單元測(cè)試

@Test
public void test_SqlSessionFactory() throws IOException {
// 1. 從SqlSessionFactory中獲取SqlSession
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config-datasource.xml"));
SqlSession sqlSession = sqlSessionFactory.openSession();

// 2. 獲取映射器對(duì)象
IUserDao userDao = sqlSession.getMapper(IUserDao.class);

// 3. 測(cè)試驗(yàn)證
User user = userDao.queryUserInfoById(1L);
logger.info("測(cè)試結(jié)果:{}", JSON.toJSONString(user));
}
  • 在單元測(cè)試中沒(méi)有什么變化,只是我們?nèi)耘f是傳遞一個(gè) 1L 的 long 類(lèi)型參數(shù),進(jìn)行方法的調(diào)用處理。通過(guò)單元測(cè)試驗(yàn)證執(zhí)行器的處理過(guò)程,讀者在學(xué)習(xí)的過(guò)程中可以進(jìn)行斷點(diǎn)測(cè)試,學(xué)習(xí)每個(gè)過(guò)程的處理內(nèi)容。

測(cè)試結(jié)果

22:16:25.770 [main] INFO  c.b.m.d.pooled.PooledDataSource - PooledDataSource forcefully closed/removed all connections.
22:16:26.076 [main] INFO c.b.m.d.pooled.PooledDataSource - Created connection 540642172.
22:16:26.198 [main] INFO cn.bugstack.mybatis.test.ApiTest - 測(cè)試結(jié)果:{"id":1,"userHead":"1_04","userId":"10001","userName":"小傅哥"}

Process finished with exit code 0

  • 從測(cè)試結(jié)果看我們已經(jīng)可以把 DefaultSqlSession#selectOne 中的調(diào)用,換成執(zhí)行器完成整個(gè)過(guò)程的處理了,解耦了這部分的邏輯操作,也能方便我們后續(xù)的擴(kuò)展。

六、總結(jié)

整個(gè)章節(jié)的實(shí)現(xiàn)都是在處理解耦這件事情,從 DefaultSqlSession#selectOne 對(duì)數(shù)據(jù)源的處理解耦到執(zhí)行器中進(jìn)行操作。而執(zhí)行器中又包括了對(duì) JDBC 處理的拆解,鏈接、準(zhǔn)備語(yǔ)句、封裝參數(shù)、處理結(jié)果,所有的這些過(guò)程經(jīng)過(guò)解耦后的類(lèi)和方法,就都可以在以后的功能迭代中非常方便的完成擴(kuò)展了。

本章節(jié)也為我們后續(xù)擴(kuò)展參數(shù)的處理、結(jié)果集的封裝預(yù)留出了擴(kuò)展點(diǎn),以及對(duì)于不同的語(yǔ)句處理器選擇的問(wèn)題,都需要在后續(xù)進(jìn)行完善和補(bǔ)充。目前我們串聯(lián)出來(lái)的是最核心的骨架結(jié)構(gòu),隨著后續(xù)的漸進(jìn)式開(kāi)發(fā)陸續(xù)迭代完善。

對(duì)于源碼的學(xué)習(xí),讀者要經(jīng)歷看、寫(xiě)、思考、應(yīng)用等幾個(gè)步驟的過(guò)程,才能更好的吸收這里面的思想,不只是照著CP一遍就完事了,否則也就失去了跟著學(xué)習(xí)源碼的意義。


分享名稱(chēng):SQL執(zhí)行器的定義和實(shí)現(xiàn)
標(biāo)題來(lái)源:http://www.dlmjj.cn/article/dhshoie.html