摘要:一簡介是支持定制化存儲過程以及高級映射的優(yōu)秀的持久層框架。避免了幾乎所有的代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集??梢詫ε渲煤驮褂煤唵蔚幕蜃⒔?,將接口和的普通的對象映射成數(shù)據(jù)庫中的記錄。三其他類型的參數(shù)我懶,不想寫了我的心愿是世界和平
(一)MyBatis簡介
(二)源碼分析MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的持久層框架。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。MyBatis 可以對配置和原生Map使用簡單的 XML 或注解,將接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java對象)映射成數(shù)據(jù)庫中的記錄。
項目是用映射器(dao)和映射文件(xxx.xml)的方式配置mybatis
先以下面的更新方法為例
dao接口的方法如下: int updateSubjectById(Subject subject)throws Exception;
代碼執(zhí)行dao方法時,會調(diào)用MapperProxy類中的invoke()方法,往下執(zhí)行會依次會調(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
因為dao中方法的參數(shù)為Subject subject,所以這里args參數(shù)為
hasNamedParameters意思是是否使用了注解參數(shù),這里沒有用到注解,值為false。
這里params.size()的值為1,如圖
所以上面的方法返回的是subject,excute()中的param就是subject。
convertArgsToSqlCommandParam()方法執(zhí)行完后,依次進入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)對參數(shù)進行包裝,該方法的實現(xiàn)如下:
private Object wrapCollection(final Object object) { if (object instanceof Collection) { StrictMap
此方法首先定義一個StrictMap,
如果參數(shù)類型是Collection的子類,則在map中加入一條entry:("collection", object),返回map
如果參數(shù)類型是List的子類,則在map中加入一條entry:("list", object),返回map
如果參數(shù)是數(shù)組,則在map中加入一條entry:("array", object),返回map
否則返回參數(shù)object
隨后會執(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); ... }
再跟幾步會進入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中是設(shè)置和一些注冊的類型[處理機的]別名,如下圖
再跟到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參數(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; }
最后進入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,再執(zhí)行SimpleExecutor的doUpdate()中的return handler.update(stmt);
然后,就沒有然后了
我懶,不想寫了
---------------------------------------EOF--------------------------------------
我的心愿是世界和平!~( ゜▽゜)つロ
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/17563.html
摘要:我認為學(xué)習(xí)框架源碼分為兩步抓住主線,掌握框架的原理和流程理解了處理思路之后,再去理解面向?qū)ο笏枷牒驮O(shè)計模式的用法目前第一步尚有問題,需要多走幾遍源碼,加深下理解,一起加油 這篇文章我們來深入閱讀下Mybatis的源碼,希望以后可以對底層框架不那么畏懼,學(xué)習(xí)框架設(shè)計中好的思想; 架構(gòu)原理 架構(gòu)圖 showImg(https://segmentfault.com/img/remote/...
摘要:在動態(tài)解析階段,和會有不同的表現(xiàn)解析為一個預(yù)編譯語句的參數(shù)標(biāo)記符。其次,在預(yù)編譯之前已經(jīng)被變量替換了,這會存在注入問題。預(yù)編譯語句對象可以重復(fù)利用。默認情況下,將對所有的進行預(yù)編譯??偨Y(jié)本文主要深入探究了對和的不同處理方式,并了解了預(yù)編譯。 mybatis 中使用 sqlMap 進行 sql 查詢時,經(jīng)常需要動態(tài)傳遞參數(shù),例如我們需要根據(jù)用戶的姓名來篩選用戶時,sql 如下: sele...
摘要:簡介我從七月份開始閱讀源碼,并在隨后的天內(nèi)陸續(xù)更新了篇文章。考慮到超長文章對讀者不太友好,以及拆分文章工作量也不小等問題。經(jīng)過兩周緊張的排版,一本小小的源碼分析書誕生了。我在寫系列文章中,買了一本書作為參考,這本書是技術(shù)內(nèi)幕。 1.簡介 我從七月份開始閱讀MyBatis源碼,并在隨后的40天內(nèi)陸續(xù)更新了7篇文章。起初,我只是打算通過博客的形式進行分享。但在寫作的過程中,發(fā)現(xiàn)要分析的代碼...
摘要:創(chuàng)建出的是對象,持有這個對象。根據(jù)接口名和方法名從對象的中檢查并獲取方法對應(yīng)的語句解析成的對象,保存它的和命令類型。實現(xiàn)類攔截映射接口的自定義方法,讓去處理方法對應(yīng)的解析成的。 前言 Mybatis是目前主流的Java ORM框架之一。mybatis-spring包則是為了讓Mybatis更好得整合進Spring的衍生產(chǎn)品。本文就從Mybatis和mybatis-spring源碼著手,...
摘要:的解析和運行原理構(gòu)建過程提供創(chuàng)建的核心接口。在構(gòu)造器初始化時會根據(jù)和的方法解析為命令。數(shù)據(jù)庫會話器定義了一個對象的適配器,它是一個接口對象,構(gòu)造器根據(jù)配置來適配對應(yīng)的對象。它的作用是給實現(xiàn)類對象的使用提供一個統(tǒng)一簡易的使用適配器。 MyBatis的解析和運行原理 構(gòu)建SqlSessionFactory過程 SqlSessionFactory提供創(chuàng)建MyBatis的核心接口SqlSess...
摘要:原因就是傳入的和原有的單引號,正好組成了,而后面恒等于,所以等于對這個庫執(zhí)行了查所有的操作。類比的執(zhí)行流程和原有的我們使用的方法就是??梢岳斫鉃榫褪怯脕斫馕龆ㄖ频姆柕恼Z句。后續(xù)的流程,就和正常的流程一致了。 前言 在JDBC中,主要使用的是兩種語句,一種是支持參數(shù)化和預(yù)編譯的PrepareStatement,能夠支持原生的Sql,也支持設(shè)置占位符的方式,參數(shù)化輸入的參數(shù),防止Sql注...
閱讀 3840·2021-10-12 10:12
閱讀 1471·2021-10-11 10:58
閱讀 2307·2021-10-09 10:01
閱讀 2617·2021-09-24 09:48
閱讀 2713·2021-09-09 11:38
閱讀 3538·2019-08-30 15:44
閱讀 1733·2019-08-30 14:22
閱讀 530·2019-08-29 12:42