在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

MyBatis的實現原理

lhl545545 ? 來源:電子發燒友網 ? 2018-02-24 11:25 ? 次閱讀

MyBatis的實現原理

mybatis底層還是采用原生jdbc來對數據庫進行操作的,只是通過 SqlSessionFactory,SqlSession Executor,StatementHandler,ParameterHandler,ResultHandler和TypeHandler等幾個處理器封裝了這些過程

執行器:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

參數處理器: ParameterHandler (getParameterObject, setParameters

結構處理器 ResultSetHandler (handleResultSets, handleOutputParameters)

sql查詢處理器:StatementHandler (prepare, parameterize, batch, update, query)

其中StatementHandler用通過ParameterHandler與ResultHandler分別進行參數預編譯 與結果處理。而ParameterHandler與ResultHandler都使用TypeHandler進行映射。如下圖:

MyBatis的實現原理

Mybatis工作過程

通過讀mybatis的源碼進行分析mybatis的執行操作的整個過程,我們通過debug調試就可以知道Mybatis每一步做了什么事,我先把debug每一步結果 截圖,然后在分析這個流程。

第一步:讀取配置文件,形成InputStream

1 創建SqlSessionFacotry的過程

MyBatis的實現原理

從debug調試看出 返回的 sqlSessionFactory 是DefaultSesssionFactory類型的,但是configuration此時已經被初始化了。查看源碼后畫如下創建DefaultSessionFactory的時序圖:

MyBatis的實現原理

2 創建SqlSession的過程

MyBatis的實現原理

從debug調試 看出SqlSessinoFactory.openSession() 返回的sqlSession是 DefaultSession類型的,此SqlSession里包含一個Configuration的對象,和一個Executor對象。查看源碼后畫如下創建DefaultSession的時序圖:

MyBatis的實現原理

3 創建Mapper的過程

MyBatis的實現原理

從debug調試可以看出,mapper是一個Mapper代理對象,而且初始化了Configuration對象,Executor的對象。查看源碼后畫如下創建Mapper的時序圖:

MyBatis的實現原理

4 執行CRUD過程

1 以select為例查看各步執行的源碼

1.mapper.selectEmployeeList()其實是MapperProxy執行invoke方法,此方法顯示是判斷Method的方法是不是Object的toString等方法如果不是就執行MapperMethod

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

// 判斷Method的方法是不是Object的toString等方法

if(Object.class.equals(method.getDeclaringClass())) {

try {

return method.invoke(this, args);

} catch (Throwable var5) {

throw ExceptionUtil.unwrapThrowable(var5);

}

} else {

//判斷private final Map

MapperMethod mapperMethod = this.cachedMapperMethod(method);

return mapperMethod.execute(this.sqlSession, args);

}

}

//查詢一級緩存和設置一級緩存

private MapperMethod cachedMapperMethod(Method method) {

MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method);

if(mapperMethod == null) {

mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration());

this.methodCache.put(method, mapperMethod);

}

return mapperMethod;

}

經過上面的調用后進入MapperMethod里面執行

//判斷sql命令類型

public Object execute(SqlSession sqlSession, Object[] args) {

Object param;

Object result;

if(SqlCommandType.INSERT == this.command.getType()) {

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));

} else if(SqlCommandType.UPDATE == this.command.getType()) {

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.update(this.command.getName(), param));

} else if(SqlCommandType.DELETE == this.command.getType()) {

param = this.method.convertArgsToSqlCommandParam(args);

result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));

} else if(SqlCommandType.SELECT == this.command.getType()) {

//我們測試的是select類型,則再判斷這個方法的返回類型

if(this.method.returnsVoid() && this.method.hasResultHandler()) {

this.executeWithResultHandler(sqlSession, args);

result = null;

} else if(this.method.returnsMany()) {

//我們是查詢列表,此方法執行

result = this.executeForMany(sqlSession, args);

} else if(this.method.returnsMap()) {

result = this.executeForMap(sqlSession, args);

} else {

param = this.method.convertArgsToSqlCommandParam(args);

result = sqlSession.selectOne(this.command.getName(), param);

}

} else {

if(SqlCommandType.FLUSH != this.command.getType()) {

throw new BindingException(“Unknown execution method for: ” + this.command.getName());

}

result = sqlSession.flushStatements();

}

