MyBatis Mapper XML 文件解读

好的,我来给你系统讲解这个 MyBatis Mapper XML 文件的全部内容。


1. 文件头部(固定模板)

xml 复制代码
<?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">

这是 XML 的文档类型声明(DTD),指定了 MyBatis Mapper 文件的约束规则,IDE 会根据它提供代码提示和校验。


2. 命名空间(namespace)

xml 复制代码
<mapper namespace="com.zy.xzne.inf.mapper.DataAlarmLogMapper">

核心作用:将 XML 文件与 Java Mapper 接口绑定。

MyBatis 通过 namespace + SQL 的 id 找到对应的 SQL 语句,例如:

  • Java 接口:DataAlarmLogMapper.insertNecAlarmLog(...)
  • XML 中对应的 SQL:namespace + insertNecAlarmLog<insert> 标签

3. <resultMap> ------ 结果集映射

第一个:NecAlarmLogResult

xml 复制代码
<resultMap type="NecAlarmLog" id="NecAlarmLogResult">
    <result property="alarmId"    column="alarm_id"    />
    <result property="parentId"   column="parent_id"   />
    <!-- ... -->
</resultMap>
属性 说明
type Java 实体类的全限定名或别名(NecAlarmLog)
id 该映射的唯一标识
property Java 实体类的属性名
column 数据库表的字段名

作用 :告诉 MyBatis 如何将数据库查询结果(alarm_id)映射到 Java 对象属性(alarmId)。这里用 type="NecAlarmLog" 说明使用了 MyBatis 的别名机制(通常在 mybatis-config.xml 中配置了 <typeAlias>)。

第二个:NecAlarmLogResultVo

xml 复制代码
<resultMap type="DataAlarmLogVo" id="NecAlarmLogResultVo">
    <!-- 基础字段同上 -->
    <result property="tunnelName"    column="tunnel_name"    />
</resultMap>

比第一个多了一个 tunnelName 属性,用于关联查询隧道表时返回隧道名称,而 NecAlarmLog 实体类中没有这个字段。


4. <sql> ------ 可复用的 SQL 片段

xml 复制代码
<sql id="selectNecAlarmLogVo">
    select alarm_id, parent_id, tenant_id, tower_id, tunnel_id, 
           err_code, err_msg, err_status, create_time 
    from nec_alarm_log
</sql>

作用 :定义一段 SQL 片段,通过 <include refid="selectNecAlarmLogVo"/> 在其他地方引用,避免重复编写相同的 SELECT 字段列表。


5. <select> ------ 查询语句

5.1 selectNecAlarmLogList ------ 条件查询列表

xml 复制代码
<select id="selectNecAlarmLogList" parameterType="DataAlarmLog" resultMap="NecAlarmLogResult">
    <include refid="selectNecAlarmLogVo"/>
    <where>
        <if test="parentId != null "> and parent_id = #{parentId}</if>
        <if test="tenantId != null "> and tenant_id = #{tenantId}</if>
        <!-- ... -->
    </where>
</select>

关键点

  • parameterType="DataAlarmLog":传入的参数是实体类,所有 <if> 条件都基于该对象的属性判断
  • resultMap="NecAlarmLogResult":使用前面定义的结果映射
  • <where> 标签:智能处理 WHERE 子句 ,如果内部所有条件都不满足,则不生成 WHERE;如果满足,会自动去掉第一个多余的 ANDOR

5.2 selectNecAlarmLogVoList ------ 关联查询

xml 复制代码
<select id="selectNecAlarmLogVoList" parameterType="NecAlarmLog" resultMap="NecAlarmLogResultVo">
    select a.alarm_id, ..., t.tunnel_name
    from nec_alarm_log a
    left join nec_tunnel t on a.tunnel_id = t.tunnel_id
    <where>...</where>
    order by a.create_time desc
</select>

特点

  • 左连接 nec_tunnel 表获取 tunnel_name
  • 使用 NecAlarmLogResultVo 映射,包含 tunnelName 字段
  • order by create_time desc:按创建时间倒序排列(最新的在前)

5.3 selectNecAlarmLogByAlarmId ------ 按 ID 查询单条

xml 复制代码
<select id="selectNecAlarmLogByAlarmId" parameterType="Long" resultMap="NecAlarmLogResult">
    <include refid="selectNecAlarmLogVo"/>
    where a.alarm_id = #{alarmId}
</select>

