热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

mybatis映射成多个list_Mybatis之对象工厂

前言在上文Mybatis之XML如何映射到方法中讲到结果映射的时候,需要创建好对象,然后再给对象的属性赋值,而创建对象就用到了Mybati
ca2065abde0c1b2088571b5bf6f572d5.png
前言

在上文Mybatis之XML如何映射到方法中讲到结果映射的时候,需要创建好对象,然后再给对象的属性赋值,而创建对象就用到了Mybatis的内置的对象工厂类DefaultObjectFactory,当然Mybatis也提供了扩展机制,用户可以实现自己的对象工厂。

对象工厂

上文中介绍了结果映射的相关逻辑在DefaultResultSetHandler处理器中,下面重点看一下创建结果对象的方法:

private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List> constructorArgTypes, List constructorArgs, String columnPrefix) throws SQLException { final Class resultType = resultMap.getType(); final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory); final List constructorMappings = resultMap.getConstructorResultMappings(); if (hasTypeHandlerForResultObject(rsw, resultType)) { return createPrimitiveResultObject(rsw, resultMap, columnPrefix); } else if (!constructorMappings.isEmpty()) { return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix); } else if (resultType.isInterface() || metaType.hasDefaultConstructor()) { return objectFactory.create(resultType); } else if (shouldApplyAutomaticMappings(resultMap, false)) { return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs, columnPrefix); } throw new ExecutorException("Do not know how to create an instance of " + resultType); }

这是创建结果对象的核心方法,创建对象的时候分成了4中情况分别是:
1.结果对象Mybatis本身提供了处理器,也就是xxTypeHandler,更多实现在org.apache.ibatis.type路径下,这种情况可以直接从Resultset中获取结果返回结果值,可以认为都是原生的类型;
2.在结果映射中指定了构造器,无需使用默认的构造器;
3.结果对象是接口或者结果对象有默认的构造器;
4.以上情况都不满足会检查是否配置了自动映射,默认开启;
以上情况都不满足则直接抛出异常,下面具体分析一下4种类型都在什么情况下执行;

1.原生类型

表示结果集是原生类型,比如string类型,相关xml配置例如:

3cac7d64e18f623998f9147c00401328.png

直接返回了原生类型string,Mybatis提供了StringTypeHandler处理器:

