摘要:一簡介是支持定制化存儲過程以及高級映射的優秀的持久層框架。避免了幾乎所有的代碼和手動設置參數以及獲取結果集。可以對配置和原生使用簡單的或注解,將接口和的普通的對象映射成數據庫中的記錄。三其他類型的參數我懶,不想寫了我的心愿是世界和平
(一)MyBatis簡介
(二)源碼分析MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數據庫中的記錄。
項目是用映射器(dao)和映射文件(xxx.xml)的方式配置mybatis
先以下面的更新方法為例
dao接口的方法如下: int updateSubjectById(Subject subject)throws Exception;
代碼執行dao方法時,會調用MapperProxy類中的invoke()方法,往下執行會依次會調用
MapperMethod中的public Object execute(SqlSession sqlSession, Object[] args),
public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else if (SqlCommandType.FLUSH == command.getType()) { result = sqlSession.flushStatements(); } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method "" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
MethodSignature中的public Object convertArgsToSqlCommandParam(Object[] args),
public Object convertArgsToSqlCommandParam(Object[] args) { final int paramCount = params.size(); if (args == null || paramCount == 0) { return null; } else if (!hasNamedParameters && paramCount == 1) { return args[params.keySet().iterator().next().intValue()]; } else { final Mapparam = new ParamMap
因為dao中方法的參數為Subject subject,所以這里args參數為
hasNamedParameters意思是是否使用了注解參數,這里沒有用到注解,值為false。
這里params.size()的值為1,如圖
所以上面的方法返回的是subject,excute()中的param就是subject。
convertArgsToSqlCommandParam()方法執行完后,依次進入rowCountResult(sqlSession.update(command.getName(), param)),SqlSessionTemplate中update()方法,
public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); }
這時SqlSessionInterceptor攔截器(SqlSessiontemplate中的私有類)會攔截到。
private class SqlSessionInterceptor implements InvocationHandler { ... Object result=method.invoke(sqlSession, args); ... }
其中arg[0]的值為com.services.forum.dao.SubjectDAO.updateSubjectById()方法,args[1]的值為SqlSessionTemplate中public int update(String statement, Object parameter)中的parameter,即dao層方法中傳的subject對象。
this.sqlSessionProxy的類型為DefaultSqlSession,其中update(...)方法return executor.update(ms, wrapCollection(parameter)); ,wrapCollection(parameter)對參數進行包裝,該方法的實現如下:
private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap
此方法首先定義一個StrictMap,
如果參數類型是Collection的子類,則在map中加入一條entry:("collection", object),返回map
如果參數類型是List的子類,則在map中加入一條entry:("list", object),返回map
如果參數是數組,則在map中加入一條entry:("array", object),返回map
否則返回參數object
隨后會執行以下方法: SimpleExecutor public int doUpdate(MappedStatement ms, Object parameter) throws SQLException StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
Configuration StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
RoutingStatementHandler public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandl er resultHandler, BoundSql boundSql){ ... delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql); ... }
PreparedStatementHandler public PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBound s, ResultHandler resultHandler, BoundSql boundSql) { super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql); }
BaseStatementHandler protected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds row Bounds, ResultHandler resultHandler, BoundSql boundSql) { ... boundSql = mappedStatement.getBoundSql(parameterObject); ... }
MappedStatement public BoundSql getBoundSql(Object parameterObject) { BoundSql boundSql = sqlSource.getBoundSql(parameterObject); ... }
再跟幾步會進入DynamicSqlSource類中的getBoundSql()方法
public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); for (Map.Entryentry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql; }
這個方法是生成sql語句的方法
sqlSourceParser中是設置和一些注冊的類型[處理機的]別名,如下圖
再跟到SqlSourceBuilder中的parse()方法,
public SqlSource parse(String originalSql, Class> parameterType, MapadditionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalPara meters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); String sql = parser.parse(originalSql); return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
該方法中有個 originalSql參數,這是sql映射文件中的原始sql語句,
update t_forum_subject SET title=#{title}, brief=#{brief}, is_top=#{isTop}, is_open=#{isOpen}, last_modify_time=#{lastModifyTime}, last_modify_userid=#{lastModifyUserId} where id=#{id} and enable=1
GenericTockenParser類中的public String parse(String text)方法將originalSql中的#{}轉為了?,并且在handler的屬性parameterMappings中添加相應的映射
public String parse(String text) { ... builder.append(handler.handleToken(content)); ... }
SqlSourceBuilder @Override public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; }
再回到DynamicSqlSource類中getBoundSql方法中,
現在sqlSource中sql屬性值變為
update t_forum_subject SET title=?, brief=?, is_top=?, is_open=?, last_modify_time=?, last_modify_userid=? where id=? and enable=1
parmeterMappings屬性的值如上圖所示
boundSql的值如上圖
SimpleExecutor private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException { Statement stmt; Connection connection = getConnection(statementLog); stmt = handler.prepare(connection); handler.parameterize(stmt); return stmt; }
最后進入DefaultParameterhandler類中的
public void setParameters(PreparedStatement ps) { ErrorContext.instance().activity("setting parameters").object(mappedStatement.getParameterMap().getId()); ListparameterMappings = 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(); 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 metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName); } 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); } } } } }
在這個方法中遍歷parameterMappings,將parameterMapping中property的值賦給propertyName,再通過
MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName);
將subject對象中的值賦給value,然后typeHandler.setParameter(ps, i + 1, value, jdbcType);將value值加入到ps的ColumnMap, ColumnNames, ColumnValues,再執行SimpleExecutor的doUpdate()中的return handler.update(stmt);
然后,就沒有然后了
我懶,不想寫了
---------------------------------------EOF--------------------------------------
我的心愿是世界和平!~( ゜▽゜)つロ
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69820.html
摘要:我認為學習框架源碼分為兩步抓住主線,掌握框架的原理和流程理解了處理思路之后,再去理解面向對象思想和設計模式的用法目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油 這篇文章我們來深入閱讀下Mybatis的源碼,希望以后可以對底層框架不那么畏懼,學習框架設計中好的思想; 架構原理 架構圖 showImg(https://segmentfault.com/img/remote/...
摘要:在動態解析階段,和會有不同的表現解析為一個預編譯語句的參數標記符。其次,在預編譯之前已經被變量替換了,這會存在注入問題。預編譯語句對象可以重復利用。默認情況下,將對所有的進行預編譯。總結本文主要深入探究了對和的不同處理方式,并了解了預編譯。 mybatis 中使用 sqlMap 進行 sql 查詢時,經常需要動態傳遞參數,例如我們需要根據用戶的姓名來篩選用戶時,sql 如下: sele...
摘要:簡介我從七月份開始閱讀源碼,并在隨后的天內陸續更新了篇文章。考慮到超長文章對讀者不太友好,以及拆分文章工作量也不小等問題。經過兩周緊張的排版,一本小小的源碼分析書誕生了。我在寫系列文章中,買了一本書作為參考,這本書是技術內幕。 1.簡介 我從七月份開始閱讀MyBatis源碼,并在隨后的40天內陸續更新了7篇文章。起初,我只是打算通過博客的形式進行分享。但在寫作的過程中,發現要分析的代碼...
摘要:創建出的是對象,持有這個對象。根據接口名和方法名從對象的中檢查并獲取方法對應的語句解析成的對象,保存它的和命令類型。實現類攔截映射接口的自定義方法,讓去處理方法對應的解析成的。 前言 Mybatis是目前主流的Java ORM框架之一。mybatis-spring包則是為了讓Mybatis更好得整合進Spring的衍生產品。本文就從Mybatis和mybatis-spring源碼著手,...
摘要:的解析和運行原理構建過程提供創建的核心接口。在構造器初始化時會根據和的方法解析為命令。數據庫會話器定義了一個對象的適配器,它是一個接口對象,構造器根據配置來適配對應的對象。它的作用是給實現類對象的使用提供一個統一簡易的使用適配器。 MyBatis的解析和運行原理 構建SqlSessionFactory過程 SqlSessionFactory提供創建MyBatis的核心接口SqlSess...
摘要:原因就是傳入的和原有的單引號,正好組成了,而后面恒等于,所以等于對這個庫執行了查所有的操作。類比的執行流程和原有的我們使用的方法就是。可以理解為就是用來解析定制的符號的語句。后續的流程,就和正常的流程一致了。 前言 在JDBC中,主要使用的是兩種語句,一種是支持參數化和預編譯的PrepareStatement,能夠支持原生的Sql,也支持設置占位符的方式,參數化輸入的參數,防止Sql注...
閱讀 472·2023-04-25 17:26
閱讀 1495·2021-08-05 09:58
閱讀 1959·2019-08-30 13:17
閱讀 944·2019-08-28 17:52
閱讀 1061·2019-08-26 18:27
閱讀 1413·2019-08-26 14:05
閱讀 3608·2019-08-26 14:05
閱讀 1586·2019-08-26 10:45