if(result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {

throw new BindingException(“Mapper method ‘” + this.command.getName() + “ attempted to return null from a method with a primitive return type (” + this.method.getReturnType() + “)。”);

} else {

return result;

}

}

private

//將param做處理 自動處理為param1,param2.。

Object param = this.method.convertArgsToSqlCommandParam(args);

List result;

if(this.method.hasRowBounds()) {

RowBounds rowBounds = this.method.extractRowBounds(args);

//調用該對象的DefaultSqlSession的selectList方法

result = sqlSession.selectList(this.command.getName(), param, rowBounds);

} else {

result = sqlSession.selectList(this.command.getName(), param);

}

return !this.method.getReturnType().isAssignableFrom(result.getClass())?(this.method.getReturnType().isArray()?this.convertToArray(result):this.convertToDeclaredCollection(sqlSession.getConfiguration(), result)):result;

}

//處理參數方法

public Object convertArgsToSqlCommandParam(Object[] args) {

int paramCount = this.params.size();

if(args != null && paramCount != 0) {

if(!this.hasNamedParameters && paramCount == 1) {

return args[((Integer)this.params.keySet().iterator().next()).intValue()];

} else {

Map

int i = 0;

for(Iterator i$ = this.params.entrySet().iterator(); i$.hasNext(); ++i) {

Entry

param.put(entry.getValue(), args[((Integer)entry.getKey()).intValue()]);

String genericParamName = “param” + String.valueOf(i + 1);

if(!param.containsKey(genericParamName)) {

param.put(genericParamName, args[((Integer)entry.getKey()).intValue()]);

}

}

return param;

}

} else {

return null;

}

}

調用DefaultSqlSession的selectList的方法

public

List var5;

try {

//獲取MappedStatement對象

MappedStatement ms = this.configuration.getMappedStatement(statement);

//調用cachingExecutor執行器的方法

var5 = this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);

} catch (Exception var9) {

throw ExceptionFactory.wrapException(“Error querying database. Cause: ” + var9, var9);

} finally {

ErrorContext.instance().reset();

}

return var5;

}

//CachingExector的query方法

public

//

BoundSql boundSql = ms.getBoundSql(parameterObject);

CacheKey key = this.createCacheKey(ms, parameterObject, rowBounds, boundSql);

//調用下2代碼

return this.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

//2代碼

public

Cache cache = ms.getCache();

if(cache != null) {

this.flushCacheIfRequired(ms);

if(ms.isUseCache() && resultHandler == null) {

this.ensureNoOutParams(ms, parameterObject, boundSql);

List

if(list == null) {

//這里是調用Executor里的query方法 如果開啟了緩存這掉CachingExecutor的 如果沒有則是調用BaseExecutor的

list = this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

this.tcm.putObject(cache, key, list);

}

return list;

}

}

return this.delegate.query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);

}

BaseExecutor的query方法

public

ErrorContext.instance().resource(ms.getResource()).activity(“executing a query”).object(ms.getId());

if(this.closed) {

throw new ExecutorException(“Executor was closed.”);

} else {

if(this.queryStack == 0 && ms.isFlushCacheRequired()) {

this.clearLocalCache();

}

List list;

try {

++this.queryStack;

list = resultHandler == null?(List)this.localCache.getObject(key):null;

if(list != null) {

this.handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);

} else {

//如果緩存中沒有就從數據庫中查詢

list = this.queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);

}

} finally {

--this.queryStack;

}