private Object createPrimitiveResultObject(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException { final Class resultType = resultMap.getType(); final String columnName; if (!resultMap.getResultMappings().isEmpty()) { final List resultMappingList = resultMap.getResultMappings(); final ResultMapping mapping = resultMappingList.get(0); columnName = prependPrefix(mapping.getColumn(), columnPrefix); } else { columnName = rsw.getColumnNames().get(0); } final TypeHandler typeHandler = rsw.getTypeHandler(resultType, columnName); return typeHandler.getResult(rsw.getResultSet(), columnName); }

首先获取字段名称,然后通过返回类型获取处理器,最后直接从ResultSet中获取结果;在获取字段名称的时候也分两种情况分别是直接配置resultType和配置resultMap两种情况,不管哪种情况如果配置了多个映射字段都只获取第一个,比如:

select id,title from blog where id = #{id} and author=#{author,javaType=string}

这种情况只会返回id的值,并且被转为string类型;

2.指定构造器

在resultmap中指定了构造器参数,比如如下的例子:

678e31c57817e7be551d56b4f4b99cb8.png

如上所示指定了id为参数的构造器,这种情况在通过对象工厂创建对象的时候不会使用默认的无参构造器,会使用带id参数的构造器,部分代码如下所示:

objectFactory.create(resultType, constructorArgTypes, constructorArgs)

三个参数分别是:返回的对象类型,构造器参数类型,构造器参数值;

3.默认构造器

无需指定构造器参数类型和构造器参数值,当然前提是类提供了默认的构造器,直接调用create方法:

objectFactory.create(resultType)

同指定构造器的方式唯一的区别就是构造器参数类型和构造器参数值都为null;下面具体看一下对象工厂的默认实现:

@Override public T create(Class type) { return create(type, null, null); } @SuppressWarnings("unchecked") @Override public T create(Class type, List> constructorArgTypes, List constructorArgs) { Class classToCreate = resolveInterface(type); // we know types are assignable return (T) instantiateClass(classToCreate, constructorArgTypes, constructorArgs); } private T instantiateClass(Class type, List> constructorArgTypes, List constructorArgs) { try { Constructor constructor; if (constructorArgTypes == null || constructorArgs == null) { constructor = type.getDeclaredConstructor(); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(); } constructor = type.getDeclaredConstructor(constructorArgTypes.toArray(new Class[constructorArgTypes.size()])); if (!constructor.isAccessible()) { constructor.setAccessible(true); } return constructor.newInstance(constructorArgs.toArray(new Object[constructorArgs.size()])); } catch (Exception e) { ...省略... } }

分别提供了有参数和无参数的构造器,没有参数的时候直接调用默认的构造器;有指定构造器参数类型,则获取有参数类型的构造器,当然对应的类里面需要提供对应参数的构造器,不然会报错;获取构造器之后,直接通过newInstance创建类对象;

4.自动映射

即没有提供默认的构造器也没有提供指定构造参数的构造器,那么Mybatis会进行自动映射;自动映射是有一个开关的,默认开启,可以在configuration里面和resultMap里面配置autoMapping开关;准备如下实例看具体是如何映射的:

41193e92d22f7584176ed2433c9ee975.png

blog类里面提供了id为参数的构造器,这样就没有了默认的构造器;这时候Mybatis会找blog里面是否存在id,title类参数的构造器,如果存在则获取此构造器创建对象,不存在则报错,如下所示:

Caused by: org.apache.ibatis.executor.ExecutorException: No constructor found in com.mybatis.vo.Blog matching [java.lang.Long, java.lang.String]

5.无法创建对象

如果第四种情况也不满足,即可以配置autoMapping="false",则Mybatis直接抛出无法创建对象,具体异常如下所示:

Caused by: org.apache.ibatis.executor.ExecutorException: Do not know how to create an instance of class com.mybatis.vo.Blog自定义对象工厂

实现自己的对象工厂也很简单,实现接口ObjectFactory或者重载DefaultObjectFactory即可,此处为了方便我们直接重载DefaultObjectFactory,并实现当需要实例化的对象是Blog时,如果没有指定author则给定一个默认值,实现如下:

public class MyObjectFactory extends DefaultObjectFactory { private static final long serialVersionUID = 1L; @Override public T create(Class type) { System.out.println("create:" + type); if (type.equals(Blog.class)){ Blog blog = (Blog)super.create(type); blog.setAuthor("ksfzhaohui"); return (T) blog; } return super.create(type); } ...省略...}

实现也很简单,只需要在create方法中判定指定的类型为Blog,然后调用父类的create方法创建对象,然后设置默认值;当然还需要在configuration中配置自定义对象工厂才能生效:

de81f134d48d45a989575aaf7ac5ec62.png
总结

本文重点介绍了默认的对象工厂和创建对象的五种情况分别是:原生的类型,指定了构造器,有默认构造器的类,使用自动映射的情况以及不满足前面四种情况的处理;最后简单试下了一个自定义的对象工厂,用来给指定的类对象指定默认的属性值。

示例代码地址

https://github.com/ksfzhaohui/blog/tree/master/mybatis



推荐阅读
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • 本文详细介绍了Spring的JdbcTemplate的使用方法,包括执行存储过程、存储函数的call()方法,执行任何SQL语句的execute()方法,单个更新和批量更新的update()和batchUpdate()方法,以及单查和列表查询的query()和queryForXXX()方法。提供了经过测试的API供使用。 ... [详细]
  • 本文讨论了在数据库打开和关闭状态下,重新命名或移动数据文件和日志文件的情况。针对性能和维护原因,需要将数据库文件移动到不同的磁盘上或重新分配到新的磁盘上的情况,以及在操作系统级别移动或重命名数据文件但未在数据库层进行重命名导致报错的情况。通过三个方面进行讨论。 ... [详细]
  • 标题: ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • Oracle seg,V$TEMPSEG_USAGE与Oracle排序的关系及使用方法
    本文介绍了Oracle seg,V$TEMPSEG_USAGE与Oracle排序之间的关系,V$TEMPSEG_USAGE是V_$SORT_USAGE的同义词,通过查询dba_objects和dba_synonyms视图可以了解到它们的详细信息。同时,还探讨了V$TEMPSEG_USAGE的使用方法。 ... [详细]
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • 解决.net项目中未注册“microsoft.ACE.oledb.12.0”提供程序的方法
    在开发.net项目中,通过microsoft.ACE.oledb读取excel文件信息时,报错“未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序”。本文提供了解决这个问题的方法,包括错误描述和代码示例。通过注册提供程序和修改连接字符串,可以成功读取excel文件信息。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • Week04面向对象设计与继承学习总结及作业要求
    本文总结了Week04面向对象设计与继承的重要知识点,包括对象、类、封装性、静态属性、静态方法、重载、继承和多态等。同时,还介绍了私有构造函数在类外部无法被调用、static不能访问非静态属性以及该类实例可以共享类里的static属性等内容。此外,还提到了作业要求,包括讲述一个在网上商城购物或在班级博客进行学习的故事,并使用Markdown的加粗标记和语句块标记标注关键名词和动词。最后,还提到了参考资料中关于UML类图如何绘制的范例。 ... [详细]
author-avatar
A600810
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有