博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
mybatis参数解析
阅读量:5941 次
发布时间:2019-06-19

本文共 10706 字,大约阅读时间需要 35 分钟。

(一)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 Map
param = new ParamMap
(); int i = 0; for (Map.Entry
entry : params.entrySet()) { param.put(entry.getValue(), args[entry.getKey().intValue()]); // issue #71, add param names as param1, param2...but ensure backward compatibility final String genericParamName = "param" + String.valueOf(i + 1); if (!param.containsKey(genericParamName)) { param.put(genericParamName, args[entry.getKey()]); } i++; } return param; }}

因为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 map = new StrictMap();        map.put("collection", object);        if (object instanceof List) {            map.put("list", object);        }        return map;    } else if (object != null && object.getClass().isArray()) {        StrictMap map = new StrictMap();        map.put("array", object);        return map;    }    return object;}

此方法首先定义一个StrictMap,

  • 如果参数类型是Collection的子类,则在map中加入一条entry:("collection", object),返回map

  • 如果参数类型是List的子类,则在map中加入一条entry:("list", object),返回map

  • 如果参数是数组,则在map中加入一条entry:("array", object),返回map

  • 否则返回参数object

随后会执行以下方法:SimpleExecutorpublic int doUpdate(MappedStatement ms, Object parameter) throws SQLException    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
ConfigurationStatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
RoutingStatementHandlerpublic RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandl    er resultHandler, BoundSql boundSql){    ...    delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);    ...}
PreparedStatementHandlerpublic PreparedStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBound    s, ResultHandler resultHandler, BoundSql boundSql) {      super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);}
BaseStatementHandlerprotected BaseStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds row    Bounds, ResultHandler resultHandler, BoundSql boundSql) {    ...    boundSql = mappedStatement.getBoundSql(parameterObject);    ...}
MappedStatementpublic 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.Entry
entry : context.getBindings().entrySet()) { boundSql.setAdditionalParameter(entry.getKey(), entry.getValue()); } return boundSql;}

这个方法是生成sql语句的方法

sqlSourceParser中是设置和一些注册的类型[处理机的]别名,如下图
图片描述

再跟到SqlSourceBuilder中的parse()方法,

public SqlSource parse(String originalSql, Class
parameterType, Map
additionalParameters) { 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@Overridepublic 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的值如上图

SimpleExecutorprivate 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());  List
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(); 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://wsqtx.baihongyu.com/

你可能感兴趣的文章
oracle sql语句实现累加、累减、累乘、累除
查看>>
SCNetworkReachabilityRef监测网络状态
查看>>
3D地图的定时高亮和点击事件(基于echarts)
查看>>
接口由40秒到200ms优化记录
查看>>
java 视频播放 多人及时弹幕技术 代码生成器 websocket springmvc mybatis SSM
查看>>
Activiti6.0,spring5,SSM,工作流引擎,OA
查看>>
第十三章:SpringCloud Config Client的配置
查看>>
使用 GPUImage 实现一个简单相机
查看>>
CoinWhiteBook:区块链在慈善事业中的应用
查看>>
Mac上基于Github搭建Hexo博客
查看>>
What does corn harvester involve?
查看>>
阿里云服务器ECS开放8080端口
查看>>
前端常用排序详解
查看>>
Spring中实现监听的方法
查看>>
使用Tooltip会出现一个问题,如果行上出现复选框
查看>>
11.03T1 DP
查看>>
P2924 [USACO08DEC]大栅栏Largest Fence
查看>>
jQuery操作table tr td
查看>>
工作总结:MFC自写排序算法(升序)
查看>>
螺旋队列问题之二
查看>>