好的,我来给你系统讲解这个 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;如果满足,会自动去掉第一个多余的AND或OR
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=",":如果最后一个字符是逗号,自动删除
举例 :如果只有 errCode 和 errMsg 有值
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 或给表起别名 |
deleteNecAlarmLogByAlarmIds 的 parameterType="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,需要修复。😊