SSM从入门到实战:2.5 SQL映射文件与动态SQL

👋 大家好,我是 阿问学长!专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高教辅资料推荐等,欢迎关注交流!🚀

12-SQL映射文件与动态SQL

📖 本文概述

本文是SSM框架系列MyBatis进阶篇的第二篇,将深入探讨SQL映射文件的编写技巧和动态SQL的使用方法。通过丰富的示例和最佳实践,帮助读者掌握MyBatis中最核心的功能之一。

🎯 学习目标

  • 深入理解SQL映射文件的结构和元素
  • 掌握结果映射(ResultMap)的高级用法
  • 学会编写各种类型的动态SQL
  • 了解SQL片段的复用技巧
  • 掌握复杂查询和关联映射的实现

1. SQL映射文件概述

1.1 映射文件的基本结构

SQL映射文件是MyBatis的核心,它定义了SQL语句和Java对象之间的映射关系。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.mapper.UserMapper"><!-- 结果映射 --><resultMap id="BaseResultMap" type="User"><!-- 主键映射 --><id column="id" property="id" jdbcType="BIGINT"/><!-- 普通字段映射 --><result column="username" property="username" jdbcType="VARCHAR"/><result column="email" property="email" jdbcType="VARCHAR"/></resultMap><!-- SQL片段 --><sql id="Base_Column_List">id, username, email, password, age, create_time, update_time</sql><!-- 查询语句 --><select id="findById" parameterType="long" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM usersWHERE id = #{id}</select><!-- 插入语句 --><insert id="insert" parameterType="User">INSERT INTO users (username, email, password, age)VALUES (#{username}, #{email}, #{password}, #{age})</insert><!-- 更新语句 --><update id="update" parameterType="User">UPDATE usersSET username = #{username}, email = #{email}WHERE id = #{id}</update><!-- 删除语句 --><delete id="deleteById" parameterType="long">DELETE FROM users WHERE id = #{id}</delete></mapper>

1.2 映射文件的主要元素

  • mapper:根元素,包含namespace属性
  • resultMap:结果映射,定义如何将查询结果映射到Java对象
  • sql:可重用的SQL片段
  • select:查询语句
  • insert:插入语句
  • update:更新语句
  • delete:删除语句

2. 结果映射(ResultMap)详解

2.1 基础结果映射

<!-- 基础结果映射 -->
<resultMap id="UserResultMap" type="com.example.entity.User"><!-- id元素:主键字段映射 --><id column="user_id" property="id" jdbcType="BIGINT"/><!-- result元素:普通字段映射 --><result column="user_name" property="username" jdbcType="VARCHAR"/><result column="user_email" property="email" jdbcType="VARCHAR"/><result column="user_age" property="age" jdbcType="INTEGER"/><result column="create_time" property="createTime" jdbcType="TIMESTAMP"/><result column="update_time" property="updateTime" jdbcType="TIMESTAMP"/>
</resultMap>

2.2 复杂类型映射

<!-- 包含复杂类型的结果映射 -->
<resultMap id="UserWithProfileResultMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><!-- association:一对一关联映射 --><association property="profile" javaType="UserProfile"><id column="profile_id" property="id"/><result column="real_name" property="realName"/><result column="phone" property="phone"/><result column="address" property="address"/></association><!-- collection:一对多关联映射 --><collection property="roles" ofType="Role"><id column="role_id" property="id"/><result column="role_name" property="roleName"/><result column="role_description" property="description"/></collection>
</resultMap>

2.3 继承结果映射

<!-- 基础结果映射 -->
<resultMap id="BaseUserResultMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><result column="create_time" property="createTime"/>
</resultMap><!-- 继承基础映射并扩展 -->
<resultMap id="ExtendedUserResultMap" type="User" extends="BaseUserResultMap"><result column="last_login_time" property="lastLoginTime"/><result column="login_count" property="loginCount"/><!-- 添加关联映射 --><association property="profile" javaType="UserProfile"><id column="profile_id" property="id"/><result column="real_name" property="realName"/></association>
</resultMap>

