Mybatis中的resultMap和resultType 和parameterMap与 parameterType的区别

不念不忘少年蓝@ 2022-01-23 11:15 565阅读 0赞

说实话Mybatis的知识点不少,在之前讲过mybatis的一级缓存与二级缓存(作用于在Executor组件)。而今天的主题是parameterMap 、parameterType、resultType、parameterMap,它们作用于ParameterHandler组件与ResultSetHandler 组件层。如果有人问你这类问题,其实是在考你的基本功(是否知道mybatis的架构或源码),这样才能get到对方关注的点。

在XML 映射文件中随处可用:parameterMap 、parameterType、resultType、parameterMap

  1. <select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.String" >
  2. select
  3. <include refid="Base_Column_List" />
  4. from bas_branchroute
  5. where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
  6. </select>
  7. <delete id="deleteByPrimaryKey" parameterType="java.lang.String" >
  8. delete from bas_branchroute
  9. where branch_route_id = #{branchRouteId,jdbcType=VARCHAR}
  10. </delete>
  11. <delete id="deleteByExample" parameterType="com.xxx.oms.model.BasBranchrouteExample" >
  12. delete from bas_branchroute
  13. <if test="_parameter != null" >
  14. <include refid="Example_Where_Clause" />
  15. </if>
  16. </delete>

基本用法

parameterMap

这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。

parameterType

将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。

resultType

从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。

resultMap

外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。

数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,而 ResultMap 就是 MyBatis 对这个问题的答案。它支持的元素有constructor(实例化类时注入)、association(关联一个对象)、collection(关联多个对象),可以表示对象间一对一、一对多等关系。更多介绍见官方文档

  1. <!-- 构造方法 -->
  2. <resultMap id="userMap" type="User">
  3. <constructor>
  4. <idArg column="id" javaType="int"/>
  5. <arg column="username" javaType="String"/>
  6. <arg column="age" javaType="_int"/>
  7. </constructor>
  8. </resultMap>
  9. <!-- 一对一 -->
  10. <resultMap id="userMap" type="User">
  11. <id property="id" column="id"></id>
  12. <result property="username" column="username"></result>
  13. <result property="password" column="password"></result>
  14. <result property="address" column="address"></result>
  15. <result property="email" column="email"></result>
  16. <association property="role" javaType="Role">
  17. <id property="id" column="role_id"></id>
  18. <result property="name" column="role_name"></result>
  19. </association>
  20. </resultMap>
  21. <!-- 一对多 -->
  22. <resultMap id="userMap" type="User">
  23. <id property="id" column="id"></id>
  24. <result property="username" column="username"></result>
  25. <result property="password" column="password"></result>
  26. <result property="address" column="address"></result>
  27. <result property="email" column="email"></result>
  28. <collection property="roles" ofType="Role">
  29. <id property="id" column="role_id"></id>
  30. <result property="name" column="role_name"></result>
  31. </collection>
  32. </resultMap>
  33. <!-- 一对多:嵌套 Select 查询 -->
  34. <resultMap id="menusMap" type="Menu">
  35. <id property="id" column="id"></id>
  36. <result property="name" column="name"></result>
  37. <result property="url" column="url"></result>
  38. <result property="m_desc" column="m_desc"></result>
  39. <result property="parent_id" column="parent_id"></result>
  40. <!-- ofType="Menu" 对应返回数据的类型-->
  41. <!--select="getMenus" 指定了SELECT语句的id-->
  42. <!--column="{parent_id=id}" 则是列的别名,参数的表达式-->
  43. <collection property="childMenu" ofType="Menu" select="getMenus" column="{parent_id=id}"></collection>
  44. </resultMap>

系统架构

关键组件

  • SqlSession:selectOne、selectList、selectMap、select、insert、update、delete、commit、rollback、flushStatements、close、clearCache、getConfiguration、getMapper、getConnection
  • Executor:update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
  • StatementHandler:prepare, parameterize, batch, update, query
  • ParameterHandler:getParameterObject, setParameters
  • ResultSetHandler:handleResultSets, handleOutputParameters

内部交互

watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2l0c29mdGNoZW5mZWk_size_16_color_FFFFFF_t_70

StatementHandler是非常重要的一层,它主要是对Statement的各种特殊处理(不负责执行),它支持三种Statement、
Prepared、Callable模式,默认值:Prepared。RoutingStatementHandler提供了一种适配模式化的统一入口。

20190625173953308.png

源码解析

你理解了上面的内容,再回头看源码就不复杂了!源码分析基于mybatis-3.4.4版本展开。

ParameterHandler

  1. public interface ParameterHandler {
  2. //获取参数对象
  3. Object getParameterObject();
  4. //设置参数对象
  5. void setParameters(PreparedStatement ps) throws SQLException;
  6. }
  7. public class DefaultParameterHandler implements ParameterHandler {
  8. public void setParameters(PreparedStatement ps) {
  9. ErrorContext.instance().activity("setting parameters").object(this.mappedStatement.getParameterMap().getId());
  10. List<ParameterMapping> parameterMappings = this.boundSql.getParameterMappings();
  11. if (parameterMappings != null) {
  12. for(int i = 0; i < parameterMappings.size(); ++i) {//遍历参数
  13. ParameterMapping parameterMapping = (ParameterMapping)parameterMappings.get(i);
  14. if (parameterMapping.getMode() != ParameterMode.OUT) {
  15. String propertyName = parameterMapping.getProperty();//参数名称
  16. Object value;//参数值
  17. if (this.boundSql.hasAdditionalParameter(propertyName)) {//是否有这个参数
  18. value = this.boundSql.getAdditionalParameter(propertyName);//获取参数值
  19. } else if (this.parameterObject == null) {
  20. value = null;
  21. } else if (this.typeHandlerRegistry.hasTypeHandler(this.parameterObject.getClass())) {
  22. value = this.parameterObject;
  23. } else {
  24. MetaObject metaObject = this.configuration.newMetaObject(this.parameterObject);//这个是什么鬼,不急慢慢看
  25. value = metaObject.getValue(propertyName);
  26. }
  27. TypeHandler typeHandler = parameterMapping.getTypeHandler();
  28. JdbcType jdbcType = parameterMapping.getJdbcType();
  29. if (value == null && jdbcType == null) {
  30. jdbcType = this.configuration.getJdbcTypeForNull();
  31. }
  32. try {
  33. typeHandler.setParameter(ps, i + 1, value, jdbcType);
  34. } catch (TypeException var10) {
  35. throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var10, var10);
  36. } catch (SQLException var11) {
  37. throw new TypeException("Could not set parameters for mapping: " + parameterMapping + ". Cause: " + var11, var11);
  38. }
  39. }
  40. }
  41. }
  42. }
  43. }

问题1:确实setParameters()是关键点,它是什么时候被触发的呢?

它分别为 PreparedStatementHandler 和 CallableStatementHandler (执行存储过程)的 parameterize()中被调用。它在构建Statement 时被触发。

  1. public class SimpleExecutor extends BaseExecutor {
  2. public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
  3. Statement stmt = null;
  4. try {
  5. Configuration configuration = ms.getConfiguration();
  6. StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, rowBounds, resultHandler, boundSql);
  7. //构建Statement对象
  8. stmt = prepareStatement(handler, ms.getStatementLog());
  9. return handler.<E>query(stmt, resultHandler);
  10. } finally {
  11. closeStatement(stmt);
  12. }
  13. }
  14. private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
  15. Statement stmt;
  16. Connection connection = getConnection(statementLog);
  17. //此时很关键,最终路径:RoutingStatementHandler.parameterize -> PreparedStatementHandler.parameterize
  18. stmt = handler.prepare(connection);
  19. handler.parameterize(stmt);
  20. return stmt;
  21. }
  22. }
  23. public class PreparedStatementHandler extends BaseStatementHandler {
  24. public void parameterize(Statement statement) throws SQLException {
  25. //最终执行这个
  26. parameterHandler.setParameters((PreparedStatement) statement);
  27. }
  28. }

问题2:MetaObject是什么鬼?