if(this.queryStack == 0) {

Iterator i$ = this.deferredLoads.iterator();

while(i$.hasNext()) {

BaseExecutor.DeferredLoad deferredLoad = (BaseExecutor.DeferredLoad)i$.next();

deferredLoad.load();

}

this.deferredLoads.clear();

if(this.configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {

this.clearLocalCache();

}

}

return list;

}

}

//從數據庫中查詢

private

//放入緩存

this.localCache.putObject(key, ExecutionPlaceholder.EXECUTION_PLACEHOLDER);

List list;

try {

//此處是調用子Executor的方法,ExecutorType默認是使用的SimpleExecutor

list = this.doQuery(ms, parameter, rowBounds, resultHandler, boundSql);

} finally {

this.localCache.removeObject(key);

}

this.localCache.putObject(key, list);

if(ms.getStatementType() == StatementType.CALLABLE) {

this.localOutputParameterCache.putObject(key, parameter);

}

return list;

}

SimpleExecutor的doQuery方法

public

Statement stmt = null;

List var9;

try {

Configuration configuration = ms.getConfiguration();

//創建StateMentHandler處理器

StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);

//調用下3的方法

stmt = this.prepareStatement(handler, ms.getStatementLog());

//調用no4的方法

var9 = handler.query(stmt, resultHandler);

} finally {

this.closeStatement(stmt);

}

return var9;

}

//下3方法

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {

Connection connection = this.getConnection(statementLog);

Statement stmt = handler.prepare(connection);

//SatementHanlder 采用PreparedStatementHandler來實現此方法,而PreparedStatementHandler調用的是父接口ParameterHandler的方法

handler.parameterize(stmt);

return stmt;

}

ParameterHandler參數處理器的方法

public interface ParameterHandler {

Object getParameterObject();

//此方法是用DefaultParameterHandler實現的

void setParameters(PreparedStatement var1) throws SQLException;

}

DefaultParameterHandler默認參數處理器的方法

public void setParameters(PreparedStatement ps) {

ErrorContext.instance().activity(“setting parameters”).object(this.mappedStatement.getParameterMap().getId());

List

if(parameterMappings != null) {

for(int i = 0; i < parameterMappings.size(); ++i) {

ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);

if(parameterMapping.getMode() != ParameterMode.OUT) {

String propertyName = parameterMapping.getProperty();

Object value;

if(this.boundSql.hasAdditionalParameter(propertyName)) {

value = this.boundSql.getAdditionalParameter(propertyName);

} else if(this.parameterObject == null) {

value = null;

} else if(this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {

value = this.parameterObject;

} else {

MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);

value = metaObject.getValue(propertyName);

}

//這里用調用 TypeHandler類型映射處理器來映射

TypeHandler typeHandler = parameterMapping.getTypeHandler();

JdbcType jdbcType = parameterMapping.getJdbcType();

if(value == null && jdbcType == null) {

jdbcType = this.configuration.getJdbcTypeForNull();

}

try {

//類型處理器設置參數映射

typeHandler.setParameter(ps, i + 1, value, jdbcType);

} catch (TypeException var10) {

throw new TypeException(“Could not set parameters for mapping: ” + parameterMapping + “。 Cause: ” + var10, var10);

} catch (SQLException var11) {

throw new TypeException(“Could not set parameters for mapping: ” + parameterMapping + “。 Cause: ” + var11, var11);

}

}

}

}

}

no4的方法

public

//此處調用原生sql的處理器

PreparedStatement ps = (PreparedStatement)statement;

//發出原生sql命令

ps.execute();

//采用ResultHandler結果處理器對結果集封裝

return this.resultSetHandler.handleResultSets(ps);

}12345678

ResultHandler代碼

public interface ResultSetHandler {

//此處調用的是DefaultResultSetHandler的方法

void handleOutputParameters(CallableStatement var1) throws SQLException;

}

1234567

DefaultResultSetHandler的方法

public List

ErrorContext.instance().activity(“handling results”).object(this.mappedStatement.getId());

List

int resultSetCount = 0;

ResultSetWrapper rsw = this.getFirstResultSet(stmt);

List

int resultMapCount = resultMaps.size();