2.4 自动映射配置

<!-- 开启自动映射 -->
<resultMap id="AutoMappingResultMap" type="User" autoMapping="true"><!-- 只需要配置特殊的映射关系 --><id column="user_id" property="id"/><!-- 其他字段会自动映射(如果字段名和属性名匹配) -->
</resultMap>

3. 动态SQL详解

3.1 if标签

if标签是最常用的动态SQL标签,用于条件判断。

<!-- 基础if用法 -->
<select id="findUsersByCondition" parameterType="User" resultMap="BaseResultMap">SELECT * FROM usersWHERE 1=1<if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if><if test="age != null">AND age = #{age}</if><if test="createTime != null">AND create_time >= #{createTime}</if>
</select><!-- 复杂条件判断 -->
<select id="findUsersWithComplexCondition" parameterType="map" resultMap="BaseResultMap">SELECT * FROM usersWHERE 1=1<if test="keyword != null and keyword != ''">AND (username LIKE CONCAT('%', #{keyword}, '%') OR email LIKE CONCAT('%', #{keyword}, '%'))</if><if test="ageRange != null"><if test="ageRange.min != null">AND age >= #{ageRange.min}</if><if test="ageRange.max != null">AND age <= #{ageRange.max}</if></if><if test="roles != null and roles.size() > 0">AND id IN (SELECT user_id FROM user_roles WHERE role_id IN<foreach collection="roles" item="roleId" open="(" separator="," close=")">#{roleId}</foreach>)</if>
</select>

3.2 where标签

where标签可以智能地处理WHERE子句,自动去除多余的AND或OR。

<select id="findUsersByConditionWithWhere" parameterType="User" resultMap="BaseResultMap">SELECT * FROM users<where><if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if><if test="age != null">AND age = #{age}</if><if test="status != null">AND status = #{status}</if></where>ORDER BY create_time DESC
</select>

3.3 set标签

set标签用于动态更新语句,自动处理逗号分隔符。

<update id="updateUserSelective" parameterType="User">UPDATE users<set><if test="username != null and username != ''">username = #{username},</if><if test="email != null and email != ''">email = #{email},</if><if test="password != null and password != ''">password = #{password},</if><if test="age != null">age = #{age},</if><if test="status != null">status = #{status},</if>update_time = CURRENT_TIMESTAMP</set>WHERE id = #{id}
</update>

3.4 choose、when、otherwise标签

类似于Java中的switch语句,用于多分支条件判断。

<select id="findUsersByType" parameterType="map" resultMap="BaseResultMap">SELECT * FROM usersWHERE 1=1<choose><when test="type == 'active'">AND status = 'ACTIVE' AND last_login_time > DATE_SUB(NOW(), INTERVAL 30 DAY)</when><when test="type == 'inactive'">AND status = 'ACTIVE' AND last_login_time <= DATE_SUB(NOW(), INTERVAL 30 DAY)</when><when test="type == 'disabled'">AND status = 'DISABLED'</when><otherwise>AND status = 'ACTIVE'</otherwise></choose><if test="orderBy != null"><choose><when test="orderBy == 'username'">ORDER BY username</when><when test="orderBy == 'createTime'">ORDER BY create_time DESC</when><when test="orderBy == 'lastLogin'">ORDER BY last_login_time DESC</when><otherwise>ORDER BY id</otherwise></choose></if>
</select>

3.5 foreach标签

foreach标签用于遍历集合,常用于IN查询和批量操作。

<!-- IN查询 -->
<select id="findUsersByIds" parameterType="list" resultMap="BaseResultMap">SELECT * FROM usersWHERE id IN<foreach collection="list" item="id" open="(" separator="," close=")">#{id}</foreach>
</select><!-- 批量插入 -->
<insert id="batchInsertUsers" parameterType="list">INSERT INTO users (username, email, password, age)VALUES<foreach collection="list" item="user" separator=",">(#{user.username}, #{user.email}, #{user.password}, #{user.age})</foreach>
</insert><!-- 批量更新(MySQL特有语法) -->
<update id="batchUpdateUsers" parameterType="list"><foreach collection="list" item="user" separator=";">UPDATE usersSET username = #{user.username}, email = #{user.email}WHERE id = #{user.id}</foreach>
</update><!-- 复杂的foreach用法 -->
<select id="findUsersByMultipleConditions" parameterType="map" resultMap="BaseResultMap">SELECT * FROM usersWHERE 1=1<if test="userIds != null and userIds.size() > 0">AND id IN<foreach collection="userIds" item="userId" open="(" separator="," close=")">#{userId}</foreach></if><if test="conditions != null and conditions.size() > 0">AND (<foreach collection="conditions" item="condition" separator=" OR ">(username LIKE CONCAT('%', #{condition.keyword}, '%') AND age BETWEEN #{condition.minAge} AND #{condition.maxAge})</foreach>)</if>
</select>

3.6 trim标签

trim标签是更通用的标签,可以自定义前缀、后缀和分隔符的处理。

<!-- 使用trim实现where功能 -->
<select id="findUsersWithTrim" parameterType="User" resultMap="BaseResultMap">SELECT * FROM users<trim prefix="WHERE" prefixOverrides="AND |OR "><if test="username != null">AND username = #{username}</if><if test="email != null">AND email = #{email}</if><if test="age != null">AND age = #{age}</if></trim>
</select><!-- 使用trim实现set功能 -->
<update id="updateUserWithTrim" parameterType="User">UPDATE users<trim prefix="SET" suffixOverrides=","><if test="username != null">username = #{username},</if><if test="email != null">email = #{email},</if><if test="age != null">age = #{age},</if></trim>WHERE id = #{id}
</update><!-- 复杂的trim用法 -->
<insert id="insertUserWithTrim" parameterType="User">INSERT INTO users<trim prefix="(" suffix=")" suffixOverrides=","><if test="username != null">username,</if><if test="email != null">email,</if><if test="password != null">password,</if><if test="age != null">age,</if></trim><trim prefix="VALUES (" suffix=")" suffixOverrides=","><if test="username != null">#{username},</if><if test="email != null">#{email},</if><if test="password != null">#{password},</if><if test="age != null">#{age},</if></trim>
</insert>

4. SQL片段复用

4.1 基础SQL片段

<!-- 定义可重用的SQL片段 -->
<sql id="Base_Column_List">id, username, email, password, age, create_time, update_time
</sql><sql id="User_Where_Clause"><where><if test="username != null and username != ''">AND username = #{username}</if><if test="email != null and email != ''">AND email = #{email}</if><if test="age != null">AND age = #{age}</if><if test="status != null">AND status = #{status}</if></where>
</sql><!-- 使用SQL片段 -->
<select id="findUsers" parameterType="User" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM users<include refid="User_Where_Clause"/>ORDER BY create_time DESC
</select><select id="countUsers" parameterType="User" resultType="long">SELECT COUNT(*)FROM users<include refid="User_Where_Clause"/>
</select>

4.2 带参数的SQL片段

<!-- 带参数的SQL片段 -->
<sql id="orderByClause"><if test="${orderBy} != null and ${orderBy} != ''">ORDER BY ${orderBy}<if test="${orderDirection} != null and ${orderDirection} != ''">${orderDirection}</if></if>
</sql><!-- 使用带参数的SQL片段 -->
<select id="findUsersWithOrder" parameterType="map" resultMap="BaseResultMap">SELECT <include refid="Base_Column_List"/>FROM users<include refid="User_Where_Clause"/><include refid="orderByClause"><property name="orderBy" value="orderBy"/><property name="orderDirection" value="orderDirection"/></include>
</select>

4.3 复杂SQL片段组合

<!-- 分页查询片段 -->
<sql id="limitClause"><if test="offset != null and limit != null">LIMIT #{offset}, #{limit}</if>
</sql><!-- 连接查询片段 -->
<sql id="userRoleJoin">LEFT JOIN user_roles ur ON u.id = ur.user_idLEFT JOIN roles r ON ur.role_id = r.id
</sql><!-- 复杂查询组合 -->
<select id="findUsersWithRolesAndPaging" parameterType="map" resultMap="UserWithRolesResultMap">SELECT u.id, u.username, u.email, u.create_time,r.id as role_id, r.role_name, r.description as role_descriptionFROM users u<include refid="userRoleJoin"/><where><if test="username != null and username != ''">AND u.username LIKE CONCAT('%', #{username}, '%')</if><if test="roleName != null and roleName != ''">AND r.role_name = #{roleName}</if><if test="startDate != null">AND u.create_time >= #{startDate}</if><if test="endDate != null">AND u.create_time <= #{endDate}</if></where>ORDER BY u.create_time DESC<include refid="limitClause"/>
</select>

5. 高级映射技巧

5.1 嵌套查询

<!-- 用户结果映射,包含延迟加载的角色信息 -->
<resultMap id="UserWithLazyRolesResultMap" type="User"><id column="id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><!-- 嵌套查询,延迟加载角色信息 --><collection property="roles" ofType="Role" select="findRolesByUserId" column="id"fetchType="lazy"/>
</resultMap><!-- 查询用户的角色 -->
<select id="findRolesByUserId" parameterType="long" resultType="Role">SELECT r.id, r.role_name as roleName, r.descriptionFROM roles rINNER JOIN user_roles ur ON r.id = ur.role_idWHERE ur.user_id = #{userId}
</select><!-- 查询用户(角色信息会延迟加载) -->
<select id="findUserWithLazyRoles" parameterType="long" resultMap="UserWithLazyRolesResultMap">SELECT id, username, email, create_timeFROM usersWHERE id = #{id}
</select>

5.2 嵌套结果映射

<!-- 嵌套结果映射,一次查询获取所有数据 -->
<resultMap id="UserWithRolesNestedResultMap" type="User"><id column="user_id" property="id"/><result column="username" property="username"/><result column="email" property="email"/><result column="create_time" property="createTime"/><!-- 嵌套结果映射 --><collection property="roles" ofType="Role"><id column="role_id" property="id"/><result column="role_name" property="roleName"/><result column="role_description" property="description"/><result column="role_create_time" property="createTime"/></collection>
</resultMap><!-- 一次查询获取用户和角色信息 -->
<select id="findUserWithRolesNested" parameterType="long" resultMap="UserWithRolesNestedResultMap">SELECT u.id as user_id,u.username,u.email,u.create_time,r.id as role_id,r.role_name,r.description as role_description,r.create_time as role_create_timeFROM users uLEFT JOIN user_roles ur ON u.id = ur.user_idLEFT JOIN roles r ON ur.role_id = r.idWHERE u.id = #{id}
</select>

6. 小结

本文深入介绍了MyBatis SQL映射文件和动态SQL的核心功能:

  1. 结果映射:基础映射、复杂类型映射、继承映射
  2. 动态SQL标签:if、where、set、choose、foreach、trim
  3. SQL片段复用:提高代码复用性和维护性
  4. 高级映射:嵌套查询和嵌套结果映射

掌握这些技能可以:

  • 编写灵活的动态SQL语句
  • 处理复杂的查询需求
  • 优化SQL性能和代码结构
  • 实现高效的对象关系映射

🔗 下一篇预告

下一篇文章将探讨MyBatis缓存机制与性能优化,学习如何使用MyBatis的一级缓存和二级缓存来提升应用性能。


相关文章:

  • 上一篇:MyBatis配置文件详解
  • 下一篇:MyBatis缓存机制与性能优化
  • 返回目录

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

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

相关文章

vue+vite打包后的文件希望放在一个子目录下

比如我们常规操作是打包的项目文件直接放在域名下面。如果我们希望把项目放在子域名下面应该怎么处理呢&#xff1f;需要两个步骤vite.config.js里面指定base的路径假设我们希望放在子目录加做call那么我们可以这样base:/call/,注意不是build目录哈。return的最外层。如果本地和…

Java:Docx4j类库简介及使用

1.简介 Docx4j 是一个功能强大的 Java 类库&#xff0c;专门用于创建和操作 Microsoft Open XML 格式&#xff08;如 Word DOCX、PowerPoint PPTX 和 Excel XLSX&#xff09;的文件。它深受 Java 开发者喜爱&#xff0c;特别是在需要自动化处理 Office 文档的场景下。 下面是一…

【机械故障】旋转机械故障引起的振动信号调制效应概述

系列文章目录 提示&#xff1a;学习笔记 机械故障信号分析 共振峰 旋转机械故障引起的振动信号调制效应概述系列文章目录一、研究背景与意义二、故障引起的调制效应分类三、非平稳信号分析方法3.1 时频分析方法3.2 信号分解方法一、研究背景与意义 在工程实践中&#xff0c;可…

密码安全隐形基石:随机数、熵源与DRBG核心解析与技术关联

前言&#xff1a;密码安全的 “隐形基石” 在数字化浪潮席卷全球的今天&#xff0c;从金融交易的密钥生成到区块链的共识机制&#xff0c;从量子通信的加密协议到智能汽车的身份认证&#xff0c;随机数如同空气般渗透在信息系统的每一个安全节点。然而&#xff0c;看似简单的 …

Vue3 + Element Plus实现表格多行文本截断与智能Tooltip提示

在实际开发中&#xff0c;我们经常需要在表格中展示较长的文本内容&#xff0c;但又希望保持界面的整洁美观。本文将介绍如何在Vue3 和 Element Plus中实现表格多行文本截断&#xff0c;并智能控制Tooltip的显示——只有当文本被截断时才显示Tooltip&#xff0c;否则不显示。 需…

使用powerquery处理数据,取时间或者日期之前的

Table.AddColumn(#"已更改列类型 1", "自定义 (2)", each letcleanText Text.Replace([备注], "#(lf)", " "),hasTime Text.Contains(cleanText, "时间&#xff1a;"),hasDate Text.Contains(cleanText, "日期&…

Java面试全栈技术解析:从Spring Cloud到Kafka的实战演练

面试官&#xff1a;请简单介绍一下Spring Cloud的核心组件&#xff1f; 谢飞机&#xff1a;嗯...Spring Cloud主要是基于Spring Boot的&#xff0c;然后有Eureka做服务发现&#xff0c;Feign做声明式REST调用&#xff0c;还有Config做配置中心... 面试官&#xff1a;那在电商场…

极简 useState:手写 20 行,支持多次 setState 合并

不依赖 React&#xff0c;用 闭包 批处理队列 实现可合并更新的 useState。一、20 行完整代码 function createUseState(initialValue) {let state initialValue;let pending null; // 合并队列let listeners [];const flush () > {if (pending ! null) {…

LabVIEW Vision视觉引导撑簧圈智能插装

为解决人工插装连接器撑簧圈时劳动强度大、效率低、一致性差的问题&#xff0c;例以 LabVIEW为开发平台&#xff0c;结合 IMAQ Vision 机器视觉库&#xff0c;搭配精密硬件搭建智能插装系统。系统可适配 9 芯、13 芯、25 芯、66 芯、128 芯 5 种规格工件&#xff0c;经 100 只产…

【Lua】题目小练11

-- 题目1&#xff1a;-- 给定表 t {"apple", "banana", "apple", "orange", "banana", "apple"}-- 写一个函数 countFreq(tbl) 返回一个新表&#xff0c;统计每个元素出现次数-- 例如&#xff1a;返回 {apple3, …

ElementUI之菜单(Menu)使用

文章目录项目创建创建项目运行项目整理目录删除src/assets中的所有logo.png删除src/components中的所有文件修改src/route/index.js删除src/views中所有文件修改src/app.vue整理完目录如下引入ElementUI安装ElementUI引入ElementUI测试是否安装成功编写src/app.vue运行结果编写…

Python训练营打卡Day44-通道注意力(SE注意力)

知识点回顾&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插入的位置通道注意力后的特征图和热力图 内…

shiro进行解密

目录Shiro 解密的核心注意事项1. 密码处理&#xff1a;坚决避免 “可逆解密”2.例子【自己模拟数据库&#xff0c;未连数据库】:Shiro 解密的核心注意事项 1. 密码处理&#xff1a;坚决避免 “可逆解密” 禁用明文存储:永远不要将明文密码存入数据库&#xff0c;必须使用 Has…

更改 Microsoft Edge 浏览器的缓存与用户数据目录位置

Microsoft Edge浏览器默认会将缓存文件和用户数据存储在系统盘&#xff08;通常是C盘&#xff09;&#xff0c;随着使用时间的增长&#xff0c;这些文件可能会占用大量空间。本文将详细介绍多种更改Edge浏览器缓存位置和用户数据目录位置的方法&#xff0c;帮助您更好地管理磁盘…

【传奇开心果系列】Flet框架实现的图形化界面的PDF转word转换器办公小工具自定义模板

let框架实现的图形化界面的PDF转word转换器办公小工具自定义模板一、效果展示截图二、PDF转Word转换器概括介绍三、功能特性四、安装依赖五、运行程序六、使用说明七、注意事项八、技术栈九、系统要求十、源码下载地址 一、效果展示截图二、PDF转Word转换器概括介绍 一个基于Fl…

STM32 定时器(PWM输入捕获)

以下是基于STM32标准库&#xff08;以STM32F103为例&#xff09;实现PWM输入模式&#xff08;自动双沿捕获&#xff09;的完整代码&#xff0c;通过配置定时器的PWM输入模式&#xff0c;可自动捕获外部PWM信号的周期&#xff08;频率&#xff09;​和占空比&#xff0c;无需手动…

Web安全开发指导规范文档V1.0

一、背景 团队最近频繁遭受网络攻击,引起了部门技术负责人的重视,笔者在团队中相对来说更懂安全,因此花了点时间编辑了一份安全开发自检清单,觉得应该也有不少读者有需要,所以将其分享出来。 二、编码安全 2.1 输入验证 说明 检查项 概述 任何来自客户端的数据,如URL和…

在Godot中为您的游戏添加并控制游戏角色的完整技术指南

这是一个在Godot中为您的游戏添加并控制玩家角色的完整技术指南。这个过程分为三大步&#xff1a;​准备资源、构建场景、编写控制脚本。道可道&#xff0c;非常道&#xff0c;名可名&#xff0c;非常名&#xff01;第一步&#xff1a;准备资源&#xff08;建模与动画&#xff…

Flink 状态 RocksDBListState(写入时的Merge优化)

RocksDBListState<K, N, V> RocksDBListState 继承自 AbstractRocksDBState<K, N, List<V>>&#xff0c;并实现了 InternalListState<K, N, V> 接口。继承 AbstractRocksDBState: 这意味着它天然获得了与 RocksDB 交互的底层能力&#xff0c;包括&…

zookeeper-保姆级配置说明

一. 基本配置&#xff1a;clientPort&#xff1a; 客户端连接的服务器所监听的tcp端口&#xff0c;默认2181dataDir&#xff1a;内存数据库保存的数据路径。myid也存放在这个目录下&#xff0c;数据以异步方式写入。dataLogDir&#xff1a;事务日志存放路径。服务在确认一个事务…