ibatis源码学习4_参数和结果的映射原理

时间:2022-09-26 00:21:22
问题
在详细介绍ibatis参数和结果映射原理之前,让我们先来思考几个问题。
1. 为什么需要参数和结果的映射?
相对于全自动的orm,ibatis一个重要目标是,通过维护POJO与SQL之间的映射关系,让我们执行 SQL时对输入输出的数据管理更加方便。也就是说,ibatis并不会为程序员在运行期自动生成SQL 执行,具体的 SQL 需要程序员编写,然后通过映射配置文件,将SQL所需的参数,以及返回的结果字段映射到指定POJO。

2. 如何维护参数和结果的映射关系?
对于变化的数据,当然是通过配置文件的方式,在sqlMap映射文件中配置参数和结果对象与SQL的映射关系。

3. 如何执行参数和结果与SQL的映射?
初始化时读取配置文件,将参数和结果与SQL的映射关系维护在SqlMapClientImpl中;处理请求时,通过配置的映射关系,构建sql参数和结果对象,可以采用反射等方式读取和写入对象的属性值。

SQL执行环境
        在ibatis整体设计和核心流程一文的SQL执行过程中,SqlMapExecutorDelegate首先获取初始化时构建好的MappedStatement (初始化过程见初始化和配置文件解析),再通过MappedStatement执行SQL,参数和结果与SQL的映射正是在MappedStatement的SQL执行过程中进行处理。在介绍参数和结果与SQL的映射原理之前,我们先看一下MappedStatement的相关类图:
ibatis源码学习4_参数和结果的映射原理
上面的这张类图需说明两点:
1. 从纵向上体现了Statement类的整体继承关系,MappedStatement接口提供了SQL执行上下文信息和执行操作,如ParameterMap、ResultMap、SQL、Timeout等上下文信息和executeQueryForList等操作信息;BaseStatement抽象类提供了MappedStatement的初步实现,它组合了MappedStatement需要的上下文信息;GeneralStatement实现类提供了MappedStatement的执行操作的基本实现;InsertStatement、SelectStatement等实现类提供了针对不同类型SQL操作的特定实现。
2. 从横向上体现了Statement类的初始化数据和请求处理数据的分离。 类图中的第一行对象(ParameterMap/ResultMap/SQL等)在初始化过程中会构造完毕,请求处理时直接获取即可; 类图中的第三行对象(RequestScope)在请求处理时才会创建,通过执行方法的参数传入,再进行后续处理。

映射主要类图
参数和结果映射的主要类图如下:
ibatis源码学习4_参数和结果的映射原理
1. ParameterMap
该接口提供了和参数处理相关的方法,如根据参数对象生成sql参数数组,为PrepareStatement设置参数等。它的默认实现类是BasicParameterMap,其内部组合了多个ParameterMapping对象。

2. ResultMap
该接口提供了和结果处理相关的方法,如根据ResultSet生成结果对象等。它的默认实现类是BasicResultMap,其内部组合了多个ResultMapping对象。

3. ParameterMapping/ResultMapping
该接口用于维护参数对象和结果对象中每个属性的详细映射信息,如propertyName、jdbcType、javaType、nullValue等,以及针对该属性类型的TypeHandler。

4. DataExchange
该接口是映射的核心接口,执行具体的映射操作,默认实现是BaseDataExchange,针对不同的参数和结果类型有不同的实现类,如JavaBeanDataExchange、MapDataExchange、ListDataExchange、PrimitiveDataExchange等,这些对象统一由DataExchangeFactory创建和管理。

