MyBatis中操作類對象的實現(xiàn)
Mapper.xml當(dāng)中的SQL標(biāo)簽都被解析成了一個一個的 MappedStatement對象。那么我們當(dāng)中的SQL是基于什么形式進行封裝的呢?
在Java中,Java當(dāng)中一切皆對象。MappedStatement當(dāng)中SQL被封裝成了 MappedStatement 當(dāng)中的SqlSource對象。
我們通過sqlSource.getBoundSql()來獲取一個BoundSql對象,BoundSQL當(dāng)中的對象就是對于SQL語句的真實封裝。
Cofiguration 和 MappedStatement 存儲的是我們配置文件或者是在注解當(dāng)中書寫的配置信息。它們是一個存儲對象。
我們還要有操作類型的對象。例如:在configuration中的mybatis-config.xml 和 我們的mapper的映射文件,xxxmapper.xml等配置文件。
1.什么是MyBatis操作類對象
在MyBatis框架中,操作類對象是用于執(zhí)行數(shù)據(jù)庫操作的核心對象。它是通過Mapper接口或者XML文件定義的,用于執(zhí)行與數(shù)據(jù)庫相關(guān)的CRUD操作(增刪改查)。
操作類對象的作用是提供一組方法,用于執(zhí)行數(shù)據(jù)庫操作并返回結(jié)果。這些方法可以是對應(yīng)于數(shù)據(jù)庫表的增刪改查操作,也可以是自定義的SQL查詢操作。
在MyBatis中,操作類對象有兩種定義方式:
- Mapper接口: 通過定義Java接口并使用注解或XML進行映射,創(chuàng)建一個Mapper接口。Mapper接口中的方法可以通過注解或XML配置與具體的SQL語句進行映射。開發(fā)人員可以通過Java代碼直接調(diào)用接口中的方法來執(zhí)行數(shù)據(jù)庫操作。
- XML文件: 通過編寫XML文件來定義SQL語句和數(shù)據(jù)庫操作,這些XML文件可以是獨立的SQL映射文件,也可以是Mapper接口對應(yīng)的XML配置文件。XML文件中定義了SQL語句的具體內(nèi)容和參數(shù)映射關(guān)系,開發(fā)人員可以通過讀取和解析這些XML文件來執(zhí)行數(shù)據(jù)庫操作。
操作類對象可以通過MyBatis的Configuration對象或者SqlSessionFactory對象來獲取。一旦獲取到操作類對象,開發(fā)人員就可以使用它提供的方法來執(zhí)行數(shù)據(jù)庫操作。操作類對象會將數(shù)據(jù)庫操作的細節(jié)封裝起來,使開發(fā)人員只需關(guān)注業(yè)務(wù)邏輯而不需要關(guān)心具體的SQL語句和數(shù)據(jù)庫連接操作。
總而言之,MyBatis操作類對象是用于執(zhí)行數(shù)據(jù)庫操作的核心對象,通過Mapper接口或者XML文件的定義,提供了一組方法來執(zhí)行與數(shù)據(jù)庫相關(guān)的增刪改查操作。它簡化了數(shù)據(jù)庫操作的代碼編寫,提高了開發(fā)效率和可維護性。
2.MyBatis操作類對象的分類
2.1 Executor
什么是Executor?
Excutor是執(zhí)行器的意思,什么是執(zhí)行器,執(zhí)行器就是完成各種操作的對象。Executor是執(zhí)行具體的SQL語句的核心組件之一。它負責(zé)接收Mapper接口或XML文件定義的SQL語句,并將其轉(zhuǎn)換為JDBC的PreparedStatement對象或Statement對象來執(zhí)行數(shù)據(jù)庫操作。
Executor主要有以下幾種分類:
2.1.1 BaseExecutor
- BaseExecutor :實現(xiàn)了Executor的全部方法,包括對緩存,事務(wù),連接提供了一系列的模板方法, 這些模板方法中留出來了四個抽象的方法等待子類去實現(xiàn)如下
protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException; protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException; protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException; protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;
2.1.2 CachingExecutor
CachingExecutor是Executor的一種實現(xiàn),它的作用是提供緩存機制,用于緩存SQL語句的執(zhí)行結(jié)果。CachingExecutor會在執(zhí)行SQL語句之前先檢查緩存中是否存在相同的SQL語句及其參數(shù),如果存在,則直接從緩存中獲取結(jié)果,而不再執(zhí)行數(shù)據(jù)庫操作。
CachingExecutor的主要作用是提高系統(tǒng)的性能和響應(yīng)速度,避免對數(shù)據(jù)庫的頻繁訪問。它可以減少與數(shù)據(jù)庫的交互次數(shù),降低數(shù)據(jù)庫的壓力,提高系統(tǒng)的吞吐量。
2.1.3 SimpleExecutor
SimpleExecutor是MyBatis默認的Executor,它是在每次執(zhí)行SQL語句時創(chuàng)建一個新的Statement對象。它不支持事務(wù)處理,每次執(zhí)行SQL語句都會進行數(shù)據(jù)庫連接的獲取和釋放。這種Executor適用于簡單的、非事務(wù)性的SQL操作。
特點是每次執(zhí)行完畢后都會將創(chuàng)建出來的statement關(guān)閉掉,他也是默認的執(zhí)行器類型。
SimpleExecutor是 MyBatis 提供的默認的執(zhí)行器,他里面封裝了MyBatis對JDBC的操作,但是雖然他叫XXXExecuto,但是真正去CRUD的還真不是SimpleExecutor,先看一下它是如何重寫 BaseExecuto的doQuery()方法的
@Override public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException { Statement stmt = null; try { Configuration configuration = ms.getConfiguration(); StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql); stmt = prepareStatement(handler, ms.getStatementLog()); return handler.<E>query(stmt, resultHandler); } finally { closeStatement(stmt); } }
首先會創(chuàng)建StatementHandler
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql); statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler); return statementHandler; }
雖然表面上看上面的代碼,感覺它只會創(chuàng)建一個叫 RoutingStatementHandler 的handler,但是其實上這里面有個秘密,根據(jù)MappedStatement 的不同,實際上他會創(chuàng)建三種不同類型的處理器,如下:
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) { 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()); } }
最后通過closeStatement來關(guān)閉Statement
protected void closeStatement(Statement statement) { if (statement != null) { try { if (!statement.isClosed()) { statement.close(); } } catch (SQLException e) { // ignore } } }
2.1.4 ReuseExecutor
ReuseExecutor是Executor的一種實現(xiàn),它會重用已經(jīng)創(chuàng)建的Statement對象。它會對SQL語句進行緩存,當(dāng)下次執(zhí)行相同的SQL語句時,會直接使用已經(jīng)創(chuàng)建的Statement對象。這種Executor適用于相同SQL語句頻繁執(zhí)行的場景,可以減少Statement對象的創(chuàng)建和銷毀開銷,提高性能。
在它在本地維護了一個容器,用來存放針對每條sql創(chuàng)建出來的statement,下次執(zhí)行相同的sql時,會先檢查容器中是否存在相同的sql,如果存在就使用現(xiàn)成的,不再重復(fù)獲取。
應(yīng)用場景示例:
- 頻繁查詢靜態(tài)數(shù)據(jù):對于一些不經(jīng)常變化的靜態(tài)數(shù)據(jù),如國家、省份、城市等信息,可以使用CachingExecutor進行緩存。這樣可以避免每次查詢都去數(shù)據(jù)庫中查詢,提高查詢效率。
- 頻繁查詢的結(jié)果集不變:對于一些業(yè)務(wù)場景中,某些查詢結(jié)果集在一段時間內(nèi)是不變的,如熱門商品、廣告信息等。可以使用CachingExecutor緩存這些查詢結(jié)果,減少數(shù)據(jù)庫的訪問次數(shù),提高性能。
需要注意的是,CachingExecutor使用緩存機制可以提高性能,但同時也可能引入數(shù)據(jù)一致性的問題。因為當(dāng)對數(shù)據(jù)庫進行增刪改操作時,會導(dǎo)致緩存數(shù)據(jù)的失效。因此,在使用CachingExecutor時,需要根據(jù)業(yè)務(wù)需求合理設(shè)置緩存的有效期,以保證數(shù)據(jù)的一致性。
在MyBatis中,默認情況下是不開啟CachingExecutor的,如果需要使用緩存機制,可以通過配置文件或注解來開啟和配置緩存。
源碼解析:
這個ReuseExecutor相對于SimpleExecutor來說,不同點就是它先來的對Statement的復(fù)用,換句話說,某條Sql對應(yīng)的Statement創(chuàng)建出來后被放在容器中保存起來,再有使用這個statement的地方就是容器中拿就行了
他是怎么實現(xiàn)的呢? 看看下面的代碼就知道了
public class ReuseExecutor extends BaseExecutor { private final Map<String, Statement> statementMap = new HashMap<String, Statement>(); public ReuseExecutor(Configuration configuration, Transaction transaction) { super(configuration, transaction); }
嗯! 所謂的容器,不過是一個叫statementMap的HashMap而已
下一個問題: 這個容器什么時候派上用場呢? 看看下面的代碼也就知道了–this.hasStatementFor(sql)
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; BoundSql boundSql = handler.getBoundSql(); String sql = boundSql.getSql(); if (hasStatementFor(sql)) { stmt = getStatement(sql); applyTransactionTimeout(stmt); } else { Connection connection = getConnection(statementLog); stmt = handler.prepare(connection, transaction.getTimeout()); putStatement(sql, stmt); } handler.parameterize(stmt); return stmt; }
最后一點: 當(dāng)MyBatis知道發(fā)生了事務(wù)的提交,回滾等操作時,ReuseExecutor會批量關(guān)閉容器中的Statement
2.1.5 BatchExecutor
BatchExecutor是Executor的一種實現(xiàn),它用于批量操作數(shù)據(jù)庫。它會將多個SQL語句進行批量執(zhí)行,減少了與數(shù)據(jù)庫的交互次數(shù),提高了性能。BatchExecutor適用于需要批量插入、更新或刪除數(shù)據(jù)的場景。
特點是進行批量修改,她會將修改操作記錄在本地,等待程序觸發(fā)提交事務(wù),或者是觸發(fā)下一次查詢時,批量執(zhí)行修改。
2.2 StatementHandler
Statementhandler是四大神器中最重要的一個對象,負責(zé)操作Statement與數(shù)據(jù)庫進行交流.在工作時 還會使用ParameterHandler進行參數(shù)配置,使用ResultHandler將查詢結(jié)果與實體類對象進行綁定。StatementHandler是MyBatis的核心組件之一,負責(zé)處理SQL語句的執(zhí)行和結(jié)果的映射。它是在執(zhí)行SQL語句之前進行參數(shù)處理和SQL語句的構(gòu)建,然后執(zhí)行SQL語句,最后將結(jié)果映射到Java對象上。
StatementHandler的主要職責(zé)包括以下幾個方面:
- 參數(shù)處理:StatementHandler會將用戶傳入的參數(shù)與SQL語句中的占位符進行匹配,構(gòu)建最終的可執(zhí)行的SQL語句。
- SQL語句的構(gòu)建:根據(jù)映射文件或注解中定義的SQL語句,StatementHandler會將參數(shù)替換為實際的值,生成最終的可執(zhí)行的SQL語句。
- SQL語句的執(zhí)行:StatementHandler將生成的SQL語句交給JDBC的Statement對象執(zhí)行,獲取執(zhí)行結(jié)果。
- 結(jié)果映射:StatementHandler將執(zhí)行結(jié)果映射到Java對象上,生成最終的結(jié)果。
StatementHandler的應(yīng)用場景:
- 執(zhí)行簡單的SQL操作:StatementHandler適用于執(zhí)行簡單的SQL操作,如查詢、插入、更新和刪除等。
- 自定義SQL語句:如果需要執(zhí)行的SQL語句不方便通過MyBatis的自動映射機制來處理,可以使用StatementHandler來自定義SQL語句,實現(xiàn)更靈活的數(shù)據(jù)庫操作。
- 復(fù)雜的結(jié)果映射:如果需要將查詢結(jié)果映射到多個Java對象中,或者需要進行一些特殊的結(jié)果處理,可以通過自定義StatementHandler來實現(xiàn)復(fù)雜的結(jié)果映射邏輯。
需要注意的是,大多數(shù)情況下,我們不需要直接操作和使用StatementHandler,MyBatis框架會自動創(chuàng)建和管理StatementHandler對象。只有在需要實現(xiàn)一些特殊的數(shù)據(jù)庫操作時,才需要自定義或擴展StatementHandler。通常情況下,我們只需要編寫Mapper接口或XML文件,定義SQL語句和結(jié)果映射規(guī)則,MyBatis會自動處理和執(zhí)行SQL語句。
后續(xù)會繼續(xù)通過跟進源碼分析…
2.3 ParameterHandler
ParameterHandler是MyBatis的核心組件之一,它負責(zé)處理SQL語句中的參數(shù)傳遞和設(shè)置。ParameterHandler主要用于將Java對象的屬性值設(shè)置到PreparedStatement中,替換SQL語句中的占位符。
ParameterHandler的主要職責(zé)包括以下幾個方面:
- 參數(shù)處理:ParameterHandler將用戶傳入的參數(shù)與SQL語句中的占位符進行匹配,根據(jù)占位符的位置和類型,將Java對象的屬性值設(shè)置到PreparedStatement中。
- 參數(shù)類型處理:ParameterHandler負責(zé)處理不同類型的參數(shù),將Java對象的屬性值轉(zhuǎn)換為對應(yīng)的數(shù)據(jù)庫類型,以便正確地設(shè)置到PreparedStatement中。
- 參數(shù)傳遞:ParameterHandler將處理后的參數(shù)傳遞給JDBC的PreparedStatement對象,以便執(zhí)行SQL語句。
ParameterHandler的應(yīng)用場景:
- SQL語句的參數(shù)傳遞:ParameterHandler適用于處理SQL語句中的參數(shù)傳遞,將Java對象的屬性值設(shè)置到PreparedStatement中。這包括簡單的參數(shù)類型,如字符串、整數(shù)、日期等,也包括復(fù)雜的參數(shù)類型,如Java對象、集合等。
- 動態(tài)SQL語句的構(gòu)建:如果SQL語句是動態(tài)生成的,根據(jù)不同的條件拼接不同的SQL片段,ParameterHandler可以根據(jù)條件設(shè)置對應(yīng)的參數(shù)值,確保動態(tài)生成的SQL語句的正確執(zhí)行。
- 參數(shù)類型轉(zhuǎn)換:如果涉及到不同的數(shù)據(jù)類型,如Java對象屬性與數(shù)據(jù)庫字段類型不一致,或需要將Java對象屬性值轉(zhuǎn)換為數(shù)據(jù)庫支持的類型,ParameterHandler可以負責(zé)處理參數(shù)類型轉(zhuǎn)換,確保參數(shù)的正確性。
需要注意的是,大多數(shù)情況下,我們不需要直接操作和使用ParameterHandler,MyBatis框架會自動創(chuàng)建和管理ParameterHandler對象。只有在需要實現(xiàn)一些特殊的參數(shù)處理或轉(zhuǎn)換操作時,才需要自定義或擴展ParameterHandler。通常情況下,我們只需要編寫Mapper接口或XML文件,定義SQL語句和參數(shù)映射規(guī)則,MyBatis會自動處理和設(shè)置參數(shù)。
后續(xù)會繼續(xù)通過跟進源碼分析…
2.4 ResultSetHandler
ResultSetHandler是MyBatis的核心組件之一,它負責(zé)將JDBC查詢結(jié)果集中的數(shù)據(jù)映射到Java對象上。ResultSetHandler將ResultSet中的每一行數(shù)據(jù)轉(zhuǎn)換為Java對象,并將這些對象組成一個集合或數(shù)組,作為最終的查詢結(jié)果返回。
ResultSetHandler的主要職責(zé)包括以下幾個方面:
- 結(jié)果集處理:ResultSetHandler將JDBC的ResultSet對象中的數(shù)據(jù)進行處理,將每一行數(shù)據(jù)轉(zhuǎn)換為Java對象。
- 映射規(guī)則:ResultSetHandler根據(jù)映射文件或注解中定義的結(jié)果映射規(guī)則,將ResultSet中的列與Java對象的屬性進行映射。
- 數(shù)據(jù)類型轉(zhuǎn)換:ResultSetHandler負責(zé)將ResultSet中的數(shù)據(jù)轉(zhuǎn)換為Java對象的屬性類型,確保類型的匹配和正確性。
- 結(jié)果集的封裝:ResultSetHandler將處理后的Java對象封裝成一個集合或數(shù)組,作為最終的查詢結(jié)果返回給用戶。
ResultSetHandler的應(yīng)用場景:
- 查詢操作:ResultSetHandler適用于執(zhí)行查詢操作,將查詢結(jié)果映射到Java對象上,并返回給用戶。
- 數(shù)據(jù)類型轉(zhuǎn)換:如果查詢結(jié)果中的數(shù)據(jù)類型與Java對象的屬性類型不匹配,或者需要進行一些特殊的數(shù)據(jù)類型轉(zhuǎn)換,ResultSetHandler可以負責(zé)處理數(shù)據(jù)類型的轉(zhuǎn)換,確保數(shù)據(jù)的正確映射。
- 結(jié)果集的定制化:如果需要將查詢結(jié)果映射到多個Java對象中,或者需要進行一些特殊的結(jié)果處理,如對結(jié)果進行分組、排序、聚合等,可以通過自定義ResultSetHandler來實現(xiàn)定制化的結(jié)果集處理邏輯。
需要注意的是,大多數(shù)情況下,我們不需要直接操作和使用ResultSetHandler,MyBatis框架會自動創(chuàng)建和管理ResultSetHandler對象。只有在需要實現(xiàn)一些特殊的結(jié)果集處理邏輯時,才需要自定義或擴展ResultSetHandler。通常情況下,我們只需要編寫Mapper接口或XML文件,定義SQL語句和結(jié)果映射規(guī)則,MyBatis會自動處理和映射結(jié)果集。
后續(xù)會繼續(xù)通過跟進源碼分析…
2.5 TypeHandler
TypeHandler是MyBatis的核心組件之一,它負責(zé)處理Java對象與數(shù)據(jù)庫類型之間的轉(zhuǎn)換。TypeHandler將Java對象的屬性值轉(zhuǎn)換為數(shù)據(jù)庫支持的數(shù)據(jù)類型,并且在從數(shù)據(jù)庫中讀取數(shù)據(jù)時將其轉(zhuǎn)換為Java對象的屬性類型。
TypeHandler的主要職責(zé)包括以下幾個方面:
- 參數(shù)設(shè)置:TypeHandler負責(zé)將Java對象的屬性值轉(zhuǎn)換為對應(yīng)的數(shù)據(jù)庫類型,將參數(shù)設(shè)置到PreparedStatement中。
- 結(jié)果獲?。篢ypeHandler負責(zé)從ResultSet中獲取數(shù)據(jù)庫的值,并將其轉(zhuǎn)換為Java對象的屬性類型。
- JDBC類型處理:TypeHandler根據(jù)Java對象的屬性類型和數(shù)據(jù)庫支持的類型,進行類型之間的轉(zhuǎn)換,確保數(shù)據(jù)的正確性和兼容性。
TypeHandler的應(yīng)用場景:
- 自定義數(shù)據(jù)類型映射:如果在數(shù)據(jù)庫中使用了一些非標(biāo)準(zhǔn)或自定義的數(shù)據(jù)類型,TypeHandler可以幫助我們將這些類型映射到Java對象的屬性類型上,實現(xiàn)數(shù)據(jù)庫類型與Java類型之間的轉(zhuǎn)換。
- 數(shù)據(jù)類型轉(zhuǎn)換:如果Java對象的屬性類型與數(shù)據(jù)庫字段類型不匹配,或者需要進行一些特殊的數(shù)據(jù)類型轉(zhuǎn)換,TypeHandler可以負責(zé)處理數(shù)據(jù)類型的轉(zhuǎn)換,確保數(shù)據(jù)的正確映射。
- 復(fù)雜數(shù)據(jù)結(jié)構(gòu)的映射:如果Java對象的屬性是一個復(fù)雜的數(shù)據(jù)結(jié)構(gòu),如JSON字符串、XML字符串等,TypeHandler可以負責(zé)將這些復(fù)雜結(jié)構(gòu)轉(zhuǎn)換為數(shù)據(jù)庫支持的數(shù)據(jù)類型,以及從數(shù)據(jù)庫中讀取并轉(zhuǎn)換為Java對象的屬性類型。
- 枚舉類型的映射:MyBatis內(nèi)置了一些常用的TypeHandler,可以直接映射Java的枚舉類型到數(shù)據(jù)庫中的數(shù)字或字符串類型。
需要注意的是,MyBatis提供了一些內(nèi)置的TypeHandler,用于處理常見的數(shù)據(jù)類型轉(zhuǎn)換,如字符串、整數(shù)、日期等。對于自定義的數(shù)據(jù)類型,我們可以通過實現(xiàn)TypeHandler接口來自定義TypeHandler,并注冊到MyBatis的配置中。這樣,在執(zhí)行SQL語句時,MyBatis就會自動調(diào)用相應(yīng)的TypeHandler來進行數(shù)據(jù)類型的轉(zhuǎn)換。
后續(xù)會繼續(xù)通過跟進源碼分析…
3.總結(jié)
到此這篇關(guān)于MyBatis中操作類對象的實現(xiàn)的文章就介紹到這了,更多相關(guān)MyBatis 操作類對象內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
springcloud?如何解決微服務(wù)之間token傳遞問題
這篇文章主要介紹了springcloud?如何解決微服務(wù)之間token傳遞問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03