MyBatis<select>节点中的resultType和resultMap属性

本文详细解释了在MyBatis中resultType和resultMap的配置用途,如何处理查询结果名称不一致问题,以及一对一和一对多查询时resultMap的正确使用方法,包括自定义别名和列名映射的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.为什么要进行resultType或resultMap注解配置
当需要实现的数据访问是查询类型的,在<select>节点中必须配置resultType或resultMap中的某1个属性(二选一),如果都没有指定,则会出现如下错误:

Caused by: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'cn.tedu.mybatis.UserMapper.count'.  It's likely that neither a Result Type nor a Result Map was specified.

解决因为名称不一致导致MyBatis无法自动封装查询结果的问题

  • 当自定义字段列表时,自定义别名,使得名称保持一致,并且,在<select>节点中使用resultType即可;
  • 当使用星号表示字段列表时,配置<resultMap>以指导MyBatis封装,并且,在<select>节点中使用resultMap应用配置的<resultMap>。

2.resultType
其中,resultType指的就是“封装查询结果的数据的类型”,也可以理解为“抽象方法的返回值的类型”,例如可以配置为:

<select id="count" resultType="java.lang.Integer">
	SELECT COUNT(*) FROM t_user
</select>

如果某个查询的抽象方法的返回值是List集合类型的,例如:

List<User> findAll();

在配置<select>的resultType属性时,该属性值必须是集合中的元素的类型,例如:

<select id="findAll" resultType="cn.tedu.mybatis.User">
    SELECT * FROM t_user ORDER BY id
</select>

注意:使用了这种做法后,就需要对抽象方法名称的定义增加一个要求“不允许重载”!

3.resultMap
3.1 一对一查询中的resultMap的使用
在没有自定义别名的情况下,如果查询结果的列名与类的属性名不一致,还可以自定义<resultMap>节点进行配置,该节点的作用就是指导MyBatis封装查询到的数据!

<!-- id:自定义的名称,将应用于select节点的resultMap属性 -->
<!-- type:封装查询结果的数据类型 -->
<resultMap id="UserMap" type="cn.tedu.mybatis.User">
    <!-- result节点:将查询结果中column列对应的值封装到类的property属性中去 -->
    <result column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="password" property="password"/>
    <result column="age" property="age"/>
    <result column="phone" property="phone"/>
    <result column="email" property="email"/>
    <result column="group_id" property="groupId"/>
</resultMap>

<select id="findById" resultMap="UserMap">
    SELECT
        *
    FROM 
        t_user 
    WHERE 
        id=#{id}
</select>

在以上配置中,需要注意:<resultMap>节点中的id属性值就是<select>节点的resultMap属性值!

如果需要执行的查询操作是单表数据查询,在配置<resultMap>时,对于那些列名与属性名一致的,可以不作配置!

在配置<resultMap>时,推荐在子级使用<id>节点配置主键,然后使用<result>节点配置其它的,便于实现缓存!MyBatis有2级缓存,其中,MyBatis的1级缓存是SqlSession缓存,开发人员无法干预,2级缓存是namespace缓存,一旦开启,默认情况下,将作用于整个XML文件,需要事先在当前XML文件中添加<cache></cache>节点,表示“启用缓存”,理启用缓存,需要封装查询结果的数据类型是实现了Serializable接口的,如果某个查询不需要使用缓存,还可以在<select>节点中配置useCache="false",然后,就不需要开发人员进行其它配置了,MyBatis会自动处理缓存的数据,并且,一旦当前namespace执行了增删改类型的操作,就会重建缓存!

小结:在SQL中使用自定义别名,并在<select>中使用resultType指定封装结果的数据类型,或在SQL中使用星号表示字段列表,并配置<resultMap>节点后在<select>中使用resultMap,均可解决由于名称不一致导致无法封装查询结果的问题。