下面以查询操作为例,分析ibatis执行过程中参数和结果的映射原理。
参数映射过程
1. SqlMapExecutorDelegate调用GeneralStatement.executeQueryWithCallback()方法,执行sql语句。

  1. protected void executeQueryWithCallback(RequestScope request, Connection conn, Object parameterObject, Object resultObject, RowHandler rowHandler, int skipResults, int maxResults)
  2. throws SQLException {
  3. ...
  4. parameterObject = validateParameter(parameterObject);  //校验输入参数
  5. Sql sql = getSql();   //获取sql对象
  6. errorContext.setMoreInfo("Check the parameter map.");
  7. ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);     //从sql对象中获取ParameterMap
  8. errorContext.setMoreInfo("Check the result map.");
  9. ResultMap resultMap = sql.getResultMap(request, parameterObject); //从sql对象中获取ResultMap
  10. request.setResultMap(resultMap);
  11. request.setParameterMap(parameterMap);
  12. Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject); //重要,目的是通过参数映射获取参数值
  13. errorContext.setMoreInfo("Check the SQL statement.");
  14. String sqlString = sql.getSql(request, parameterObject); //获取sql语句
  15. errorContext.setActivity("executing mapped statement");
  16. errorContext.setMoreInfo("Check the SQL statement or the result map.");
  17. RowHandlerCallback callback = new RowHandlerCallback(resultMap, resultObject, rowHandler);
  18. sqlExecuteQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback);  // 重要,执行sql操作
  19. ...
  1. protected void executeQueryWithCallback(RequestScope request, Connection conn, Object parameterObject, Object resultObject, RowHandler rowHandler, int skipResults, int maxResults)
  2. throws SQLException {
  3. ...
  4. parameterObject = validateParameter(parameterObject);  //校验输入参数
  5. Sql sql = getSql();   //获取sql对象
  6. errorContext.setMoreInfo("Check the parameter map.");
  7. ParameterMap parameterMap = sql.getParameterMap(request, parameterObject);     //从sql对象中获取ParameterMap
  8. errorContext.setMoreInfo("Check the result map.");
  9. ResultMap resultMap = sql.getResultMap(request, parameterObject); //从sql对象中获取ResultMap
  10. request.setResultMap(resultMap);
  11. request.setParameterMap(parameterMap);
  12. Object[] parameters = parameterMap.getParameterObjectValues(request, parameterObject); //重要,目的是通过参数映射获取参数值
  13. errorContext.setMoreInfo("Check the SQL statement.");
  14. String sqlString = sql.getSql(request, parameterObject); //获取sql语句
  15. errorContext.setActivity("executing mapped statement");
  16. errorContext.setMoreInfo("Check the SQL statement or the result map.");
  17. RowHandlerCallback callback = new RowHandlerCallback(resultMap, resultObject, rowHandler);
  18. sqlExecuteQuery(request, conn, sqlString, parameters, skipResults, maxResults, callback);  // 重要,执行sql操作
  19. ...

该方法执行过程中,有非常重要的两步: 根据参数对象生成sql执行参数数组,该步在parameterMap.getParameterObjectValues()中完成;通过参数数组为PrepareStatement设置参数,该步在sqlExecuteQuery()方法中完成。下面重点看一下这两个的方法的实现。

2. BasicParameterMap.getParameterObjectValues()的实现如下:

  1. public Object[] getParameterObjectValues(RequestScope request, Object parameterObject) {
  2. return dataExchange.getData(request, this, parameterObject);
  3. }
  1. public Object[] getParameterObjectValues(RequestScope request, Object parameterObject) {
  2. return dataExchange.getData(request, this, parameterObject);
  3. }

可以看出,参数对象映射主要交给dataExchange对象完成,针对不同的parameterClass,BasicParameterMap初始化时会创建不同的dataExchange对象。这里以MapDataExchange对象为例,说明参数的映射过程。

3. MapDataExchange.getData()的实现如下:

  1. public Object[] getData(RequestScope request, ParameterMap parameterMap, Object parameterObject) {
  2. if (!(parameterObject instanceof Map)) {
  3. throw new RuntimeException("Error.  Object passed into MapDataExchange was not an instance of Map.");
  4. }
  5. Object[] data = new Object[parameterMap.getParameterMappings().length];
  6. Map map = (Map) parameterObject;
  7. ParameterMapping[] mappings = parameterMap.getParameterMappings();
  8. for (int i = 0; i < mappings.length; i++) {
  9. data[i] = map.get(mappings[i].getPropertyName());
  10. }
  11. return data;
  12. }
  1. public Object[] getData(RequestScope request, ParameterMap parameterMap, Object parameterObject) {
  2. if (!(parameterObject instanceof Map)) {
  3. throw new RuntimeException("Error.  Object passed into MapDataExchange was not an instance of Map.");
  4. }
  5. Object[] data = new Object[parameterMap.getParameterMappings().length];
  6. Map map = (Map) parameterObject;
  7. ParameterMapping[] mappings = parameterMap.getParameterMappings();
  8. for (int i = 0; i < mappings.length; i++) {
  9. data[i] = map.get(mappings[i].getPropertyName());
  10. }
  11. return data;
  12. }

可以看出,map参数对象的映射方式是,通过循环处理每一个ParameterMapping对象获得属性名称,再从map参数中获取对应的属性值,最终放入参数数组中。有兴趣的同学可以看一下JavaBeanDataExchange的实现,其内部通过组合AccessPlan对象,使用反射方式生成参数数组。

4. 通过上面两步已经获得了sql执行参数数组,sqlExecuteQuery()方法将通过参数数组为PrepareStatement设置参数。sqlExecuteQuery()方法内部调用SqlExecutor.executeQuery()进行查询,部分源码如下:

  1. public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, intmaxResults, RowHandlerCallback callback) throws SQLException {
  2. ...
  3. PreparedStatement ps = null;
  4. ResultSet rs = null;
  5. setupResultObjectFactory(request);
  6. try {
  7. errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
  8. Integer rsType = request.getStatement().getResultSetType();
  9. //准备PrepareStatement
  10. if (rsType != null) {
  11. ps = prepareStatement(request.getSession(), conn, sql, rsType);
  12. } else {
  13. ps = prepareStatement(request.getSession(), conn, sql);
  14. }
  15. setStatementTimeout(request.getStatement(), ps);
  16. Integer fetchSize = request.getStatement().getFetchSize();
  17. if (fetchSize != null) {
  18. ps.setFetchSize(fetchSize.intValue());
  19. }
  20. errorContext.setMoreInfo("Check the parameters (set parameters failed).");
  21. request.getParameterMap().setParameters(request, ps, parameters); //为PrepareStatement设置参数
  22. errorContext.setMoreInfo("Check the statement (query failed).");
  23. ps.execute(); //执行
  24. errorContext.setMoreInfo("Check the results (failed to retrieve results).");
  25. // Begin ResultSet Handling
  26. rs = handleMultipleResults(ps, request, skipResults, maxResults, callback);  //处理结果
  27. ...
  28. }
  1. public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
  2. ...
  3. PreparedStatement ps = null;
  4. ResultSet rs = null;
  5. setupResultObjectFactory(request);
  6. try {
  7. errorContext.setMoreInfo("Check the SQL Statement (preparation failed).");
  8. Integer rsType = request.getStatement().getResultSetType();
  9. //准备PrepareStatement
  10. if (rsType != null) {
  11. ps = prepareStatement(request.getSession(), conn, sql, rsType);
  12. } else {
  13. ps = prepareStatement(request.getSession(), conn, sql);
  14. }
  15. setStatementTimeout(request.getStatement(), ps);
  16. Integer fetchSize = request.getStatement().getFetchSize();
  17. if (fetchSize != null) {
  18. ps.setFetchSize(fetchSize.intValue());
  19. }
  20. errorContext.setMoreInfo("Check the parameters (set parameters failed).");
  21. request.getParameterMap().setParameters(request, ps, parameters); //为PrepareStatement设置参数
  22. errorContext.setMoreInfo("Check the statement (query failed).");
  23. ps.execute(); //执行
  24. errorContext.setMoreInfo("Check the results (failed to retrieve results).");
  25. // Begin ResultSet Handling
  26. rs = handleMultipleResults(ps, request, skipResults, maxResults, callback);  //处理结果
  27. ...
  28. }

上面方法中通过调用getParameterMap().setParameters(request, ps, parameters)为PrepareStatement设置参数,其中BasicParameterMap().setParameters()源码如下:

  1. public void setParameters(RequestScope request, PreparedStatement ps, Object[] parameters)
  2. throws SQLException {
  3. ....
  4. if (parameterMappings != null) {
  5. for (int i = 0; i < parameterMappings.length; i++) {
  6. BasicParameterMapping mapping = (BasicParameterMapping) parameterMappings[i];
  7. errorContext.setMoreInfo(mapping.getErrorString());
  8. if (mapping.isInputAllowed()) {
  9. setParameter(ps, mapping, parameters, i); //循环处理每一个参数
  10. }
  11. }
  12. }
  13. }
  1. public void setParameters(RequestScope request, PreparedStatement ps, Object[] parameters)
  2. throws SQLException {
  3. ....
  4. if (parameterMappings != null) {
  5. for (int i = 0; i < parameterMappings.length; i++) {
  6. BasicParameterMapping mapping = (BasicParameterMapping) parameterMappings[i];
  7. errorContext.setMoreInfo(mapping.getErrorString());
  8. if (mapping.isInputAllowed()) {
  9. setParameter(ps, mapping, parameters, i); //循环处理每一个参数
  10. }
  11. }
  12. }
  13. }

在前面参数数组生成时我们看到,parameterMappings和参数数组是一一对应关系,并且保证前后顺序,这里再循环处理每一个parameterMappings和相应参数值,为PrepareStatement设置参数。 其中setParameter()方法如下:

  1. protected void setParameter(PreparedStatement ps, BasicParameterMapping mapping, Object[] parameters, int i) throwsSQLException {
  2. Object value = parameters[i];
  3. // 设置空值
  4. String nullValueString = mapping.getNullValue();
  5. if (nullValueString != null) {
  6. TypeHandler handler = mapping.getTypeHandler();
  7. if (handler.equals(value, nullValueString)) {
  8. value = null;
  9. }
  10. }
  11. // 设置Parameter
  12. TypeHandler typeHandler = mapping.getTypeHandler();
  13. if (value != null) {
  14. typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
  15. } else if (typeHandler instanceof CustomTypeHandler) {
  16. typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
  17. } else {
  18. int jdbcType = mapping.getJdbcType();
  19. if (jdbcType != JdbcTypeRegistry.UNKNOWN_TYPE) {
  20. ps.setNull(i + 1, jdbcType);
  21. } else {
  22. ps.setNull(i + 1, Types.OTHER);
  23. }
  24. }
  25. }
  1. protected void setParameter(PreparedStatement ps, BasicParameterMapping mapping, Object[] parameters, int i) throws SQLException {
  2. Object value = parameters[i];
  3. // 设置空值
  4. String nullValueString = mapping.getNullValue();
  5. if (nullValueString != null) {
  6. TypeHandler handler = mapping.getTypeHandler();
  7. if (handler.equals(value, nullValueString)) {
  8. value = null;
  9. }
  10. }
  11. // 设置Parameter
  12. TypeHandler typeHandler = mapping.getTypeHandler();
  13. if (value != null) {
  14. typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
  15. } else if (typeHandler instanceof CustomTypeHandler) {
  16. typeHandler.setParameter(ps, i + 1, value, mapping.getJdbcTypeName());
  17. } else {
  18. int jdbcType = mapping.getJdbcType();
  19. if (jdbcType != JdbcTypeRegistry.UNKNOWN_TYPE) {
  20. ps.setNull(i + 1, jdbcType);
  21. } else {
  22. ps.setNull(i + 1, Types.OTHER);
  23. }
  24. }
  25. }

上面的方法最终调用TypeHandler进行参数设置,下面以BigDecimalTypeHandler为例,看一下setParameter()方法实现:

  1. public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)
  2. throws SQLException {
  3. ps.setBigDecimal(i, ((BigDecimal) parameter));
  4. }
  1. public void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)
  2. throws SQLException {
  3. ps.setBigDecimal(i, ((BigDecimal) parameter));
  4. }

