MyBatis中mapper.xml 的sql映射规则

一、SQL 映射文件核心元素

MyBatis 映射文件的顶级元素(按定义顺序):

  1. cache:命名空间的缓存配置。
  2. cache-ref:引用其他命名空间的缓存。
  3. resultMap:自定义结果集映射。
  4. sql:可重用的 SQL 片段。
  5. insertupdatedelete:数据操作语句。
  6. select:查询语句。

二、参数传递与处理

1. 单参数

-基础类型/字符串

<select id="selectUser" resultType="User" parameterType="int">SELECT * FROM user WHERE id = #{id}
</select>

-POJO 对象

<insert id="insertUser" parameterType="User">INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>

2. 多参数

- 默认 param1, param2(不推荐):

<select id="selectUser" resultType="User">SELECT * FROM user WHERE id = #{param1} AND name = #{param2}
</select>

- @Param 注解(推荐):

User selectUser(@Param("id") int id, @Param("name") String name);
<select id="selectUser" resultType="User">SELECT * FROM user WHERE id = #{id} AND name = #{name}
</select>

3. 复杂参数

- Map 类型

<select id="selectUserByMap" resultType="User" parameterType="map">SELECT * FROM user WHERE name = #{name} AND age = #{age}
</select>

- 混合参数(POJO + @Param):

List<User> selectUsers(@Param("role") String role, User user);
<select id="selectUsers" resultType="User">SELECT * FROM user WHERE role = #{role} AND age = #{user.age}
</select>

三、主键生成与回填

1. 自增主键(如 MySQL)

