最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

mybatis查詢語句揭秘之封裝數(shù)據(jù)

 更新時間:2019年04月07日 09:49:43   作者:不懂是非  
這篇文章主要給大家介紹了關(guān)于mybatis查詢語句揭秘之封裝數(shù)據(jù)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用mybatis具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧

一、前言

繼上一篇mybatis查詢語句的背后,這一篇主要圍繞著mybatis查詢的后期操作,即跟數(shù)據(jù)庫交互的時候。由于本人也是一邊學習源碼一邊記錄,內(nèi)容難免有錯誤或不足之處,還望諸位指正,本文只可當參考作用。謹記!

二、分析

繼上一篇博文的查詢例子,mybatis在最后的查詢最終會走SimpleExecutor類的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為routingStatementHandler
 StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
 stmt = prepareStatement(handler, ms.getStatementLog());
 // 雖然是執(zhí)行的routingStatementHandler.query,但返回結(jié)果的還是PreparedStatementHandler處理
 return handler.query(stmt, resultHandler);
 } finally {
 closeStatement(stmt);
 }
 }

private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
 Statement stmt;
 // 使用了代理模式,也可以理解為對connection進行了一層包裝,這里的作用就是加了log處理
 Connection connection = getConnection(statementLog);
 //進行預編譯,即類似jdbc的 sql,如 select * from user where id=?
 stmt = handler.prepare(connection, transaction.getTimeout());
 // 對執(zhí)行查詢的sql進行參數(shù)設置
 handler.parameterize(stmt);
 return stmt;
 }

關(guān)于 handler.prepare的作用這里簡單介紹下,不做代碼分析。

會設置fetchSize,作用就是一次性從數(shù)據(jù)庫抓取數(shù)據(jù),好像默認值是10條,如果每次只抓取一條,則進行rs.next的時候,會再次查庫。

如果是insert操作,并且數(shù)據(jù)庫主鍵自增且還設置了可以返回主鍵,則會還做獲取主鍵的操作。

先從設置參數(shù)說起,也就是handler.parameterize。先看下源碼,具體位置在DefaultParameterHandler類里面

@Override
 public void setParameters(PreparedStatement ps) {
 ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId());
 // 獲取配置文件里面的sql參數(shù)信息,如sql為select * from user where id=#{userId,jdbcType=INTEGER}
 // ParameterMapping 記錄了參數(shù)名也就是userId,還有記錄了對應的jdbc類型,還有對應的javaType等等,具體可以debug看下
 List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
 if (parameterMappings != null) {
 for (int i = 0; i < parameterMappings.size(); i++) {
 ParameterMapping parameterMapping = parameterMappings.get(i);
 if (parameterMapping.getMode() != ParameterMode.OUT) {
  Object value;
  String propertyName = parameterMapping.getProperty();
  // 如果為true,那么sql參數(shù)中有類似 user.name 格式
  if (boundSql.hasAdditionalParameter(propertyName)) { // issue #448 ask first for additional params
  value = boundSql.getAdditionalParameter(propertyName);
  } else if (parameterObject == null) {
  value = null;
  } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
  value = parameterObject;
  } else {
  // metaObject 類似一個工具類,它里面有一個反射工廠,可以專門解析一個類的信息,如字段的setter/getter/屬性信息,這里不做多余介紹
  // 1、下面詳細介紹 
  MetaObject metaObject = configuration.newMetaObject(parameterObject);
  value = metaObject.getValue(propertyName);// 取值
  }
  // 獲取對應的typeHandler,一般情況不設置的話,基本都是ObjectTypeHandler
  TypeHandler typeHandler = parameterMapping.getTypeHandler();
  JdbcType jdbcType = parameterMapping.getJdbcType();
  if (value == null && jdbcType == null) {
  jdbcType = configuration.getJdbcTypeForNull();
  }
  try {
  // 進行設值
  typeHandler.setParameter(ps, i + 1, value, jdbcType);
  } catch (TypeException e) {
  throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
  } catch (SQLException e) {
  throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + e, e);
  }
 }
 }
 }
 }