MetaObject是将Properties映射为bean属性,实际上就是提供 类|集合|Map 的一种自动识别的访问形式。PropertyTokenizer是的属性分词器,提供了对象和集合的一种概念形式,如: object.parent.name(只是记录了 object 而已,可以通过next方法创建一个对象,返回new PropertyTokenizer(“parent”) 再次调用next 返回new PropertyTokenizer(“name”))。

  1. //基本用法
  2. @Test
  3. public void testGetSet(){
  4. Animal animal = new Animal();
  5. //testBean
  6. MetaObject metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
  7. metaObject.setValue("name","bean");
  8. System.out.println(animal.getName());
  9. //testMap
  10. animal.setName("map");
  11. Map<String,Animal> map = new HashMap<>();
  12. metaObject = MetaObject.forObject(map, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
  13. metaObject.setValue("deer",animal);
  14. System.out.println(map.get("deer").getName());
  15. //testCollection
  16. animal.setName("collection");
  17. List<Animal> list = new ArrayList<>();
  18. metaObject = MetaObject.forObject(list, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
  19. metaObject.add(animal);
  20. System.out.println(list.get(0).getName());
  21. //testTokenizer
  22. animal.setParent(new Animal());
  23. metaObject = MetaObject.forObject(animal, new DefaultObjectFactory(), new DefaultObjectWrapperFactory(), new DefaultReflectorFactory());
  24. metaObject.setValue("parent.name","tokenizer");
  25. System.out.println(animal.getParent().getName());
  26. }
  27. //源码
  28. public class MetaObject {
  29. private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
  30. this.originalObject = object;//原始对象
  31. this.objectFactory = objectFactory;
  32. this.objectWrapperFactory = objectWrapperFactory;
  33. if (object instanceof ObjectWrapper) {//原始对象为ObjectWrapper
  34. this.objectWrapper = (ObjectWrapper) object;
  35. } else if (objectWrapperFactory.hasWrapperFor(object)) {
  36. this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
  37. } else if (object instanceof Map) {//原始对象为Map,提供了 object.setName,object.setAge 以及类似 object.getParent().setName(),object.getParent().getParent().setName() 的赋值方式
  38. this.objectWrapper = new MapWrapper(this, (Map) object);
  39. } else if (object instanceof Collection) {//原始对象为Collection,提供了集合的add,addAll 方法
  40. this.objectWrapper = new CollectionWrapper(this, (Collection) object);
  41. } else {//默认为BeanWrapper,提供的赋值与map相同
  42. this.objectWrapper = new BeanWrapper(this, object);
  43. }
  44. }
  45. //构建
  46. public static MetaObject forObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory) {
  47. if (object == null) {
  48. return NULL_META_OBJECT;//记录Class相关的getter
  49. } else {
  50. return new MetaObject(object, objectFactory, objectWrapperFactory);
  51. }
  52. }
  53. public Object getValue(String name) {
  54. PropertyTokenizer prop = new PropertyTokenizer(name);
  55. if (prop.hasNext()) {
  56. MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
  57. if (metaValue == MetaObject.NULL_META_OBJECT) {
  58. return null;
  59. } else {
  60. return metaValue.getValue(prop.getChildren());
  61. }
  62. } else {
  63. return objectWrapper.get(prop);
  64. }
  65. }
  66. public void setValue(String name, Object value) {
  67. //属性分词器,识别属性.符号
  68. PropertyTokenizer prop = new PropertyTokenizer(name);
  69. if (prop.hasNext()) {
  70. MetaObject metaValue = metaObjectForProperty(prop.getIndexedName());
  71. if (metaValue == MetaObject.NULL_META_OBJECT) {
  72. if (value == null && prop.getChildren() != null) {
  73. return; // don't instantiate child path if value is null
  74. } else {
  75. metaValue = objectWrapper.instantiatePropertyValue(name, prop, objectFactory);
  76. }
  77. }
  78. metaValue.setValue(prop.getChildren(), value);
  79. } else {
  80. objectWrapper.set(prop, value);
  81. }
  82. }
  83. }

ResultSetHandler

