(一)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
随后会执行以下方法: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.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@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()); 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--------------------------------------
我的心愿是世界和平!~( ゜▽゜)つロ