對于上述代碼中的一部分這里負責將parameterObject的里面的值整出來(也就是傳入的參數(shù)),如果參數(shù)是map結(jié)構(gòu),就從map里面取值,如果不是,如單個非javabean參數(shù),則直接取值,如果是單個javabean,則通過metaObject類轉(zhuǎn)換成一個BeanWrapper,進行取值

這段代碼也就負責對預編譯后的sql設置參數(shù),這里邏輯主要是圍繞以下步驟進行得,

獲取參數(shù)名,獲取參數(shù)值,獲取參數(shù)類型,然后做進行設值操作

/**
 * mybatis數(shù)據(jù)處理有單結(jié)果集和多結(jié)果集處理,一般多結(jié)果集出現(xiàn)存儲過程中,如果存儲過程中寫了兩條select語句,如
 * select * from user , select * from classes 這種情況這里不做介紹,因為本人用的不多,理解的也不是很透徹。
 * 這里不多做介紹,這里只針對簡單映射做一個大概介紹
 *
 */
public List<Object> handleResultSets(Statement stmt) throws SQLException {
 ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
 // 保存查詢結(jié)果
 final List<Object> multipleResults = new ArrayList<>();

 int resultSetCount = 0;
 // 獲取第一條數(shù)據(jù)
 ResultSetWrapper rsw = getFirstResultSet(stmt);
 // 如果不是多結(jié)果集映射,一般resultMaps的大小為1
 // resultMap中存儲的有類的字段屬性,數(shù)據(jù)庫字段名稱等信息
 List<ResultMap> resultMaps = mappedStatement.getResultMaps();
 int resultMapCount = resultMaps.size();
 // 校驗數(shù)據(jù)的正確性
 validateResultMapsCount(rsw, resultMapCount);
 while (rsw != null && resultMapCount > resultSetCount) {
 ResultMap resultMap = resultMaps.get(resultSetCount);
 // 處理結(jié)果集映射
 handleResultSet(rsw, resultMap, multipleResults, null);
 rsw = getNextResultSet(stmt);
 cleanUpAfterHandlingResultSet();
 resultSetCount++;
 }
 // 處理slect 標簽的resultSets屬性,多個用逗號隔開,個人幾乎沒用過,略過
 String[] resultSets = mappedStatement.getResultSets();
 if (resultSets != null) {
 while (rsw != null && resultSetCount < resultSets.length) {
 ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
 if (parentMapping != null) {
  String nestedResultMapId = parentMapping.getNestedResultMapId();
  ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
  handleResultSet(rsw, resultMap, null, parentMapping);
 }
 rsw = getNextResultSet(stmt);
 cleanUpAfterHandlingResultSet();
 resultSetCount++;
 }
 }

 return collapseSingleResultList(multipleResults);
 }

以上代碼就是為結(jié)果映射做一個鋪墊,重點是在hanleResultSet方法里,

private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
 try {// 針對簡單映射,parentMapping是為Null的
 if (parentMapping != null) {
 handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
 } else {
 // 默認使用defaultResultHandler,如需使用自定義的,則可在傳參加入resultHandler接口實現(xiàn)類
 if (resultHandler == null) {
  DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
  // 處理結(jié)果,結(jié)果存在resultHandler里
  handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
  multipleResults.add(defaultResultHandler.getResultList());
 } else {
  handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
 }
 }
 } finally {
 // issue #228 (close resultsets)
 closeResultSet(rsw.getResultSet());
 }
 }
public void handleRowValues(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping) throws SQLException {
 // 處理有嵌套映射的情況
 if (resultMap.hasNestedResultMaps()) {
 ensureNoRowBounds();
 checkResultHandler();
 handleRowValuesForNestedResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
 } else {//沒有嵌套映射
 handleRowValuesForSimpleResultMap(rsw, resultMap, resultHandler, rowBounds, parentMapping);
 }
 }
