MyBatis中mapper.xml 的sql映射规则

news2025/3/30 15:20:47

一、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.coloradmin.cn/o/2322534.html

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

相关文章

深入解析 Java 类加载机制及双亲委派模型

&#x1f50d; Java的类加载机制是确保应用程序正确运行的基础&#xff0c;特别是双亲委派模型&#xff0c;它通过父类加载器逐层加载类&#xff0c;避免冲突和重复加载。但在某些特殊场景下&#xff0c;破坏双亲委派模型会带来意想不到的效果。本文将深入解析Java类加载机制、…

MySQL数据库精研之旅第四期:解锁库操作高阶技能

专栏&#xff1a;MySQL数据库成长记 个人主页&#xff1a;手握风云 目录 一、查看所有表 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 是一种将开发与运维实践相结合的模式&#xff0c;旨在缩短软件开发周期并交付高质量软件。 DevOps 是什么&#xff1f; 开发团队与运维团队之间的协作 • 持续集成与持续交付&#xff08;CI/CD&#xff09; • 流程自动化 • 基础设施即代码&#xff08;IaC&#xff09;…

VS自定义静态库并在其他项目中使用

1、VS创建一个空项目或者静态库项目 2、右键项目 属性 修改生成文件类型 3、生成解决方案 4、复制.h文件和.lib文件作为静态库 5、创建一个新项目 测试使用新生成的静态库 在新项目UseStaticLib中加一个新文件夹lib&#xff0c;lib中放入上面的.h和.lib文件。 6、vs中右…

力扣32.最长有效括号(栈)

32. 最长有效括号 - 力扣&#xff08;LeetCode&#xff09; 代码区&#xff1a; #include<stack> #include<string> /*最长有效*/ class Solution { public:int longestValidParentheses(string s) {stack<int> st;int ans0;int ns.length();st.push(-1);fo…

vue3 项目中预览 word(.docx)文档方法

vue3 项目中预览 word&#xff08;.docx&#xff09;文档方法 通过 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 工作流程&#xff08;四阶段&#xff09; 三、关键技术机制 1. 中继代理&#xff08;Relay Agent&#xff09; 2. Option 82&#xff08;中继信息选项&#xff09; 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+部署文档+讲解),源码可白嫖!

摘要 人类现已迈入二十一世纪&#xff0c;科学技术日新月异&#xff0c;经济、资讯等各方面都有了非常大的进步&#xff0c;尤其是资讯与网络技术的飞速发展&#xff0c;对政治、经济、军事、文化等各方面都有了极大的影响。 利用电脑网络的这些便利&#xff0c;发展一套二手交…

帕金森患者的生活重塑:从 “嘴” 开启康复之旅

当提到帕金森病&#xff0c;许多人会联想到震颤、僵硬和行动迟缓等症状。这种神经系统退行性疾病&#xff0c;给患者的生活带来了巨大的挑战。然而&#xff0c;你可知道&#xff0c;帕金森患者恢复正常生活&#xff0c;可以从 “嘴” 开始管理&#xff1f; 帕金森病在全球影响着…

JVM 为什么不使用引用计数算法?——深入解析 GC 策略

在 Java 中&#xff0c;垃圾回收&#xff08;Garbage Collection, GC&#xff09;是一个至关重要的功能&#xff0c;它能够自动管理内存&#xff0c;回收不再使用的对象&#xff0c;从而防止内存泄漏。然而&#xff0c;在垃圾回收的实现上&#xff0c;JVM 并未采用引用计数算法…

【HarmonyOS NEXT】EventHub和Emitter的使用场景与区别

一、EventHub是什么&#xff1f; 移动应用开发的同学应该比较了解EventHub&#xff0c;类似于EventBus。标准的事件广播通知&#xff0c;订阅&#xff0c;取消订阅的处理。EventHub模块提供了事件中心&#xff0c;提供订阅、取消订阅、触发事件的能力。 类似的框架工具有很多…

01-系统编程

一、程序和进程的区别&#xff1a; window系统&#xff1a; 1、程序存储在硬盘中&#xff0c;文件格式为.exe后缀&#xff0c;静态的 2、进程运行在内存中&#xff0c;动态的 Linux系统 1、程序存储在硬盘中&#xff0c;文件格式为.ELF&#xff08;可执行的链接文件&#…

Linux编译器gcc/g++使用完全指南:从编译原理到动静态链接

一、gcc/g基础认知 在Linux开发环境中&#xff0c;gcc和g是我们最常用的编译器工具&#xff1a; gcc&#xff1a;GNU C Compiler&#xff0c;专门用于编译C语言程序g&#xff1a;GNU C Compiler&#xff0c;用于编译C程序&#xff08;也可编译C语言&#xff09; &#x1f4cc…

26考研|数学分析:定积分及应用

这一部分作为数学分析的灵魂&#xff0c;在数学分析的计算中&#xff0c;绝大部分的问题都可以转换成定积分的计算问题&#xff0c;所以在这部分的学习中&#xff0c;一定要注意提升计算能力&#xff0c;除此之外&#xff0c;由积分引出的相关积分不等式也是分析的重点和难点&a…

扩展卡尔曼滤波

1.非线性系统的线性化 标准卡尔曼滤波 适用于线性化系统&#xff0c;扩展卡尔曼滤波 则扩展到了非线性系统&#xff0c;核心原理就是将非线性系统线性化&#xff0c;主要用的的知识点是 泰勒展开&#xff08;我另外一篇文章的链接&#xff09;&#xff0c;如下是泰勒展开的公式…

4.Matplotlib:基础绘图

一 直方图 1.如何构建直方图 将值的范围分段&#xff0c;将整个值的范围分成一系列间隔&#xff0c;然后计算每个间隔中有多少值。 2.直方图的适用场景 一般用横轴表示数据类型&#xff0c;纵轴表示分布情况。 直方图可以用于识别数据的分布模式和异常值&#xff0c;以及观察数…

VSCode 市场发现恶意扩展正在传播勒索软件!

在VSCode 市场中发现了两个隐藏着勒索软件的恶意扩展。其中一个于去年 10 月出现在微软商店&#xff0c;但很长时间没有引起注意。 这些是扩展ahban.shiba 和 ahban.cychelloworld&#xff0c;目前已从商店中删除。 此外&#xff0c;ahban.cychelloworld 扩展于 2024 年 10 月…

工作流引擎Flowable介绍及SpringBoot整合使用实例

Flowable简介 Flowable 是一个轻量级的业务流程管理&#xff08;BPM&#xff09;和工作流引擎&#xff0c;基于 Activiti 项目发展而来&#xff0c;专注于提供高性能、可扩展的工作流解决方案。它主要用于企业级应用中的流程自动化、任务管理和审批流等场景。 Flowable 的核心…

K8s证书--运维之最佳选择(K8s Certificate - the best Choice for Operation and Maintenance)

K8s证书--运维之最佳选择 No -Number- 01 一个月速通CKA 为了速通CKA&#xff0c;主要办了两件事情 1. 在官方的Killercoda上&#xff0c;练习CKA的题目。把命令敲熟悉。 // https://killercoda.com/killer-shell-ckad 2. 使用K3s在多台虚拟机上快速搭建了K8s集群&…