还是按老的套路,以handleResultSets()为突破口,它主要是对Statement执行后产生的结果集转化成List。

  1. public interface ResultSetHandler {
  2. //将Statement执行后产生的结果集转化成list
  3. <E> List<E> handleResultSets(Statement var1) throws SQLException;
  4. //将Statement执行后产生的结果集转化成cursor
  5. <E> Cursor<E> handleCursorResultSets(Statement var1) throws SQLException;
  6. //处理存储过程执行后的输出参数
  7. void handleOutputParameters(CallableStatement var1) throws SQLException;
  8. }
  9. public class DefaultResultSetHandler implements ResultSetHandler {
  10. @Override
  11. public List<Object> handleResultSets(Statement stmt) throws SQLException {
  12. ErrorContext.instance().activity("handling results").object(mappedStatement.getId());
  13. final List<Object> multipleResults = new ArrayList<Object>();
  14. int resultSetCount = 0;
  15. ResultSetWrapper rsw = getFirstResultSet(stmt);//结果集的第一个结果
  16. List<ResultMap> resultMaps = mappedStatement.getResultMaps();
  17. int resultMapCount = resultMaps.size();
  18. validateResultMapsCount(rsw, resultMapCount);
  19. while (rsw != null && resultMapCount > resultSetCount) {
  20. ResultMap resultMap = resultMaps.get(resultSetCount);
  21. //根据resultMap处理rsw生成java对象,最关键的一步,最终调用getRowValue
  22. handleResultSet(rsw, resultMap, multipleResults, null);
  23. //获取结果集的下一个结果
  24. rsw = getNextResultSet(stmt);
  25. cleanUpAfterHandlingResultSet();
  26. resultSetCount++;
  27. }
  28. String[] resultSets = mappedStatement.getResultSets();
  29. if (resultSets != null) {//和resultMaps的遍历处理类似
  30. while (rsw != null && resultSetCount < resultSets.length) {
  31. ResultMapping parentMapping = nextResultMaps.get(resultSets[resultSetCount]);
  32. if (parentMapping != null) {
  33. String nestedResultMapId = parentMapping.getNestedResultMapId();
  34. ResultMap resultMap = configuration.getResultMap(nestedResultMapId);
  35. handleResultSet(rsw, resultMap, null, parentMapping);
  36. }
  37. rsw = getNextResultSet(stmt);
  38. cleanUpAfterHandlingResultSet();
  39. resultSetCount++;
  40. }
  41. }
  42. return collapseSingleResultList(multipleResults);
  43. }
  44. private void handleResultSet(ResultSetWrapper rsw, ResultMap resultMap, List<Object> multipleResults, ResultMapping parentMapping) throws SQLException {
  45. try {
  46. if (parentMapping != null) {
  47. handleRowValues(rsw, resultMap, null, RowBounds.DEFAULT, parentMapping);
  48. } else {
  49. if (resultHandler == null) {
  50. DefaultResultHandler defaultResultHandler = new DefaultResultHandler(objectFactory);
  51. //最终会调用getRowValue
  52. handleRowValues(rsw, resultMap, defaultResultHandler, rowBounds, null);
  53. multipleResults.add(defaultResultHandler.getResultList());
  54. } else {//最终会调用getRowValue
  55. handleRowValues(rsw, resultMap, resultHandler, rowBounds, null);
  56. }
  57. }
  58. } finally {
  59. // issue #228 (close resultsets)
  60. closeResultSet(rsw.getResultSet());
  61. }
  62. }
  63. private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap) throws SQLException {
  64. final ResultLoaderMap lazyLoader = new ResultLoaderMap();
  65. //生成对象
  66. Object rowValue = createResultObject(rsw, resultMap, lazyLoader, null);
  67. if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
  68. final MetaObject metaObject = configuration.newMetaObject(rowValue);
  69. boolean foundValues = this.useConstructorMappings;
  70. if (shouldApplyAutomaticMappings(resultMap, false)) {//自动映射
  71. foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, null) || foundValues;
  72. }
  73. //property映射,ResultLoaderMap属性嵌套查询且懒加载
  74. foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, null) || foundValues;
  75. foundValues = lazyLoader.size() > 0 || foundValues;
  76. rowValue = (foundValues || configuration.isReturnInstanceForEmptyRow()) ? rowValue : null;
  77. }
  78. return rowValue;
  79. }
  80. private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
  81. throws SQLException {
  82. final Class<?> resultType = resultMap.getType();
  83. final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
  84. final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
  85. if (hasTypeHandlerForResultObject(rsw, resultType)) {//存在适用的typeHanlder类,事实上一般为基本数据类型或者其封装类
  86. return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
  87. } else if (!constructorMappings.isEmpty()) {//有参构造函数的constructor映射
  88. return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
  89. } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {//接口或者无参构造函数
  90. return objectFactory.create(resultType);
  91. } else if (shouldApplyAutomaticMappings(resultMap, false)) {//有参构造函数的自动映射
  92. return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix);
  93. }
  94. throw new ExecutorException("Do not know how to create an instance of " + resultType);
  95. }
  96. }