到这里,谜底已经揭开了。

总结一下上面整个过程: 初始化时通过配置文件构建ParameterMap;请求处理时再通过ParameterMap构建出对应的sql参数数组,这个构建过程通过调用dataExchange对象完成;最后通过sql参数数组为PrepareStatement设置参数。

结果映射过程
1. 在参数映射过程的第4步中,最后一行代码是handleMultipleResults(),正是这里进行结果映射,部分源码如下:

  1. private ResultSet handleMultipleResults(PreparedStatement ps, RequestScope request, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
  2. ResultSet rs;
  3. rs = getFirstResultSet(ps);
  4. if (rs != null) {
  5. handleResults(request, rs, skipResults, maxResults, callback);
  6. }
  7. ...
  1. private ResultSet handleMultipleResults(PreparedStatement ps, RequestScope request, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
  2. ResultSet rs;
  3. rs = getFirstResultSet(ps);
  4. if (rs != null) {
  5. handleResults(request, rs, skipResults, maxResults, callback);
  6. }
  7. ...

2. handleResults()方法的实现如下:

  1. private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback)throws SQLException {
  2. try {
  3. request.setResultSet(rs);
  4. ResultMap resultMap = request.getResultMap();
  5. ...
  6. int resultsFetched = 0;
  7. while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
  8. Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);  //获取每条记录的各个属性值
  9. callback.handleResultObject(request, columnValues, rs); //通过属性值组装结果对象
  10. resultsFetched++;
  11. }
  12. }
  13. } finally {
  14. request.setResultSet(null);
  15. }
  16. }
  1. private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException {
  2. try {
  3. request.setResultSet(rs);
  4. ResultMap resultMap = request.getResultMap();
  5. ...
  6. int resultsFetched = 0;
  7. while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) {
  8. Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs);  //获取每条记录的各个属性值
  9. callback.handleResultObject(request, columnValues, rs); //通过属性值组装结果对象
  10. resultsFetched++;
  11. }
  12. }
  13. } finally {
  14. request.setResultSet(null);
  15. }
  16. }

