摘要:一簡介是支持定制化存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的持久層框架。避免了幾乎所有的代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集??梢詫?duì)配置和原生使用簡單的或注解,將接口和的普通的對(duì)象映射成數(shù)據(jù)庫中的記錄。三其他類型的參數(shù)我懶,不想寫了我的心愿是世界和平
(一)MyBatis簡介
(二)源碼分析MyBatis 是支持定制化 SQL、存儲(chǔ)過程以及高級(jí)映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動(dòng)設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以對(duì)配置和原生Map使用簡單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫中的記錄。
項(xiàng)目是用映射器(dao)和映射文件(xxx.xml)的方式配置mybatis
先以下面的更新方法為例
dao接口的方法如下: int updateSubjectById(Subject subject)throws Exception;
代碼執(zhí)行dao方法時(shí),會(huì)調(diào)用MapperProxy類中的invoke()方法,往下執(zhí)行會(huì)依次會(huì)調(diào)用
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
因?yàn)閐ao中方法的參數(shù)為Subject subject,所以這里args參數(shù)為
hasNamedParameters意思是是否使用了注解參數(shù),這里沒有用到注解,值為false。
這里params.size()的值為1,如圖
所以上面的方法返回的是subject,excute()中的param就是subject。
convertArgsToSqlCommandParam()方法執(zhí)行完后,依次進(jìn)入rowCountResult(sqlSession.update(command.getName(), param)),SqlSessionTemplate中update()方法,
public int update(String statement, Object parameter) { return this.sqlSessionProxy.update(statement, parameter); }
這時(shí)SqlSessionInterceptor攔截器(SqlSessiontemplate中的私有類)會(huì)攔截到。
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對(duì)象。
this.sqlSessionProxy的類型為DefaultSqlSession,其中update(...)方法return executor.update(ms, wrapCollection(parameter)); ,wrapCollection(parameter)對(duì)參數(shù)進(jìn)行包裝,該方法的實(shí)現(xiàn)如下:
private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap
此方法首先定義一個(gè)StrictMap,
如果參數(shù)類型是Collection的子類,則在map中加入一條entry:("collection", object),返回map
如果參數(shù)類型是List的子類,則在map中加入一條entry:("list", object),返回map
如果參數(shù)是數(shù)組,則在map中加入一條entry:("array", object),返回map
否則返回參數(shù)object
隨后會(huì)執(zhí)行以下方法: 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); ... }
再跟幾步會(huì)進(jìn)入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; }
這個(gè)方法是生成sql語句的方法
sqlSourceParser中是設(shè)置和一些注冊(cè)的類型[處理機(jī)的]別名,如下圖
再跟到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()); }
該方法中有個(gè) originalSql參數(shù),這是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中的#{}轉(zhuǎn)為了?,并且在handler的屬性parameterMappings中添加相應(yīng)的映射
public String parse(String text) { ... builder.append(handler.handleToken(content)); ... }
SqlSourceBuilder @Override public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; }
再回到DynamicSqlSource類中g(shù)etBoundSql方法中,
現(xiàn)在sqlSource中sql屬性值變?yōu)?/p>
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; }
最后進(jìn)入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); } } } } }
在這個(gè)方法中遍歷parameterMappings,將parameterMapping中property的值賦給propertyName,再通過
MetaObject metaObject = configuration.newMetaObject(parameterObject); value = metaObject.getValue(propertyName);
將subject對(duì)象中的值賦給value,然后typeHandler.setParameter(ps, i + 1, value, jdbcType);將value值加入到ps的ColumnMap, ColumnNames, ColumnValues,再執(zhí)行SimpleExecutor的doUpdate()中的return handler.update(stmt);
然后,就沒有然后了
我懶,不想寫了
---------------------------------------EOF--------------------------------------
我的心愿是世界和平!~( ゜▽゜)つロ
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69820.html
摘要:我認(rèn)為學(xué)習(xí)框架源碼分為兩步抓住主線,掌握框架的原理和流程理解了處理思路之后,再去理解面向?qū)ο笏枷牒驮O(shè)計(jì)模式的用法目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油 這篇文章我們來深入閱讀下Mybatis的源碼,希望以后可以對(duì)底層框架不那么畏懼,學(xué)習(xí)框架設(shè)計(jì)中好的思想; 架構(gòu)原理 架構(gòu)圖 showImg(https://segmentfault.com/img/remote/...
摘要:在動(dòng)態(tài)解析階段,和會(huì)有不同的表現(xiàn)解析為一個(gè)預(yù)編譯語句的參數(shù)標(biāo)記符。其次,在預(yù)編譯之前已經(jīng)被變量替換了,這會(huì)存在注入問題。預(yù)編譯語句對(duì)象可以重復(fù)利用。默認(rèn)情況下,將對(duì)所有的進(jìn)行預(yù)編譯。總結(jié)本文主要深入探究了對(duì)和的不同處理方式,并了解了預(yù)編譯。 mybatis 中使用 sqlMap 進(jìn)行 sql 查詢時(shí),經(jīng)常需要?jiǎng)討B(tài)傳遞參數(shù),例如我們需要根據(jù)用戶的姓名來篩選用戶時(shí),sql 如下: sele...
摘要:簡介我從七月份開始閱讀源碼,并在隨后的天內(nèi)陸續(xù)更新了篇文章??紤]到超長文章對(duì)讀者不太友好,以及拆分文章工作量也不小等問題。經(jīng)過兩周緊張的排版,一本小小的源碼分析書誕生了。我在寫系列文章中,買了一本書作為參考,這本書是技術(shù)內(nèi)幕。 1.簡介 我從七月份開始閱讀MyBatis源碼,并在隨后的40天內(nèi)陸續(xù)更新了7篇文章。起初,我只是打算通過博客的形式進(jìn)行分享。但在寫作的過程中,發(fā)現(xiàn)要分析的代碼...
摘要:創(chuàng)建出的是對(duì)象,持有這個(gè)對(duì)象。根據(jù)接口名和方法名從對(duì)象的中檢查并獲取方法對(duì)應(yīng)的語句解析成的對(duì)象,保存它的和命令類型。實(shí)現(xiàn)類攔截映射接口的自定義方法,讓去處理方法對(duì)應(yīng)的解析成的。 前言 Mybatis是目前主流的Java ORM框架之一。mybatis-spring包則是為了讓Mybatis更好得整合進(jìn)Spring的衍生產(chǎn)品。本文就從Mybatis和mybatis-spring源碼著手,...
摘要:的解析和運(yùn)行原理構(gòu)建過程提供創(chuàng)建的核心接口。在構(gòu)造器初始化時(shí)會(huì)根據(jù)和的方法解析為命令。數(shù)據(jù)庫會(huì)話器定義了一個(gè)對(duì)象的適配器,它是一個(gè)接口對(duì)象,構(gòu)造器根據(jù)配置來適配對(duì)應(yīng)的對(duì)象。它的作用是給實(shí)現(xiàn)類對(duì)象的使用提供一個(gè)統(tǒng)一簡易的使用適配器。 MyBatis的解析和運(yùn)行原理 構(gòu)建SqlSessionFactory過程 SqlSessionFactory提供創(chuàng)建MyBatis的核心接口SqlSess...
摘要:原因就是傳入的和原有的單引號(hào),正好組成了,而后面恒等于,所以等于對(duì)這個(gè)庫執(zhí)行了查所有的操作。類比的執(zhí)行流程和原有的我們使用的方法就是。可以理解為就是用來解析定制的符號(hào)的語句。后續(xù)的流程,就和正常的流程一致了。 前言 在JDBC中,主要使用的是兩種語句,一種是支持參數(shù)化和預(yù)編譯的PrepareStatement,能夠支持原生的Sql,也支持設(shè)置占位符的方式,參數(shù)化輸入的參數(shù),防止Sql注...
閱讀 485·2023-04-25 17:26
閱讀 1504·2021-08-05 09:58
閱讀 1970·2019-08-30 13:17
閱讀 953·2019-08-28 17:52
閱讀 1069·2019-08-26 18:27
閱讀 1424·2019-08-26 14:05
閱讀 3622·2019-08-26 14:05
閱讀 1597·2019-08-26 10:45