3.2 一对多查询中的resultMap的使用
在一对多的查询中,MyBatis根本就不知道怎么封装查询结果中的数据!当没有匹配的查询结果,或只有1条查询结果时,并不会报错,只是查询结果的数据不正确而已,如果查询结果有多条,则会出现如下错误信息:

Caused by: org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 2

为了保障能够正确的封装查询结果,必须使用<resultMap>来指导MyBatis封装!并且,在SQL语句中,还需要为某个id定义别名,以保证查询结果每一列的名称都不同,否则,MyBatis只会从相同的列名中排列靠前的那一列中取数据,甚至,如果在<resultMap>中使用<id>配置主键,当出现多条数据的最靠前的那一列id值相同时,重复id的数据将不会被封装!

具体配置如下:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  
<mapper namespace="cn.tedu.mybatis.GroupMapper">

	<resultMap id="GroupVOMap" type="cn.tedu.mybatis.GroupVO">
		<id column="gid" property="id" />
		<result column="name" property="name"/>
		<!-- collection节点:用于配置1对多的属性 -->
		<!-- ofType属性:集合元素类型 -->
		<collection property="users"
			ofType="cn.tedu.mybatis.User">
			<id column="id" property="id"/>
			<result column="username" property="username"/>
			<result column="password" property="password"/>
			<result column="age" property="age"/>
			<result column="phone" property="phone"/>
			<result column="email" property="email"/>
			<result column="group_id" property="groupId"/>
		</collection>
	</resultMap>

	<select id="findVOById" resultMap="GroupVOMap">
		SELECT
			t_group.id AS gid, name,
			t_user.*
		FROM 
			t_group
		LEFT JOIN
			t_user
		ON
			t_group.id=t_user.group_id
		WHERE
			t_group.id=#{id}
	</select>

</mapper>

注意:在配置一对多的查询时,在<resultMap>中,即使存在列名与属性名完全相同的数据,也必须通过<id>或<result>节点进行配置!

### MCP API 的文档与使用教程 MCP 是一种用于增强大型语言模型 (LLM) 功能的技术框架,它通过提示(Prompts)、资源(Resources)以及工具(Tools)这三种核心原语来扩展 LLM 能力[^2]。Apifox 平台也认识到 MCP 技术在 API 开发领域的重要作用,并将其应用于实际场景中[^1]。 为了实现将 `/Users/syw/project/wechatAr` 文件夹下的所有文件上传至远程服务器 `47.93.xx.xx` 用户名 `root` 下的 `/opt/ll` 目录的操作,可以基于 MCP 工具功能构建一个自定义的服务逻辑。以下是具体实现方法: #### 实现方案 利用 SCP 命令完成文件传输任务,并结合 MCP 的 Tool 功能封装此操作以便于后续调用。当关键词为“上传微信目录”时,触发该工具执行相应动作。 ```python import subprocess def upload_wechat_directory(): source_dir = "/Users/syw/project/wechatAr/*" target_server = "root@47.93.xx.xx:/opt/ll/" try: result = subprocess.run(["scp", "-r", source_dir, target_server], check=True) return {"status": "success", "message": f"All files from {source_dir} have been uploaded to {target_server}"} except Exception as e: return {"status": "error", "message": str(e)} # 将上述函数注册为 MCP 中的一个 tool tools = { "upload_wechat_directory_tool": upload_wechat_directory, } # 定义 prompt resource 配置部分省略... ``` 以上代码片段展示了如何创建一个名为 `upload_wechat_directory_tool` 的工具并将其集成到 MCP 系统里去[^3]。每当接收到匹配条件的消息比如含有特定关键字的时候就会激活对应的行为即启动SCP进程从而达成目标需求。 #### 进一步学习资料推荐 对于希望深入研究或者实践更多关于 MCP 应用案例的人士来说,《MCP 教程进阶篇》提供了丰富的实例分析技术细节值得参考阅读;另外《MCP 极简入门:超快速上手运行简单的 MCP 服务 MCP 客户端》同样是非常好的起点材料之一可以帮助初学者迅速掌握基础概念及其运作机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值