上面方法中依次处理ResultSet的每条记录,首先获取该记录的属性值数组,该步通过BasicResultMap.getResults()方法实现;再将属性值数组装换成结果对象,该步通过RowHandlerCallback.handleResultObject()方法实现;最后存储在RowHandlerCallback对象中,最终在GeneralStatement中通过rowHandler.getList()返回结果。下面分别看一下这两个方法的具体实现。

3. BasicResultMap.getResults()的部分源码如下:

  1. public Object[] getResults(RequestScope request, ResultSet rs)
  2. throws SQLException {
  3. ...
  4. boolean foundData = false;
  5. Object[] columnValues = new Object[getResultMappings().length];
  6. // 依次处理ResultMappings,设置每个属性值
  7. for (int i = 0; i < getResultMappings().length; i++) {
  8. BasicResultMapping mapping = (BasicResultMapping) getResultMappings()[i];
  9. if (mapping.getStatementName() != null) {
  10. if (resultClass == null) {
  11. throw new SqlMapException("The result class was null when trying to get results for ResultMap named " + getId() +".");
  12. } else if (Map.class.isAssignableFrom(resultClass)) {
  13. Class javaType = mapping.getJavaType();
  14. if (javaType == null) {
  15. javaType = Object.class;
  16. }
  17. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, javaType);
  18. } else if (DomTypeMarker.class.isAssignableFrom(resultClass)) {
  19. Class javaType = mapping.getJavaType();
  20. if (javaType == null) {
  21. javaType = DomTypeMarker.class;
  22. }
  23. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, javaType);
  24. } else {
  25. Probe p = ProbeFactory.getProbe(resultClass);
  26. Class type = p.getPropertyTypeForSetter(resultClass, mapping.getPropertyName());
  27. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, type);
  28. }
  29. foundData = foundData || columnValues[i] != null;
  30. } else if (mapping.getNestedResultMapName() == null) {
  31. columnValues[i] = getPrimitiveResultMappingValue(rs, mapping);
  32. if (columnValues[i] == null) {
  33. columnValues[i] = doNullMapping(columnValues[i], mapping);
  34. }
  35. else  {
  36. foundData = true;
  37. }
  38. }
  39. }
  40. request.setRowDataFound(foundData);
  41. return columnValues;
  42. }
  1. public Object[] getResults(RequestScope request, ResultSet rs)
  2. throws SQLException {
  3. ...
  4. boolean foundData = false;
  5. Object[] columnValues = new Object[getResultMappings().length];
  6. // 依次处理ResultMappings,设置每个属性值
  7. for (int i = 0; i < getResultMappings().length; i++) {
  8. BasicResultMapping mapping = (BasicResultMapping) getResultMappings()[i];
  9. if (mapping.getStatementName() != null) {
  10. if (resultClass == null) {
  11. throw new SqlMapException("The result class was null when trying to get results for ResultMap named " + getId() + ".");
  12. } else if (Map.class.isAssignableFrom(resultClass)) {
  13. Class javaType = mapping.getJavaType();
  14. if (javaType == null) {
  15. javaType = Object.class;
  16. }
  17. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, javaType);
  18. } else if (DomTypeMarker.class.isAssignableFrom(resultClass)) {
  19. Class javaType = mapping.getJavaType();
  20. if (javaType == null) {
  21. javaType = DomTypeMarker.class;
  22. }
  23. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, javaType);
  24. } else {
  25. Probe p = ProbeFactory.getProbe(resultClass);
  26. Class type = p.getPropertyTypeForSetter(resultClass, mapping.getPropertyName());
  27. columnValues[i] = getNestedSelectMappingValue(request, rs, mapping, type);
  28. }
  29. foundData = foundData || columnValues[i] != null;
  30. } else if (mapping.getNestedResultMapName() == null) {
  31. columnValues[i] = getPrimitiveResultMappingValue(rs, mapping);
  32. if (columnValues[i] == null) {
  33. columnValues[i] = doNullMapping(columnValues[i], mapping);
  34. }
  35. else  {
  36. foundData = true;
  37. }
  38. }
  39. }
  40. request.setRowDataFound(foundData);
  41. return columnValues;
  42. }

