MyBatis中mapper.xml 的sql映射规则
一、SQL 映射文件核心元素
MyBatis 映射文件的顶级元素(按定义顺序):
cache:命名空间的缓存配置。cache-ref:引用其他命名空间的缓存。resultMap:自定义结果集映射。sql:可重用的 SQL 片段。insert、update、delete:数据操作语句。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 时,index 和 item 分别代表键和值。
示例代码:
// 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.关键注意事项
-
参数类型匹配
- 单参数集合需通过
@Param显式命名。 - 多参数需用
@Param避免混淆。
- 单参数集合需通过
-
安全与性能
- 始终使用
#{}占位符(防止 SQL 注入)。 - 批量操作时,注意数据库的 SQL 语句长度限制。
- 始终使用
-
动态 SQL 灵活性
- 结合
<if>标签实现条件遍历:<foreach collection="ids" item="id"><if test="id != null">#{id}</if> </foreach>
- 结合
8.总结
| 参数类型 | collection 值 | 示例场景 |
|---|---|---|
List | @Param 指定的名称 | IN 查询、批量插入 |
Set | @Param 指定的名称 | 去重后的 IN 查询 |
| 数组 | array | 原生数组参数的 IN 查询 |
Map | map.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 = ? -
防御措施:
- 白名单校验:限制动态值的范围。
// 只允许特定表名 if (!Arrays.asList("user", "employee").contains(tableName)) {throw new IllegalArgumentException("非法表名"); } - 转义特殊字符:过滤或转义输入中的特殊符号(如
',;)。
- 白名单校验:限制动态值的范围。
4. 总结
| 场景 | 占位符 | 示例 | 安全性 |
|---|---|---|---|
参数值(如 id) | #{} | WHERE id = #{id} | 安全(推荐) |
| 动态表名 | ${} | FROM ${tableName} | 需校验输入(风险) |
| 动态列名/排序字段 | ${} | ORDER BY ${column} | 需校验输入(风险) |
原则:
- 优先使用
#{},确保安全性。 - 仅在必要时使用
${},并严格校验输入值。
2. 返回类型
1. 单对象:resultType="User"
- 作用:将单条数据库记录映射到一个 Java 对象(如
User)。 - 规则:
- 数据库列名需与 Java 对象属性名一致(或通过别名匹配),否则需使用
resultMap。 - 若查询结果为多条记录,会抛出异常(
TooManyResultsException)。
- 数据库列名需与 Java 对象属性名一致(或通过别名匹配),否则需使用
- 示例:
<select id="selectUserById" resultType="User">SELECT * FROM user WHERE id = #{id} </select>- 返回类型:
User对象。 - 若未查询到数据,返回
null。
- 返回类型:
2. 集合:resultType="User"
- 作用:将多条数据库记录映射为
List<User>。 - 规则:
- MyBatis 自动将多行结果封装为
List,无需额外配置。 - 若查询结果为空,返回空列表(非
null)。
- MyBatis 自动将多行结果封装为
- 示例:
<select id="selectAllUsers" resultType="User">SELECT * FROM user </select>- 返回类型:
List<User>。 - 即使结果为空,返回
Collections.emptyList()。
- 返回类型:
3. Map 类型
3.1 直接返回 Map(resultType="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 对象。
- 键(K):由
- 规则:
- 需在 DAO 接口方法上添加
@MapKey("属性名")。 - 查询结果中指定的属性值必须唯一,否则会覆盖或抛出异常。
- 需在 DAO 接口方法上添加
- 示例:
@MapKey("id") Map<Integer, User> selectAllUsersAsMap();<select id="selectAllUsersAsMap" resultType="User">SELECT * FROM user </select>- 返回类型:
Map<Integer, User>,键为User的id,值为对应的User对象。
- 返回类型:
4.关键区别与注意事项
| 类型 | resultType 值 | 返回值类型 | 适用场景 |
|---|---|---|---|
| 单对象 | User | User | 查询单条记录 |
| 集合 | User | List<User> | 查询多条记录 |
| 单条记录的 Map | map | Map<String, Object> | 需要灵活访问列名/值的场景 |
| 多条记录的 Map | User + @MapKey | Map<K, User> | 以特定属性为键,对象为值的映射 |
5.常见问题
-
字段名与属性名不一致
- 使用
resultMap或 SQL 别名(AS)解决:<select id="selectUser" resultType="User">SELECT user_id AS id, user_name AS name FROM user </select>
- 使用
-
集合返回类型为
null- MyBatis 默认返回空集合(
Collections.emptyList()),而非null。
- MyBatis 默认返回空集合(
-
@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) |
相关文章:
MyBatis中mapper.xml 的sql映射规则
一、SQL 映射文件核心元素 MyBatis 映射文件的顶级元素(按定义顺序): cache:命名空间的缓存配置。cache-ref:引用其他命名空间的缓存。resultMap:自定义结果集映射。sql:可重用的 SQL 片段。i…...
深入解析 Java 类加载机制及双亲委派模型
🔍 Java的类加载机制是确保应用程序正确运行的基础,特别是双亲委派模型,它通过父类加载器逐层加载类,避免冲突和重复加载。但在某些特殊场景下,破坏双亲委派模型会带来意想不到的效果。本文将深入解析Java类加载机制、…...
糖尿病大模型预测及临床应用研究智能管理系统技术文档
目录 1. 数据工程规范1.1 多源数据集成1.2 特征工程架构 2. 核心模型架构2.1 分层预测网络2.2 动态血糖预测模块 3. 实时决策系统3.1 术中预警协议3.2 麻醉方案优化器 4. 验证体系实现4.1 数字孪生验证平台4.2 临床验证流程 5. 系统部署方案5.1 边缘计算架构5.2 性能指标 6. 安…...
MySQL数据库精研之旅第四期:解锁库操作高阶技能
专栏:MySQL数据库成长记 个人主页:手握风云 目录 一、查看所有表 1.1. 语法 二、创建表 2.1. 语法 2.2. 示例 2.3. 表在磁盘上对应的⽂件 三、查看表结构 3.1. 语法 3.2. 示例 四、修改表 4.1. 语法 4.2. 示例 五、删除表 5.1. 语法 5.2.…...
【DevOps】DevOps and CI/CD Pipelines
DevOps 是一种将开发与运维实践相结合的模式,旨在缩短软件开发周期并交付高质量软件。 DevOps 是什么? 开发团队与运维团队之间的协作 • 持续集成与持续交付(CI/CD) • 流程自动化 • 基础设施即代码(IaC)…...
Oracle详解
Oracle 数据库是一款由 Oracle 公司开发和维护的关系数据库管理系统(RDBMS)。Oracle 数据库广泛应用于企业级应用中,尤其是在需要高可用性、高性能和安全性的场景。以下是对 Oracle 数据库的详细介绍,包括它的各个方面。 一、Ora…...
VS自定义静态库并在其他项目中使用
1、VS创建一个空项目或者静态库项目 2、右键项目 属性 修改生成文件类型 3、生成解决方案 4、复制.h文件和.lib文件作为静态库 5、创建一个新项目 测试使用新生成的静态库 在新项目UseStaticLib中加一个新文件夹lib,lib中放入上面的.h和.lib文件。 6、vs中右…...
5G AAU(Active Antenna Unit)详细介绍
5G AAU(Active Antenna Unit)详细介绍 1. 定义与架构 5G AAU(Active Antenna Unit,有源天线单元)是5G无线基站系统中的核心组件,它集成了射频(RF)和天线功能,是4G时代R…...
力扣32.最长有效括号(栈)
32. 最长有效括号 - 力扣(LeetCode) 代码区: #include<stack> #include<string> /*最长有效*/ class Solution { public:int longestValidParentheses(string s) {stack<int> st;int ans0;int ns.length();st.push(-1);fo…...
【计算机网络中的奈氏准则与香农定理】
文章目录 一、前言二、奈氏准则1. 概念2. 奈氏准则公式3. 奈氏准则的意义 三、香农定理1. 概念2. 香农定理公式3. 香农定理的意义 四、奈氏准则与香农定理的对比五、应用示例1. 奈氏准则示例2. 香农定理示例 六、总结 一、前言 在计算机网络中,数据的传输速率与信道…...
vue3 项目中预览 word(.docx)文档方法
vue3 项目中预览 word(.docx)文档方法 通过 vue-office/docx 插件预览 docx 文档通过 vue-office/excel 插件预览 excel 文档通过 vue-office/pdf 插件预览 pdf 文档 安装插件 npm install vue-office/docx vue-demi示例代码 <template><Vu…...
DHCP(Dynamic Host Configuration Protocol)原理深度解析
目录 一、DHCP 核心功能 二、DHCP 工作流程(四阶段) 三、关键技术机制 1. 中继代理(Relay Agent) 2. Option 82(中继信息选项) 3. 租期管理 4. 冲突检测 四、DHCP 与网络架构交互 1. MLAG 环境 2.…...
创建login.api.js步骤和方法
依次创建 login.api.js、home.api.js...... login.api.js、home.api.js 差不多 导入到 main.js main.js 项目中使用...
基于springboot二手交易平台(源码+lw+部署文档+讲解),源码可白嫖!
摘要 人类现已迈入二十一世纪,科学技术日新月异,经济、资讯等各方面都有了非常大的进步,尤其是资讯与网络技术的飞速发展,对政治、经济、军事、文化等各方面都有了极大的影响。 利用电脑网络的这些便利,发展一套二手交…...
帕金森患者的生活重塑:从 “嘴” 开启康复之旅
当提到帕金森病,许多人会联想到震颤、僵硬和行动迟缓等症状。这种神经系统退行性疾病,给患者的生活带来了巨大的挑战。然而,你可知道,帕金森患者恢复正常生活,可以从 “嘴” 开始管理? 帕金森病在全球影响着…...
相生、相克、乘侮、复杂病机及对应的脏腑功能联系
一、五行相生关系(母子关系) 五行生序脏腑关系生理表现举例木生火肝(木)滋养心(火)肝血充足则心血旺盛火生土心(火)温煦脾(土)心阳充足则脾胃运化功能正常土…...
鸿蒙OS 5 架构设计探秘:从分层设计到多端部署
文章目录 鸿蒙OS架构设计探秘:从分层设计到多端部署一、鸿蒙的分层架构设计二、模块化设计的精髓三、智慧分发设计:资源的动态调度四、一次开发,多端部署的实践总结与思考 鸿蒙OS架构设计探秘:从分层设计到多端部署 最近两年来&a…...
5. 实现一个中间件
原文地址: 实现一个中间件 更多内容请关注:php代码框架 理解中间件 中间件(Middleware) 是一种在请求被路由到控制器方法之前或响应返回客户端之前执行的代码。它通常用于处理通用任务,如身份验证、日志记录、CORS 处理等。 在…...
JVM 为什么不使用引用计数算法?——深入解析 GC 策略
在 Java 中,垃圾回收(Garbage Collection, GC)是一个至关重要的功能,它能够自动管理内存,回收不再使用的对象,从而防止内存泄漏。然而,在垃圾回收的实现上,JVM 并未采用引用计数算法…...
【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别
一、EventHub是什么? 移动应用开发的同学应该比较了解EventHub,类似于EventBus。标准的事件广播通知,订阅,取消订阅的处理。EventHub模块提供了事件中心,提供订阅、取消订阅、触发事件的能力。 类似的框架工具有很多…...
01-系统编程
一、程序和进程的区别: window系统: 1、程序存储在硬盘中,文件格式为.exe后缀,静态的 2、进程运行在内存中,动态的 Linux系统 1、程序存储在硬盘中,文件格式为.ELF(可执行的链接文件&#…...
Linux编译器gcc/g++使用完全指南:从编译原理到动静态链接
一、gcc/g基础认知 在Linux开发环境中,gcc和g是我们最常用的编译器工具: gcc:GNU C Compiler,专门用于编译C语言程序g:GNU C Compiler,用于编译C程序(也可编译C语言) 📌…...
UMI-OCR Docker 部署
额外补充 Docker 0.前置条件 部署前,请检查主机的CPU是否具有AVX指令集 lscpu | grep avx 输出如下即可继续部署 Flags: ... avx ... avx2 ... 1.下载dockerfile wget https://raw.githubusercontent.com/hiroi-sora/Umi-OCR_runtime_linux/main/Do…...
26考研|数学分析:定积分及应用
这一部分作为数学分析的灵魂,在数学分析的计算中,绝大部分的问题都可以转换成定积分的计算问题,所以在这部分的学习中,一定要注意提升计算能力,除此之外,由积分引出的相关积分不等式也是分析的重点和难点&a…...
React Hooks使用方法:useState,useRef,useEffect,useReducer,useContext用法实战案例
react hooks介绍,包括了state,ref,effect,reducer,context等常见hooks,也包括forwardRef和createContext用法,下面看代码吧,我用的是js写的。每个hook都做了个案例。 // 使用state来…...
线程池详解:在SpringBoot中的最佳实践
线程池详解:在SpringBoot中的最佳实践 引言 在Java并发编程中,线程池是一种非常重要的资源管理工具,它允许我们在应用程序中有效地管理和重用线程,从而提高性能并降低资源消耗。特别是在SpringBoot等企业级应用中,正…...
扩展卡尔曼滤波
1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统,扩展卡尔曼滤波 则扩展到了非线性系统,核心原理就是将非线性系统线性化,主要用的的知识点是 泰勒展开(我另外一篇文章的链接),如下是泰勒展开的公式…...
AI作为学术评审专家有哪些优缺点?
大家好这里是AIWritePaper官方账号,官网👉AIWritePaper论文完成初稿之后,一般情况下,宝子们还需要找专家给我们提出评审意见。找专家评审其实并不容易,即使对老师来说,找人评审论文也是一件苦活。我们这个时…...
微信小程序登录和获取手机号
目录 准备工作 实现流程 实现代码 公共部分 通过code获取openid等信息 解密手机号 扩展 不借助工具类实现解密 借助工具类获取access_token 准备工作 需要小程序账号(可以去微信公众平台创建一个测试号或者正式号) appid:小程序id …...
4.Matplotlib:基础绘图
一 直方图 1.如何构建直方图 将值的范围分段,将整个值的范围分成一系列间隔,然后计算每个间隔中有多少值。 2.直方图的适用场景 一般用横轴表示数据类型,纵轴表示分布情况。 直方图可以用于识别数据的分布模式和异常值,以及观察数…...