⚠️ 有个小问题 :这里写的是 where a.alarm_id,但 <include> 引用的 SQL 片段中表名是 nec_alarm_log,没有指定别名 a,所以实际上应该写成 where alarm_id = #{alarmId},或者给表起别名。


6. <insert> ------ 插入语句

xml 复制代码
<insert id="insertNecAlarmLog" parameterType="NecAlarmLog" useGeneratedKeys="true" keyProperty="alarmId">
    insert into nec_alarm_log
    <trim prefix="(" suffix=")" suffixOverrides=",">
        <if test="parentId != null">parent_id,</if>
        <!-- ... -->
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
        <if test="parentId != null">#{parentId},</if>
        <!-- ... -->
    </trim>
</insert>

核心机制:动态插入

<trim> 标签的作用:

  • prefix="(":在最前面添加 (
  • suffix=")":在最后面添加 )
  • suffixOverrides=","如果最后一个字符是逗号,自动删除

举例 :如果只有 errCodeerrMsg 有值

sql 复制代码
insert into nec_alarm_log (err_code, err_msg) values (?, ?)

而不是生成错误的 (err_code, err_msg,)

useGeneratedKeys="true" keyProperty="alarmId" :插入成功后,将数据库自动生成的主键值回填到 Java 对象的 alarmId 属性中。


7. <update> ------ 更新语句

xml 复制代码
<update id="updateNecAlarmLog" parameterType="NecAlarmLog">
    update nec_alarm_log
    <trim prefix="SET" suffixOverrides=",">
        <if test="parentId != null">parent_id = #{parentId},</if>
        <!-- ... -->
    </trim>
    where alarm_id = #{alarmId}
</update>

<trim prefix="SET" suffixOverrides=","> 的机制:

  • 所有动态条件拼接到 SET 后面
  • 自动去掉最后一个多余的逗号

举例 :只更新 errStatus

sql 复制代码
update nec_alarm_log SET err_status = ? where alarm_id = ?

8. <delete> ------ 删除语句

8.1 单条删除

xml 复制代码
<delete id="deleteNecAlarmLogByAlarmId" parameterType="Long">
    delete from nec_alarm_log where alarm_id = #{alarmId}
</delete>

删除单条记录。

8.2 批量删除(重点)

xml 复制代码
<delete id="deleteNecAlarmLogByAlarmIds" parameterType="String">
    delete from nec_alarm_log where alarm_id in
    <foreach item="alarmId" collection="array" open="(" separator="," close=")">
        #{alarmId}
    </foreach>
</delete>

传入参数说明

  • Java 方法:deleteNecAlarmLogByAlarmIds(Long[] alarmIds)
  • 因为是数组,所以 collection="array"
  • 如果加了 @Param("ids"),则 collection="ids"

生成 SQL 示例 (传入 [1,2,3]):

sql 复制代码
delete from nec_alarm_log where alarm_id in (1,2,3)

⚠️ 注意 :这里 parameterType="String" 写错了,应该是 Long[]java.lang.Long[]


9. 动态 SQL 标签总结