this.validateResultMapsCount(rsw, resultMapCount);

while(rsw != null && resultMapCount 》 resultSetCount) {

ResultMap resultMap = (ResultMap)resultMaps.get(resultSetCount);

this.handleResultSet(rsw, resultMap, multipleResults, (ResultMapping)null);

rsw = this.getNextResultSet(stmt);

this.cleanUpAfterHandlingResultSet();

++resultSetCount;

}

String[] resultSets = this.mappedStatement.getResulSets();

if(resultSets != null) {

while(rsw != null && resultSetCount < resultSets.length) {

ResultMapping parentMapping = (ResultMapping)this.nextResultMaps.get(resultSets[resultSetCount]);

if(parentMapping != null) {

String nestedResultMapId = parentMapping.getNestedResultMapId();

ResultMap resultMap = this.configuration.getResultMap(nestedResultMapId);

this.handleResultSet(rsw, resultMap, (List)null, parentMapping);

}

rsw = this.getNextResultSet(stmt);

this.cleanUpAfterHandlingResultSet();

++resultSetCount;

}

}

return this.collapseSingleResultList(multipleResults);

}

//處理結果集

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List

try {

if(parentMapping != null) {

this.handleRowValues(rsw, resultMap, (ResultHandler)null, RowBounds.DEFAULT, parentMapping);

} else if(this.resultHandler == null) {

DefaultResultHandler defaultResultHandler = new DefaultResultHandler(this.objectFactory);

this.handleRowValues(rsw, resultMap, defaultResultHandler, this.rowBounds, (ResultMapping)null);

multipleResults.add(defaultResultHandler.getResultList());

} else {

this.handleRowValues(rsw, resultMap, this.resultHandler, this.rowBounds, (ResultMapping)null);

}

} finally {

this.closeResultSet(rsw.getResultSet());

}

}

private void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?》 resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {

if(resultMap.hasNestedResultMaps()) {

this.ensureNoRowBounds();

this.checkResultHandler();

this.handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

} else {

this.handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);

}

}

private void handleRowValuesForNestedResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?》 resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {

DefaultResultContext

this.skipRows(rsw.getResultSet(), rowBounds);

Object rowValue = null;

while(this.shouldProcessMoreRows(resultContext, rowBounds) && rsw.getResultSet().next()) {

ResultMap discriminatedResultMap = this.resolveDiscriminatedResultMap(rsw.getResultSet(), resultMap, (String)null);

CacheKey rowKey = this.createRowKey(discriminatedResultMap, rsw, (String)null);

Object partialObject = this.nestedResultObjects.get(rowKey);

if(this.mappedStatement.isResultOrdered()) {

if(partialObject == null && rowValue != null) {

this.nestedResultObjects.clear();

this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());

}

//獲取行的值

rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject);

} else {

rowValue = this.getRowValue(rsw, discriminatedResultMap, rowKey, (String)null, partialObject);

if(partialObject == null) {

this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());

}

}

}

if(rowValue != null && this.mappedStatement.isResultOrdered() && this.shouldProcessMoreRows(resultContext, rowBounds)) {

this.storeObject(resultHandler, resultContext, rowValue, parentMapping, rsw.getResultSet());

}

}

String resultMapId = resultMap.getId();

Object resultObject = partialObject;

if(partialObject != null) {

MetaObject metaObject = this.configuration.newMetaObject(partialObject);

this.putAncestor(partialObject, resultMapId, columnPrefix);

this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, false);