上面的代码依次处理ResultMappings,设置每个属性值,最后统一放入columnValues数组中。

4. RowHandlerCallback.handleResultObject()方法的实现如下:

  1. public void handleResultObject(RequestScope request, Object[] results, ResultSet rs) throws SQLException {
  2. Object object;
  3. request.setCurrentNestedKey(null);
  4. object = resultMap.resolveSubMap(request, rs).setResultObjectValues(request, resultObject, results);  //生成结果对象
  5. ...
  6. rowHandler.handleRow(object); // 将object加入rowHandler内部的list中
  7. }
  8. }
  1. public void handleResultObject(RequestScope request, Object[] results, ResultSet rs) throws SQLException {
  2. Object object;
  3. request.setCurrentNestedKey(null);
  4. object = resultMap.resolveSubMap(request, rs).setResultObjectValues(request, resultObject, results);  //生成结果对象
  5. ...
  6. rowHandler.handleRow(object); // 将object加入rowHandler内部的list中
  7. }
  8. }

该方法内部实现调用BasicResultMap.setResultObjectValues()生成结果对象,再将结果对象保存在RowHandler中。setResultObjectValues()方法目标是将属性值设置到对象中,部分场景是借助上文提及的dataExchange对象实现,这里就不再详述。

小结
最后,以一张图总结参数和结果的映射整体流程:
ibatis源码学习4_参数和结果的映射原理
每个映射过程都主要拆分为两步,为什么需要这样的设计?一个核心的原因是实现职责分离,不同阶段借助不同的处理方式(dataExchange,typeHandler等),使整个处理逻辑更清晰。

 
0