标签 作用
<if> 条件判断,满足才拼接 SQL
<where> 智能生成 WHERE,自动处理多余 AND/OR
<trim> 灵活处理前缀、后缀和多余字符
<foreach> 遍历集合,用于 IN 查询或批量插入
<include> 引用 <sql> 定义的片段
<set> 智能生成 SET,自动处理多余逗号(等于 <trim prefix="SET" suffixOverrides=",">

10. 整体架构关系图

sql 复制代码
Java Mapper 接口 (DataAlarmLogMapper)
         ↓ 通过 namespace 绑定
XML Mapper (DataAlarmLogMapper.xml)
         ↓ 包含
    ┌────┼────┬────┬────┬────┐
    ↓    ↓    ↓    ↓    ↓    ↓
  resultMap sql select insert update delete
         ↓
    动态生成 SQL → 执行 → 映射结果 → 返回 Java 对象

11. 可以优化的点

问题 建议
selectNecAlarmLogByAlarmId 中用了 a.alarm_id 但表没有别名 改成 alarm_id 或给表起别名
deleteNecAlarmLogByAlarmIdsparameterType="String" 错误 改为 java.lang.Long[] 或省略
两个 resultMap 字段重复 可以用 <resultMap extends="NecAlarmLogResult"> 继承复用
sql 片段中表名硬编码 可以接受,但注意关联查询时要单独写

如果还有不明白的地方,或者想了解 MyBatis 的别名配置、分页插件、一级/二级缓存等进阶内容,可以继续问我!😊

是的! 你理解得完全正确。👍

让我详细拆解一下这个执行过程:


1. 引用关系

xml 复制代码
<select id="selectNecAlarmLogByAlarmId" ...>
    <include refid="selectNecAlarmLogVo"/>
    where a.alarm_id = #{alarmId}
</select>

<include refid="selectNecAlarmLogVo"/> 引用了上面定义的 <sql> 片段:

xml 复制代码
<sql id="selectNecAlarmLogVo">
    select alarm_id, parent_id, tenant_id, tower_id, tunnel_id, 
           err_code, err_msg, err_status, create_time 
    from nec_alarm_log
</sql>

2. 拼接后的最终 SQL

MyBatis 在执行时会先把 <include> 替换成 SQL 片段的内容,然后拼接后面的 where 条件,最终生成:

sql 复制代码
select alarm_id, parent_id, tenant_id, tower_id, tunnel_id, 
       err_code, err_msg, err_status, create_time 
from nec_alarm_log
where a.alarm_id = #{alarmId}

3. ⚠️ 但是!这里有个 BUG

注意看拼接后的 SQL:

sql 复制代码
from nec_alarm_log
where a.alarm_id = #{alarmId}

问题 :表名是 nec_alarm_log,没有起别名 a,但 where 条件中却用了 a.alarm_id

这会导致 SQL 执行报错:

sql 复制代码
Unknown column 'a.alarm_id' in 'where clause'

4. 正确的修复方式

方案一:去掉别名前缀

xml 复制代码
<select id="selectNecAlarmLogByAlarmId" parameterType="Long" resultMap="NecAlarmLogResult">
    <include refid="selectNecAlarmLogVo"/>
    where alarm_id = #{alarmId}
</select>

生成:

sql 复制代码
where alarm_id = #{alarmId}  ✅

方案二:给表起别名,并修改 SQL 片段

xml 复制代码
<sql id="selectNecAlarmLogVo">
    select a.alarm_id, a.parent_id, a.tenant_id, a.tower_id, a.tunnel_id, 
           a.err_code, a.err_msg, a.err_status, a.create_time 
    from nec_alarm_log a
</sql>

这样引用后就是:

sql 复制代码
from nec_alarm_log a
where a.alarm_id = #{alarmId}  ✅

5. 对比你文件中的其他查询

你的 selectNecAlarmLogVoList 就是用的方案二:

xml 复制代码
<select id="selectNecAlarmLogVoList" ...>
    select a.alarm_id, ..., t.tunnel_name
    from nec_alarm_log a
    left join nec_tunnel t on a.tunnel_id = t.tunnel_id
    <where>
        <if test="..."> and a.parent_id = #{parentId}</if>
        <!-- 都用 a. 前缀 -->
    </where>
</select>

所以建议把 selectNecAlarmLogVo 也改成带别名 a 的版本,保持统一,也避免 selectNecAlarmLogByAlarmId 报错。


总结 :你的理解是对的,<include> 就是代码复用的意思,把 SQL 片段"复制粘贴"到引用的位置。但当前这个查询因为别名问题存在 bug,需要修复。😊

相关推荐
这个DBA有点耶2 小时前
数据库迁移灰度验证:从读流量到双写再到全量切换的完整路径
数据库·mysql·架构
Wang's Blog2 小时前
PostgreSQL笔记16: 索引基础——概念、类型、创建与最佳实践
数据库·笔记·postgresql
用户3169353811832 小时前
"批量"删除-sql
数据库
瀚高PG实验室2 小时前
SQL优化案例:使用分区表优化SQL查询性能
linux·服务器·数据库·sql·microsoft·postgresql
SelectDB2 小时前
15 分钟搭建 PostgreSQL + Apache Iceberg + Apache Doris 的湖仓分析平台
数据库
SelectDB3 小时前
科大讯飞可观测性底座:从 ES/Loki 到 Apache Doris 的可观测存储分析升级实践
数据库
SelectDB3 小时前
Cisco WebEx 数据平台统一改造:基于 Apache Doris / SelectDB 替换 Trino、Pinot、Iceberg 及 Kyuubi
数据库
数据技术说3 小时前
MySQL 和 PostgreSQL 的 CDC 方案到底有什么区别?
数据库·架构
SelectDB3 小时前
灵犀科技统一数据服务平台:基于 Apache Doris / SelectDB 实现存储降本 60%、计算提效 10 倍
数据库