<insert id="insertUser" useGeneratedKeys="true" keyProperty="id">INSERT INTO user (name, email) VALUES (#{name}, #{email})
</insert>
  • useGeneratedKeys="true":启用 JDBC 的自动生成主键。
  • keyProperty="id":将生成的主键赋值给对象的 id 属性。

2. 非自增主键(如 Oracle)

<insert id="insertUser"><selectKey keyProperty="id" resultType="int" order="BEFORE">SELECT MAX(id) + 1 FROM user</selectKey>INSERT INTO user (id, name) VALUES (#{id}, #{name})
</insert>
  • order="BEFORE":先执行 selectKey 生成主键,再插入数据。

四、结果映射(resultMap

1. 基础映射

<resultMap id="userResultMap" type="User"><id property="id" column="user_id" /><result property="name" column="user_name" />
</resultMap>

2. 关联对象(一对一)

<resultMap id="userWithRoleMap" type="User"><association property="role" javaType="Role"><id property="roleId" column="role_id" /><result property="roleName" column="role_name" /></association>
</resultMap>

3. 集合映射(一对多)

<resultMap id="userWithOrdersMap" type="User"><collection property="orders" ofType="Order"><id property="orderId" column="order_id" /><result property="orderNo" column="order_no" /></collection>
</resultMap>

五、动态 SQL

1. 条件查询(<if> + <where>

<select id="selectUser" resultType="User">SELECT * FROM user<where><if test="name != null">AND name = #{name}</if><if test="age != null">AND age = #{age}</if></where>
</select>

2. 循环遍历(<foreach>

<select id="selectUsersByIds" resultType="User">SELECT * FROM user WHERE id IN<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

1. 参数是 List

当方法参数直接传递一个 List 时,MyBatis 默认将其封装为 Map,键为 list

示例代码:
// DAO 方法
List<User> selectUsersByIds(@Param("ids") List<Integer> ids);
<!-- XML 映射 -->
<select id="selectUsersByIds" resultType="User">SELECT * FROM user WHERE id IN<foreach collection="ids" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>
关键点:
  • collection="ids":对应 @Param("ids") 注解的参数名。
  • item="id":遍历的每个元素变量名。
  • open="("close=")":包裹生成的 SQL 片段。
  • separator=",":元素之间的分隔符。

2. 参数是 Set

Set 的处理方式与 List 类似,MyBatis 会自动将其转换为 List 处理。

示例代码:
// DAO 方法
List<User> selectUsersByNames(@Param("names") Set<String> names);
<!-- XML 映射 -->
<select id="selectUsersByNames" resultType="User">SELECT * FROM user WHERE name IN<foreach collection="names" item="name" open="(" separator="," close=")">#{name}</foreach>
</select>

3. 参数是数组

当直接传递数组时,collection 属性需指定为 array

示例代码:
// DAO 方法
List<User> selectUsersByIds(int[] ids);
<!-- XML 映射 -->
<select id="selectUsersByIds" resultType="User">SELECT * FROM user WHERE id IN<foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
</select>

4. 参数是 Map 中的集合

如果参数是 Map,需通过 @Param 指定键名。

示例代码:
// DAO 方法
List<User> selectUsers(@Param("data") Map<String, Object> data);
<!-- XML 映射 -->
<select id="selectUsers" resultType="User">SELECT * FROM user WHERE id IN<foreach collection="data.ids" item="id" open="(" separator="," close=")">#{id}</foreach>AND name IN<foreach collection="data.names" item="name" open="(" separator="," close=")">#{name}</foreach>
</select>

5. 批量插入示例

使用 <foreach> 实现批量插入:

// DAO 方法
void batchInsertUsers(@Param("users") List<User> users);
<!-- XML 映射 -->
<insert id="batchInsertUsers">INSERT INTO user (name, email) VALUES<foreach collection="users" item="user" separator=",">(#{user.name}, #{user.email})</foreach>
</insert>

6. 遍历 Map 类型

当集合元素是 Map 时,indexitem 分别代表键和值。

示例代码:
// DAO 方法
void insertUserRoles(@Param("roles") Map<Integer, String> roles);
<!-- XML 映射 -->
<insert id="insertUserRoles">INSERT INTO role (user_id, role_name) VALUES<foreach collection="roles" index="userId" item="roleName" separator=",">(#{userId}, #{roleName})</foreach>
</insert>

7.关键注意事项

  1. 参数类型匹配

    • 单参数集合需通过 @Param 显式命名。
    • 多参数需用 @Param 避免混淆。
  2. 安全与性能

    • 始终使用 #{} 占位符(防止 SQL 注入)。
    • 批量操作时,注意数据库的 SQL 语句长度限制。
  3. 动态 SQL 灵活性

    • 结合 <if> 标签实现条件遍历:
      <foreach collection="ids" item="id"><if test="id != null">#{id}</if>
      </foreach>
      

8.总结

参数类型collection示例场景
List@Param 指定的名称IN 查询、批量插入
Set@Param 指定的名称去重后的 IN 查询
数组array原生数组参数的 IN 查询
Mapmap.key@Param复杂参数组合的动态查询

通过合理使用 <foreach>,可以高效处理集合类参数,简化批量操作和动态 SQL 的编写。

3. 分支选择(<choose>

<select id="selectUserByChoose" resultType="User">SELECT * FROM user<where><choose><when test="id != null">id = #{id}</when><otherwise>status = 1</otherwise></choose></where>
</select>

六、高级特性

1. 分步查询与延迟加载

  • 分步查询
    <resultMap id="catMap" type="Cat"><association property="owner" select="selectOwnerById" column="owner_id" />
    </resultMap>
    
  • 延迟加载配置
    <settings><setting name="lazyLoadingEnabled" value="true" /><setting name="aggressiveLazyLoading" value="false" />
    </settings>
    

2. SQL 片段复用(<sql>

<sql id="userColumns">id, name, email</sql>
<select id="selectUser" resultType="User">SELECT <include refid="userColumns" /> FROM user
</select>

七、特殊处理

1. #{}${}

以下是 #{}${} 的核心区别及使用场景总结,结合动态表名/字段的示例说明:


1. #{}${} 的核心区别

特性#{}${}
原理预编译(PreparedStatement)字符串直接拼接(SQL 注入风险)
安全性防 SQL 注入(推荐)不安全(需严格校验输入)
适用场景参数值(如 WHERE 条件)动态表名、列名、排序字段等
示例WHERE id = #{id}ORDER BY ${columnName}

2. 动态表名与字段的示例

场景 1:动态表名(如多租户系统)
<!-- 根据传入的表名查询数据 -->
<select id="selectByDynamicTable" resultType="User">SELECT * FROM ${tableName} WHERE id = #{id}
</select>
  • 调用方式
    List<User> users = userDao.selectByDynamicTable("user_2023", 1001);
    
  • 生成 SQL
    SELECT * FROM user_2023 WHERE id = ?
    
场景 2:动态排序字段
<!-- 根据传入的排序字段动态排序 -->
<select id="selectUsersOrderBy" resultType="User">SELECT * FROM user ORDER BY ${orderByColumn} ${sortDirection}
</select>
  • 调用方式
    List<User> users = userDao.selectUsersOrderBy("age", "DESC");
    
  • 生成 SQL
    SELECT * FROM user ORDER BY age DESC
    
场景 3:动态列名(如选择特定字段)
<!-- 选择动态列 -->
<select id="selectDynamicColumns" resultType="map">SELECT ${columns} FROM user WHERE id = #{id}
</select>
  • 调用方式
    Map<String, Object> result = userDao.selectDynamicColumns("name, email", 1001);
    
  • 生成 SQL
    SELECT name, email FROM user WHERE id = ?
    

3. 安全注意事项

  • 风险场景:如果用户输入未经校验,直接使用 ${} 可能导致 SQL 注入。

    // 危险示例:用户输入恶意表名
    String tableName = "user; DROP TABLE user; --";
    userDao.selectByDynamicTable(tableName, 1001); 
    

    生成 SQL

    SELECT * FROM user; DROP TABLE user; -- WHERE id = ?
    
  • 防御措施

    1. 白名单校验:限制动态值的范围。
      // 只允许特定表名
      if (!Arrays.asList("user", "employee").contains(tableName)) {throw new IllegalArgumentException("非法表名");
      }
      
    2. 转义特殊字符:过滤或转义输入中的特殊符号(如 ', ;)。

4. 总结

场景占位符示例安全性
参数值(如 id#{}WHERE id = #{id}安全(推荐)
动态表名${}FROM ${tableName}需校验输入(风险)
动态列名/排序字段${}ORDER BY ${column}需校验输入(风险)

原则

  • 优先使用 #{},确保安全性。
  • 仅在必要时使用 ${},并严格校验输入值。

2. 返回类型

1. 单对象:resultType="User"

  • 作用:将单条数据库记录映射到一个 Java 对象(如 User)。
  • 规则
    • 数据库列名需与 Java 对象属性名一致(或通过别名匹配),否则需使用 resultMap
    • 若查询结果为多条记录,会抛出异常(TooManyResultsException)。
  • 示例
    <select id="selectUserById" resultType="User">SELECT * FROM user WHERE id = #{id}
    </select>
    
    • 返回类型:User 对象。
    • 若未查询到数据,返回 null

2. 集合:resultType="User"

  • 作用:将多条数据库记录映射为 List<User>
  • 规则
    • MyBatis 自动将多行结果封装为 List,无需额外配置。
    • 若查询结果为空,返回空列表(非 null)。
  • 示例
    <select id="selectAllUsers" resultType="User">SELECT * FROM user
    </select>
    
    • 返回类型:List<User>
    • 即使结果为空,返回 Collections.emptyList()

3. Map 类型

3.1 直接返回 MapresultType="map"
  • 作用:将单条记录映射为 Map<String, Object>,键为列名,值为对应数据。
  • 示例
    <select id="selectUserAsMap" resultType="map">SELECT id, name FROM user WHERE id = #{id}
    </select>
    
    • 返回类型:Map<String, Object>,如 {"id": 1, "name": "Alice"}
3.2 使用 @MapKey 注解返回 Map<K, V>
  • 作用:将多条记录映射为 Map<K, V>,其中:
    • 键(K):由 @MapKey 指定的属性值(如 id)。
    • 值(V):对应的 Java 对象。
  • 规则
    • 需在 DAO 接口方法上添加 @MapKey("属性名")
    • 查询结果中指定的属性值必须唯一,否则会覆盖或抛出异常。
  • 示例
    @MapKey("id")
    Map<Integer, User> selectAllUsersAsMap();
    
    <select id="selectAllUsersAsMap" resultType="User">SELECT * FROM user
    </select>
    
    • 返回类型:Map<Integer, User>,键为 Userid,值为对应的 User 对象。

4.关键区别与注意事项

类型resultType返回值类型适用场景
单对象UserUser查询单条记录
集合UserList<User>查询多条记录
单条记录的 MapmapMap<String, Object>需要灵活访问列名/值的场景
多条记录的 MapUser + @MapKeyMap<K, User>以特定属性为键,对象为值的映射

5.常见问题

  1. 字段名与属性名不一致

    • 使用 resultMap 或 SQL 别名(AS)解决:
      <select id="selectUser" resultType="User">SELECT user_id AS id, user_name AS name FROM user
      </select>
      
  2. 集合返回类型为 null

    • MyBatis 默认返回空集合(Collections.emptyList()),而非 null
  3. @MapKey 的唯一性

    • 若指定的键属性(如 id)存在重复值,MyBatis 会保留最后一个匹配的对象,可能导致数据丢失。

6.总结

  • 单对象:直接使用 resultType="User"
  • 集合:同样使用 resultType="User",MyBatis 自动封装为 List
  • Map
    • 单条记录:resultType="map"
    • 多条记录:结合 @MapKey 注解,指定键属性。

合理选择 resultType 可简化结果映射,提升开发效率。


八、总结

场景解决方案示例
自增主键useGeneratedKeys="true" + keyProperty="id"插入后自动填充 id
多参数查询@Param 注解#{id} + #{name}
字段名与属性名映射resultMap<result property="userName" column="user_name" />
动态条件查询<if> + <where>按条件拼接 WHERE 子句
批量操作<foreach>IN (1, 2, 3)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】计算机网络协议、接口与服务全面解析——结合生活化案例与图文详解

协议、接口与服务 导读一、协议1.1 定义1.2 组成 二、接口三、服务3.1 定义3.2 服务与协议的区别3.3 分类3.3.1 面向连接服务于无连接服务3.3.2 可靠服务和不可靠服务3.3.3 有应答服务和无应答服务 结语 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff01;&#xff01;…

Ubuntu服务器中Swapper如何与虚拟内存配合

在Ubuntu服务器中&#xff0c;Swapper和虚拟内存是操作系统中重要的概念&#xff0c;它们共同协作以提高系统的内存管理效率。当物理内存不足时&#xff0c;Swapper会帮助系统将不活跃的数据从内存转移到磁盘上的交换空间(Swap)&#xff0c;以释放内存给需要更多资源的进程。下…

SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景

以下是 SQL Server 中常见的数据类型及其详细解释、内存占用和适用场景&#xff1a; 数据类型类别数据类型解释内存占用适用场景整数类型bigint用于存储范围较大的整数&#xff0c;范围是 -2^63 (-9,223,372,036,854,775,808) 到 2^63-1 (9,223,372,036,854,775,807)8 字节需要…

vue数字公式篇 Tinymce结合使用(二)

继上一篇的数字公式 &#xff0c; 这次的功能是将公式能插入编辑器以及修改 1、Tinymce 自定义 LateX 按钮&#xff0c;打开公式编辑器窗口 LateX.vue window.tinymce.init({...//基础配置这里我就不写了setup(ed) {//自定义 LateX 按钮ed.ui.registry.addButton(LateX, {text:…

python数据增强和转换

数据增强和转换 固定转换随机转换概率控制的转换 固定转换 边缘补充像素(Pad)尺寸变换(Resize)中心截取(CenterCrop)顶角及中心截取(FiveCrop)尺灰度变换(GrayScale) 概率控制的转换 随机垂直翻转(RandomVerticalFlip)随机应用(RandomApply) # -*- coding: utf-8 -*- fro…

Ubuntu下UEFI安全启动安装Nvdia驱动

简介 众所周知&#xff0c;Ubuntu默认使用Nouveau开源驱动&#xff0c;其性能受限&#xff0c;因此我们需要安装Nvidia专用驱动。 安装专用驱动的一般方法非常简单&#xff0c;只需要sudo ubuntu-drivers devices && sudo ubuntu-drivers autoinstall即可&#xff0c…

05_循环结构三目运算符

目录 一、双重for循环 练习 二、break关键字 三、continue 关键字 练习 四、三元运算 / 三目运算 一、双重for循环 外层循环 循环一次&#xff0c;&#xff0c;&#xff0c;内层循环 循环一圈&#xff01;&#xff01;&#xff01; 循环里嵌套循环&#xff1a; for(var…

数据结构初阶-二叉树链式

目录 1.概念与结构 2.二叉数链式的实现 2.1遍历规则 2.2申请内存空间 2.3手动构建一棵二叉树 2.4二叉树结点的个数 2.5二叉树叶子结点的个数 2.6二叉树第K层结点个数 2.7二叉树的高度 2.8二叉树中查找值为x的结点 2.9二叉树的销毁 3.层序遍历 3.1概念 3.2层序遍历…

鸿蒙HarmonyOS NEXT之无感监听

鸿蒙中存在一些无感监听&#xff0c;这些监听经过系统API封装使用很简单&#xff0c;但是对实际业务开发中有很重要&#xff0c;例如埋点业务、数据统计、行为上报、切面拦截等。 Navigation的页面切换 在鸿蒙中Navigation被用来作为路由栈进行页面跳转&#xff0c;如果你想知…

批量处理word里面表格的空白行

1&#xff0c;随便打开一个word文档。 2&#xff0c;按下Alt F11 VBA编辑器,在左侧的「工程资源管理器」窗口中找到Normal 项目,右键选择插入->模块。 弹出一下弹窗 3&#xff0c;输入一下代码 代码&#xff1a; Sub RemoveEmptyTableRows()Dim tbl As TableDim row As R…

3ds Max 2026 新功能全面解析

一、视口性能与交互体验升级 1. Hydra 2.0 视口渲染引擎 3ds Max 2026 引入了 Hydra 2.0&#xff0c;大幅优化了视口渲染性能&#xff0c;尤其是在处理复杂场景和高质量实时预览时&#xff0c;流畅度提升显著。 支持USD&#xff08;通用场景描述&#xff09;格式&#xff0c…

JVM垃圾回收笔记02-垃圾回收器

文章目录 前言1.串行(Serial 收集器/Serial Old 收集器)Serial 收集器Serial Old 收集器相关参数-XX:UseSerialGC 2.吞吐量优先(Parallel Scavenge 收集器/Parallel Old 收集器)Parallel Scavenge 收集器Parallel Old 收集器相关参数-XX:UseParallelGC ~ -XX:UseParallelOldGC-…

图解AUTOSAR_SWS_UDPNetworkManagement

AUTOSAR UDP 网络管理 (UdpNm) 技术详解 基于 AUTOSAR 规范的 UDP 网络管理模块可视化指南 目录 AUTOSAR UDP 网络管理 (UdpNm) 技术详解 目录1. 概述2. UdpNm 状态机 2.1 状态机概述2.2 主要状态说明2.3 状态转换机制2.4 并行状态3. UdpNm 架构设计 3.1 架构概述3.2 接口设计3…

android 图形开发的技能学习路线

需要以下几个方面的知识&#xff1a; OpenGL ES的基础和高级应用图形渲染管线的工作原理3D数学&#xff08;矩阵、向量、四元数&#xff09;着色器编程&#xff08;GLSL&#xff09;libGDX框架的使用和定制性能优化和内存管理跨平台渲染技术 接下来&#xff0c;考虑如何结构化…

使用AI一步一步实现若依(26)

功能26&#xff1a;新增一个新员工培训页面 功能25&#xff1a;角色管理 功能24&#xff1a;菜单管理 功能23&#xff1a;从后端获取路由/菜单数据 功能22&#xff1a;用户管理 功能21&#xff1a;使用axios发送请求 功能20&#xff1a;使用分页插件 功能19&#xff1a;集成My…

vue响应式原理剖析

一、什么是响应式? 我们先来看一下响应式意味着什么?我们来看一段代码: m有一个初始化的值,有一段代码使用了这个值; 那么在m有一个新的值时,这段代码可以自动重新执行; let m = 20 console.log(m) console.log(m * 2)m = 40上面的这样一种可以自动响应数据变量的代码机…

无人机航电系统电池技术解析!

1. 常用电池类型 锂聚合物电池&#xff08;LiPo&#xff09; 特点&#xff1a;高能量密度、轻量化、放电效率高&#xff0c;是目前主流选择。 缺点&#xff1a;对过充/过放敏感&#xff0c;需严格管理&#xff0c;存在轻微膨胀或起火风险。 锂离子电池&#xff08;Li-ion…

ubuntu下终端打不开的排查思路和解决方法

问题现象描述&#xff1a;ubuntu开机后系统桌面显示正常&#xff0c;其他图形化的app也都能打开无异常&#xff0c;唯独只有terminal终端打不开&#xff0c;无论是鼠标点击终端软件&#xff0c;还是ctrlaltt&#xff0c;还是altF2后输入gnome-terminal后按回车&#xff0c;这三…

Maven入门

1、简介 Apache Maven是一个项目管理及自动构建工具&#xff0c;由Apache软件基金会所提供。基于项目对象模型&#xff08;缩写&#xff1a;POM&#xff09;概念&#xff0c;Maven利用一个中央信息片断能管理一个项目的构建、报告和文档等步骤。 2、作用 1&#xff09;依赖导…

Rk3588,Opencv读取Gmsl相机,Rga yuv422转换rgb (降低CPU使用率)

RK3588, 使用OpenCv 读取 gmsl 相机,获得yuv422格式图像, 使用 rga 转换 rgb 图像。减少cpu占用率. 查看相机信息 v4l2-ctl --all -d /dev/cam0 , 查看自己相机分辨率,输出格式等信息,对应修改后续代码测试… Driver Info:Driver name : rkcifCard type : rkc…