this.ancestorObjects.remove(resultMapId);

} else {

ResultLoaderMap lazyLoader = new ResultLoaderMap();

resultObject = this.createResultObject(rsw, resultMap, lazyLoader, columnPrefix);

if(resultObject != null && !this.typeHandlerRegistry.hasTypeHandler(resultMap.getType())) {

MetaObject metaObject = this.configuration.newMetaObject(resultObject);

boolean foundValues = !resultMap.getConstructorResultMappings().isEmpty();

if(this.shouldApplyAutomaticMappings(resultMap, true)) {

foundValues = this.applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;

}

foundValues = this.applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;

this.putAncestor(resultObject, resultMapId, columnPrefix);

foundValues = this.applyNestedResultMappings(rsw, resultMap, metaObject, columnPrefix, combinedKey, true) || foundValues;

this.ancestorObjects.remove(resultMapId);

foundValues = lazyLoader.size() 》 0 || foundValues;

resultObject = foundValues?resultObject:null;

}

if(combinedKey != CacheKey.NULL_CACHE_KEY) {

this.nestedResultObjects.put(combinedKey, resultObject);

}

}

return resultObject;

}

private boolean shouldApplyAutomaticMappings(ResultMap resultMap, boolean isNested) {

return resultMap.getAutoMapping() != null?resultMap.getAutoMapping().booleanValue():(isNested?AutoMappingBehavior.FULL == this.configuration.getAutoMappingBehavior():AutoMappingBehavior.NONE != this.configuration.getAutoMappingBehavior());

}

private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {

List

boolean foundValues = false;

if(autoMapping.size() 》 0) {

Iterator i$ = autoMapping.iterator();

while(true) {

//這里使用了內部類對參數和結果集進行映射

DefaultResultSetHandler.UnMappedColumAutoMapping mapping;

Object value;

do {

if(!i$.hasNext()) {

return foundValues;

}

mapping = (DefaultResultSetHandler.UnMappedColumAutoMapping)i$.next();

value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);

} while(value == null && !this.configuration.isCallSettersOnNulls());

if(value != null || !mapping.primitive) {

metaObject.setValue(mapping.property, value);

}

foundValues = true;

}

} else {

return foundValues;

}

}

private static class UnMappedColumAutoMapping {

private final String column;

private final String property;

private final TypeHandler<?》 typeHandler;

private final boolean primitive;

//此處才類型器對結果進行映射

public UnMappedColumAutoMapping(String column, String property, TypeHandler<?》 typeHandler, boolean primitive) {

this.column = column;

this.property = property;

this.typeHandler = typeHandler;

this.primitive = primitive;

}

}

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • mybatis
    +關注

    關注

    0

    文章

    60

    瀏覽量

    6713