ibatis源码学习4_参数和结果的映射原理的更多相关文章

  1. ibatis源码学习1&lowbar;整体设计和核心流程

    背景介绍ibatis实现之前,先来看一段jdbc代码: Class.forName("com.mysql.jdbc.Driver"); String url = "jdb ...

  2. Qt Creator 源码学习笔记04,多插件实现原理分析

    阅读本文大概需要 8 分钟 插件听上去很高大上,实际上就是一个个动态库,动态库在不同平台下后缀名不一样,比如在 Windows下以.dll结尾,Linux 下以.so结尾 开发插件其实就是开发一个动态 ...

  3. ibatis源码学习2&lowbar;初始化和配置文件解析

    问题在详细介绍ibatis初始化过程之前,让我们先来思考几个问题. 1. ibatis初始化的目标是什么?上文中提到过,ibatis初始化的核心目标是构造SqlMapClientImpl对象,主要是其 ...

  4. ibatis源码学习3&lowbar;源码包结构

    ibatis的技术是从xml里面字符串转换成JAVA对象,对象填充JDBC的statement查询,然后从resultset取对象返回,另外利用ThreadLocal实现线程安全,JDBC保证了事务控 ...

  5. 【mybatis源码学习】mybatis的sql语句映射

    一.重要的接口和类 org.apache.ibatis.scripting.LanguageDriver //语言驱动org.apache.ibatis.scripting.xmltags.XMLLa ...

  6. SpringBoot源码学习系列之嵌入式Servlet容器

    目录 1.博客前言简单介绍 2.定制servlet容器 3.变换servlet容器 4.servlet容器启动原理 SpringBoot源码学习系列之嵌入式Servlet容器启动原理 @ 1.博客前言 ...

  7. mybatis源码学习&lpar;一&rpar; 原生mybatis源码学习

    最近这一周,主要在学习mybatis相关的源码,所以记录一下吧,算是一点学习心得 个人觉得,mybatis的源码,大致可以分为两部分,一是原生的mybatis,二是和spring整合之后的mybati ...

  8. mybatis源码学习&lpar;三&rpar;-一级缓存二级缓存

    本文主要是个人学习mybatis缓存的学习笔记,主要有以下几个知识点 1.一级缓存配置信息 2.一级缓存源码学习笔记 3.二级缓存配置信息 4.二级缓存源码 5.一级缓存.二级缓存总结 1.一级缓存配 ...

  9. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