private void handleRowValuesForSimpleResultMap(ResultSetWrapper rsw, ResultMap resultMap, ResultHandler<?> resultHandler, RowBounds rowBounds, ResultMapping parentMapping)
 throws SQLException {
 DefaultResultContext<Object> resultContext = new DefaultResultContext<>();
 ResultSet resultSet = rsw.getResultSet();
 // 跳過多少行,到達指定記錄位置,如在傳參的時候傳入了rowBounds,則會根據(jù)該類的offset值跳到指定記錄位置
 skipRows(resultSet, rowBounds);
 // shouldProcessMoreRows 用來檢測是否能繼續(xù)對后續(xù)的結(jié)果進行映射
 while (shouldProcessMoreRows(resultContext, rowBounds) && !resultSet.isClosed() && resultSet.next()) {
 //用來處理resultMap節(jié)點中配置了discriminator節(jié)點,這里忽略掉
 ResultMap discriminatedResultMap = resolveDiscriminatedResultMap(resultSet, resultMap, null);
 // 得到的結(jié)果就是sql執(zhí)行后的一行記錄,如返回User對象信息,則rowValue就代表一個user實例,里面已經(jīng)有值了
 Object rowValue = getRowValue(rsw, discriminatedResultMap, null);
 //保存數(shù)據(jù)
 storeObject(resultHandler, resultContext, rowValue, parentMapping, resultSet);
 }
 }
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
 final ResultLoaderMap lazyLoader = new ResultLoaderMap();
 // 創(chuàng)建對象,可以理解為對resultMap節(jié)點的type屬性值,進行了反射處理,得到了一個對象,但屬性值都是默認值。
 Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
 if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
 final MetaObject metaObject = configuration.newMetaObject(rowValue);
 boolean foundValues = this.useConstructorMappings;
 //是否需要自動映射,有三種映射,分別為None,partial,full,默認第二種,處理非嵌套映射,可通過autoMappingBehavior 配置
 if (shouldApplyAutomaticMappings(resultMap, false)) {
 // 映射resultMap中未明確指定的列,如類中含有username屬性,但是resultMap中沒配置,則通過這個進行數(shù)據(jù)映射,還是可以查詢到結(jié)果
 foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
 }
 // 處理resultMap中指定的列
 foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
 foundValues = lazyLoader.size() > 0 || foundValues;
 // 如果沒查詢到結(jié)果,但配置可返回空對象(指的是沒有設置屬性值得對象),則返回空對象,否則返回null
 rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
 }
 return rowValue;
 }

這里只介紹resultMap中有明確指定的列

private boolean applyPropertyMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, ResultLoaderMap lazyLoader, String columnPrefix)
 throws SQLException {
 // 獲取數(shù)據(jù)字段名
 final List<String> mappedColumnNames = rsw.getMappedColumnNames(resultMap, columnPrefix);
 boolean foundValues = false;
 // 獲取的數(shù)據(jù)就是resultMap節(jié)點中配置的result節(jié)點,有多個result節(jié)點,這個集合大小就是多少
 // 里面存儲的是屬性名/字段名等信息
 final List<ResultMapping> propertyMappings = resultMap.getPropertyResultMappings();
 for (ResultMapping propertyMapping : propertyMappings) {
 String column = prependPrefix(propertyMapping.getColumn(), columnPrefix);
 // 是否有嵌套映射
 if (propertyMapping.getNestedResultMapId() != null) {
 // the user added a column attribute to a nested result map, ignore it
 column = null;
 }
 // 針對1來說一般常與嵌套查詢配合使用
 // 2 判斷屬性基本映射
 // 3 多結(jié)果集的一個處理
 if (propertyMapping.isCompositeResult()// 1
  || (column != null && mappedColumnNames.contains(column.toUpperCase(Locale.ENGLISH)))// 2
  || propertyMapping.getResultSet() != null) {// 3
 // 獲取當前column字段對于的值,有用到typeHandler來進行參數(shù)的一個轉(zhuǎn)換
 Object value = getPropertyMappingValue(rsw.getResultSet(), metaObject, propertyMapping, lazyLoader, columnPrefix);
 
 //獲取類的屬性字段名
 final String property = propertyMapping.getProperty();
 if (property == null) {
  continue;
 } else if (value == DEFERRED) {// 類似占位符。處理懶加載數(shù)據(jù)
  foundValues = true;
  continue;
 }
 if (value != null) {
  foundValues = true;
 }
 if (value != null || (configuration.isCallSettersOnNulls() && !metaObject.getSetterType(property).isPrimitive())) {
  // 進行設置屬性值
  metaObject.setValue(property, value);
 }
 }
 }
 return foundValues;
 }