收藏 人收藏

    評論

    相關推薦

    一文了解MyBatis的查詢原理

    本文通過MyBatis一個低版本的bug(3.4.5之前的版本)入手,分析MyBatis的一次完整的查詢流程,從配置文件的解析到一個查詢的完整執行過程詳細解讀MyBatis的一次查詢流程,通過本文
    的頭像 發表于 10-10 11:42 ?1430次閱讀

    Mybatis自動生成增刪改查代碼

    使用 mybatis generator 自動生成代碼,實現數據庫的增刪改查。 1 配置Mybatis插件 在pom文件添加依賴: pluginsplugin
    的頭像 發表于 01-13 15:43 ?1101次閱讀
    <b class='flag-5'>Mybatis</b>自動生成增刪改查代碼

    Mybatis的內部設計介紹

    Mybatis源碼分析-整體設計(一)
    發表于 06-06 09:43

    基于SpringBoot mybatis方式的增刪改查實現

    SpringBoot mybatis方式實現增刪改查
    發表于 06-18 16:56

    MyBatis的整合

    SpringBoot-15-之整合MyBatis-注解篇+分頁
    發表于 10-28 08:09

    Mybatis是什么

    Mybatis第一講
    發表于 06-04 15:33

    SSM框架在Web應用開發中的設計與實現 pdf下載

    表現層的功能,比如響應請求。Spring 框架主要起到容器的功能,整合了 SpringMVC 和 Mybatis實現層 與層之間的
    發表于 01-29 09:47 ?2次下載

    mybatis快速入門

    本文詳細介紹了mybatis相關知識,以及mybatis快速入門步驟詳解。
    的頭像 發表于 02-24 09:41 ?3534次閱讀
    <b class='flag-5'>mybatis</b>快速入門

    easy-mybatis Mybatis的增強框架

    ./oschina_soft/gitee-easy-mybatis.zip
    發表于 06-14 09:45 ?1次下載
    easy-<b class='flag-5'>mybatis</b> <b class='flag-5'>Mybatis</b>的增強框架

    Fluent Mybatis、原生MybatisMybatis Plus對比

    使用fluent mybatis可以不用寫具體的xml文件,通過java api可以構造出比較復雜的業務sql語句,做到代碼邏輯和sql邏輯的合一。不再需要在Dao中組裝查詢或更新操作,在xml或
    的頭像 發表于 09-15 15:41 ?1433次閱讀

    MyBatis-Plus為什么不支持聯表

    `的所有功能`MyBatis Plus Join`同樣擁有;框架的使用方式和`MyBatis Plus`一樣簡單,幾行代碼就能實現聯表查詢的功能
    的頭像 發表于 02-28 15:19 ?2469次閱讀
    <b class='flag-5'>MyBatis</b>-Plus為什么不支持聯表

    MyBatis、JDBC等做大數據量數據插入的案例和結果

    30萬條數據插入插入數據庫驗證 實體類、mapper和配置文件定義 不分批次直接梭哈 循環逐條插入 MyBatis實現插入30萬條數據 JDBC實現插入30萬條數據 總結 ? 本文主要講述通過
    的頭像 發表于 05-22 11:23 ?1082次閱讀
    <b class='flag-5'>MyBatis</b>、JDBC等做大數據量數據插入的案例和結果

    SpringBoot+Mybatis如何實現流式查詢?

    使用mybatis作為持久層的框架時,通過mybatis執行查詢數據的請求執行成功后,mybatis返回的結果集不是一個集合或對象,而是一個迭代器,可以通過遍歷迭代器來取出結果集
    的頭像 發表于 06-12 09:57 ?1272次閱讀

    mybatis的dao能重載嗎

    不同需求的數據操作。 重載是指在同一個類中定義了多個方法,它們具有相同的名稱但具有不同的參數。重載允許使用相同的方法名來處理不同類型和數量的參數,以提供更加靈活的操作。 在MyBatis的DAO中,我們可以通過重載方法來實現不同類型和數量的參數。例如,可以定義一個根
    的頭像 發表于 12-03 11:51 ?1292次閱讀

    mybatis接口動態代理原理

    MyBatis是一款輕量級的Java持久化框架,它通過XML或注解配置的方式,將數據庫操作與SQL語句解耦,提供了一種簡單、靈活的數據訪問方式。在MyBatis中,使用動態代理技術來實現接口的代理
    的頭像 發表于 12-03 11:52 ?945次閱讀
    主站蜘蛛池模板: 五月婷婷丁香久久| 日本免费观看网站| 免费看 s色| 午夜毛片免费看| 男人和女人做爽爽视频在线观看| 国产国产人免费人成免费视频| 天天操天天摸天天干| 中文字幕123| 日日噜夜夜噜| 最新久久精品| xxxx日本69| 色视频免费观看| 亚洲精品系列| 27pao强力打造高清免费高| 天堂网www中文在线资源| 成人在线免费电影| 88av免费观看| 性生交大片免费一级| 国产成人一区二区在线不卡| 国产精品久久久久久久牛牛| 亚洲一区二区三区麻豆| 国产一区二区在线观看免费| 国产亚洲精品aaa大片| 亚洲最大成人在线| 国产美女精品三级在线观看| 97capcom超频在线| 色视频www在线播放国产人成| 最近最新中文字幕6页| 又粗又长又大真舒服好爽漫画| 日韩艹| 欧洲国产精品精华液| 一级特黄色片| 成年美女| 亚洲www在线| 久久青青成人亚洲精品| 操美女免费视频| 色婷婷色丁香| 日本黄色大全| 午夜爱爱免费视频| 午夜视频在线观看一区二区| 乱高h亲女|