随机推荐

  1. Ubuntu 14&period;04 LTS中怎样解决系统设置残缺的问题

    iBus卸载之后,系统设置会缺失,是Ubuntu 14.04和iBus之间的关系引起的,但是如果直接安装 sudo apt-get install ubuntu-desktop 这个会把amazon广 ...

  2. Codeforces Round &num;372 &lpar;Div&period; 2&rpar; C 数学

    http://codeforces.com/contest/716/problem/C 题目大意:感觉这道题还是好懂得吧. 思路:不断的通过列式子的出来了.首先我们定义level=i, uplevel ...

  3. Ubuntu出现ERR&lowbar;PROXY&lowbar;CONNECTION&lowbar;FAILED错误解决方案

    我是Ubuntu新手,因为想查看国外的资料,然后安装了灯笼,结果打开谷歌浏览器出现了ERR_PROXY_CONNECTION_FAILED错误,未连接到互联网,代理服务器出现错误,然后Firefox也 ...

  4. Unity Mathf&sol;Math数学运算函数说明全集&lpar;Chinar总结&rpar;

    Unity Mathf 数学函数库 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...

  5. TCP&sol;IP Illustrated Vol1 Second Edition即英文版第二版,TCP部分个人勘误

    目前已经有了英文版第二版的TCPIP详解,中文版暂时还没有,但是英文版还是有好几处错误,作者和官方竟然没有维护一个勘误表. 个人阅读过程中针对TCP部分可能有问题的地方简单勘误一下 P596:示意图中 ...

  6. java字节码文件指令集

    网上找的没有指令码这列  自己把它加上 更方便查阅 指令从0x00-0xc9 没有0xba 常量入栈指令 指令码 操作码(助记符) 操作数 描述(栈指操作数栈) 0x01 aconst_null nu ...

  7. C&num;网络编程(二)应用篇

    (一)TcpListen类.TcpClient类 TcpListener类和TcpClient类都是System.Net.Sockets命名空间下的类,利用TcpListener和TcpClient可 ...

  8. Eclipse常用操作

    一 Eclipse常用快捷键 ctrl+Q:跳到最后一次编辑处. ctrl+F:在当前文件中查找并替换. ctrl+T:查看类的继承关系. alt+左箭头:返回到光标的上一个位置. alt+右箭头:前 ...

  9. 【POJ】2947 Widget Factory(高斯消元)

    http://poj.org/problem?id=2947 各种逗啊..还好1a了.. 题意我就不说了,百度一大把. 转换为mod的方程组,即 (x[1,1]*a[1])+(x[1,2]*a[2]) ...

  10. hive中select中DISTINCT的技巧和使用

    hive中select中DISTINCT的技巧和使用 单表的唯一查询用:distinct 多表的唯一查询用:group by 在使用MySQL时,有时需要查询出某个字段不重复的记录,虽然mysql提供 ...