问题
在详细介绍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的相关类图:

上面的这张类图需说明两点:
1. 从纵向上体现了Statement类的整体继承关系,MappedStatement接口提供了SQL执行上下文信息和执行操作,如ParameterMap、ResultMap、SQL、Timeout等上下文信息和executeQueryForList等操作信息;BaseStatement抽象类提供了MappedStatement的初步实现,它组合了MappedStatement需要的上下文信息;GeneralStatement实现类提供了MappedStatement的执行操作的基本实现;InsertStatement、SelectStatement等实现类提供了针对不同类型SQL操作的特定实现。
2. 从横向上体现了Statement类的初始化数据和请求处理数据的分离。 类图中的第一行对象(ParameterMap/ResultMap/SQL等)在初始化过程中会构造完毕,请求处理时直接获取即可; 类图中的第三行对象(RequestScope)在请求处理时才会创建,通过执行方法的参数传入,再进行后续处理。

映射主要类图
参数和结果映射的主要类图如下:

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对象实现,这里就不再详述。

小结
最后,以一张图总结参数和结果的映射整体流程:

每个映射过程都主要拆分为两步,为什么需要这样的设计?一个核心的原因是实现职责分离,不同阶段借助不同的处理方式(dataExchange,typeHandler等),使整个处理逻辑更清晰。

 
0

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

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

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

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

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

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

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

  4. ibatis源码学习3_源码包结构

    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源码学习(一) 原生mybatis源码学习

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

  8. mybatis源码学习(三)-一级缓存二级缓存

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

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

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

随机推荐

  1. 第十章 消息驱动的微服务: Spring Cloud Stream

    Spring Cloud Stream 是一个用来为微服务应用构建消息驱动能力的框架. 它可以基于Spring Boot 来创建独立的. 可用于生产的 Spring 应用程序. 它通过使用 Sprin ...

  2. sql developer Oracle 数据库 用户对象下表及表结构的导入导出

    Oracle数据库表数据及结构的导入导出   导出的主机与即将导入到的目标主机的tablespace 及用户名需一直!!!!!

  3. 解决git报ssh variant 'simple' does not support setting port

    解决办法 在git bash中输入命令 git config --global ssh.variant ssh

  4. 使用ffmpeg合并视频文件的三种方法

    ffmpeg合并视频的方法有三种.国内大多数仅介绍了其中之一.于是觉得有必要翻译一下.其实在ffmpeg的 FAQ文档中有比较详细的说明. 使用concat协议进行视频文件的合并 这种方式的适用场景是 ...

  5. fatal error C1010: 在查找预编译头时遇到意外的文件结尾 (转)

    错误描述:fatal error C1010: 在查找预编译头时遇到意外的文件结尾.是否忘记了向源中添加“#include "stdafx.h"”? 错误分析:     此错误发生 ...

  6. spring+mybatis结合实例

    1.通过上两篇文章的学习,大致了解了spring和mybatis的架构和使用原理,下面这篇文章就将给出两者结合的一个小实例,通过该实例进一步探索这两个框架的魅力,工程所需要的所有jar包都在链接:ht ...

  7. MVC项目不同域之间的UrlRouting

    一.DomainAction,方便生成不同域下的url 1.新建3个MVC项目,一个公用类库WebCore Demo.WebApplication0 绑定域名 www.demo.com demo.co ...

  8. java aop 日志打印 正则设置

    package tz.lion.Utils.aop; import com.alibaba.fastjson.JSON;import org.springframework.web.multipart ...

  9. Linux实战教学笔记26:http协议原理

    目录 第二十六节 http协议原理 第1章 Web服务基础 1.1 http服务重要基础 1.2 HTTP协议 1.3 HTTP资源 1.4 网站流量度量术语 1.5 www服务软件介绍 1.6 本章 ...

  10. mybatis+oracle如何批量执行多条update

    接口 public void setStatus(List<Columns> columnsList); mapping xmlmapping 中使用foreach,关于标签的使用,资料非 ...