或許有人奇怪為啥沒看到查詢的對象有set操作,值就到了對象里面去了,這里全是metaObject給你操作了,具體的,大家可以自行了解這個類,只能說這個類的功能很強大。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。

相關(guān)文章

  • Java 模擬真正的并發(fā)請求詳情

    Java 模擬真正的并發(fā)請求詳情

    有時需要測試一下某個功能的并發(fā)性能,又不要想借助于其他工具,索性就自己的開發(fā)語言,來一個并發(fā)請求就最方便了。下文我們就來學習Java 如何模擬真正的并發(fā)請求
    2021-09-09
  • Java實現(xiàn)獲取小程序帶參二維碼并保存到本地

    Java實現(xiàn)獲取小程序帶參二維碼并保存到本地

    這篇文章主要介紹了Java實現(xiàn)獲取小程序帶參二維碼并保存到本地,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java?Collections工具類中常用算法解析

    Java?Collections工具類中常用算法解析

    在軟件開發(fā)中,算法是非常重要的一部分,它們可以提供高效的數(shù)據(jù)處理和操作,這篇文章主要為大家介紹了Collections?工具類集合框架中常用算法,感興趣的可以了解一下
    2023-06-06
  • java synchronized用法詳解

    java synchronized用法詳解

    本篇文章主要介紹了java synchronized用法詳解,synchronized是Java中的關(guān)鍵字,是一種同步鎖。有興趣的同學可以了解一下。
    2016-11-11
  • spring項目中切面及AOP的使用方法

    spring項目中切面及AOP的使用方法

    我們知道,spring兩大核心,IOC(控制反轉(zhuǎn))和AOP(切面),那為什么要使用AOP,AOP是什么呢?帶著這些問題通過本文學習下吧
    2021-06-06
  • SpringBoot項目中定時器的實現(xiàn)示例

    SpringBoot項目中定時器的實現(xiàn)示例

    在Spring?Boot項目中,你可以使用Spring框架提供的@Scheduled注解來編寫定時任務,本文就來介紹一下SpringBoot項目中定時器的實現(xiàn),感興趣的可以了解一下
    2023-11-11
  • Java實現(xiàn)的簡易記事本

    Java實現(xiàn)的簡易記事本

    這篇文章主要介紹了Java實現(xiàn)的簡易記事本,較為詳細的分析了基于java實現(xiàn)記事本程序的完整過程,具有一定參考借鑒價值,需要的朋友可以參考下
    2015-04-04
  • Spring Boot使用和配置Druid

    Spring Boot使用和配置Druid

    本篇文章主要介紹了Spring Boot使用和配置Druid,Druid號稱是Java語言中最好的數(shù)據(jù)庫連接池,并且能夠提供強大的監(jiān)控和擴展功能
    2017-04-04
  • Java ThreadLocal的設計理念與作用

    Java ThreadLocal的設計理念與作用

    這篇文章主要介紹了Java ThreadLocal的設計理念與作用,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-03-03
  • 詳解@Autowired(required=false)注入注意的問題

    詳解@Autowired(required=false)注入注意的問題

    這篇文章主要介紹了@Autowired(required=false)注入注意的問題,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04

最新評論

屏东市| 伊通| 五华县| 景宁| 温泉县| 乐平市| 中方县| 青龙| 达孜县| 习水县| 永昌县| 无为县| 津市市| 习水县| 中牟县| 丽江市| 黔东| 岢岚县| 环江| 邵武市| 阿拉善右旗| 湖北省| 绥德县| 平南县| 合肥市| 辉南县| 湘西| 海门市| 康乐县| 左权县| 洛浦县| 泗水县| 神农架林区| 邻水| 上栗县| 绵阳市| 霍州市| 沽源县| 大渡口区| 新余市| 天峻县|