它的被调用链不复杂,基本上可以直接定位到SimpleStatementHandler。

此外还有一点细节:在xml中select的查询中配置的不是resultMap而是resultType,在mybatis中也是按照resultMap进行存储的,具体验证可看下面源码分析。

  1. public class SimpleStatementHandler extends BaseStatementHandler {
  2. @Override
  3. public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
  4. String sql = boundSql.getSql();
  5. statement.execute(sql);
  6. return resultSetHandler.<E>handleResultSets(statement);//直接调用我们关注的方法
  7. }
  8. }
  9. public class MapperBuilderAssistant extends BaseBuilder {
  10. //在addMappedStatement方法(构建MappedStatement,它非常关键)中被调用
  11. private List<ResultMap> getStatementResultMaps(
  12. String resultMap,
  13. Class<?> resultType,
  14. String statementId) {
  15. resultMap = applyCurrentNamespace(resultMap, true);
  16. List<ResultMap> resultMaps = new ArrayList<ResultMap>();
  17. if (resultMap != null) {//resultMap配置支持多个用,分割的resultMap
  18. String[] resultMapNames = resultMap.split(",");
  19. for (String resultMapName : resultMapNames) {
  20. try {
  21. resultMaps.add(configuration.getResultMap(resultMapName.trim()));
  22. } catch (IllegalArgumentException e) {
  23. throw new IncompleteElementException("Could not find result map " + resultMapName, e);
  24. }
  25. }
  26. } else if (resultType != null) {//resultType会构建一个inlineResultMap
  27. ResultMap inlineResultMap = new ResultMap.Builder(
  28. configuration,
  29. statementId + "-Inline",
  30. resultType,
  31. new ArrayList<ResultMapping>(),
  32. null).build();
  33. resultMaps.add(inlineResultMap);
  34. }
  35. return resultMaps;
  36. }
  37. }
  38. /**
  39. * StatementHandler中通过它可以获取关于Statement所有细节
  40. **/
  41. public final class MappedStatement {
  42. private String resource;
  43. private Configuration configuration;//配置
  44. private String id;//mapper中的id
  45. private Integer fetchSize;//batch需要
  46. private Integer timeout;//超时
  47. private StatementType statementType;
  48. private ResultSetType resultSetType;
  49. private SqlSource sqlSource;//数据源
  50. private Cache cache;
  51. private ParameterMap parameterMap;
  52. private List<ResultMap> resultMaps;
  53. private boolean flushCacheRequired;
  54. private boolean useCache;
  55. private boolean resultOrdered;
  56. private SqlCommandType sqlCommandType;
  57. private KeyGenerator keyGenerator;
  58. private String[] keyProperties;
  59. private String[] keyColumns;
  60. private boolean hasNestedResultMaps;
  61. private String databaseId;
  62. private Log statementLog;
  63. private LanguageDriver lang;
  64. private String[] resultSets;
  65. public BoundSql getBoundSql(Object parameterObject) {//可执行的sql
  66. ...
  67. }
  68. }

看源码其实没有什么技巧,要大胆提问+你的猜想,然后去验证,并从中体会学习优秀的设计。88174217

发表评论

表情:
评论列表 (有 0 条评论,565人围观)

还没有评论,来说两句吧...

相关阅读