👋 大家好,我是 阿问学长
!专注于分享优质开源项目
解析、毕业设计项目指导
支持、幼小初高
的教辅资料
推荐等,欢迎关注交流!🚀
12-SQL映射文件与动态SQL
📖 本文概述
本文是SSM框架系列MyBatis进阶篇的第二篇,将深入探讨SQL映射文件的编写技巧和动态SQL的使用方法。通过丰富的示例和最佳实践,帮助读者掌握MyBatis中最核心的功能之一。
🎯 学习目标
- 深入理解SQL映射文件的结构和元素
- 掌握结果映射(ResultMap)的高级用法
- 学会编写各种类型的动态SQL
- 了解SQL片段的复用技巧
- 掌握复杂查询和关联映射的实现
1. SQL映射文件概述
1.1 映射文件的基本结构
SQL映射文件是MyBatis的核心,它定义了SQL语句和Java对象之间的映射关系。
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">
<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 users
WHERE 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 users
SET 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 基础结果映射
xml
<!-- 基础结果映射 -->
<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 复杂类型映射
xml
<!-- 包含复杂类型的结果映射 -->
<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 继承结果映射
xml
<!-- 基础结果映射 -->
<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 自动映射配置
xml
<!-- 开启自动映射 -->
<resultMap id="AutoMappingResultMap" type="User" autoMapping="true">
<!-- 只需要配置特殊的映射关系 -->
<id column="user_id" property="id"/>
<!-- 其他字段会自动映射(如果字段名和属性名匹配) -->
</resultMap>
3. 动态SQL详解
3.1 if标签
if标签是最常用的动态SQL标签,用于条件判断。
xml
<!-- 基础if用法 -->
<select id="findUsersByCondition" parameterType="User" resultMap="BaseResultMap">
SELECT * FROM users
WHERE 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 users
WHERE 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。
xml
<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标签用于动态更新语句,自动处理逗号分隔符。
xml
<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语句,用于多分支条件判断。
xml
<select id="findUsersByType" parameterType="map" resultMap="BaseResultMap">
SELECT * FROM users
WHERE 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查询和批量操作。
xml
<!-- IN查询 -->
<select id="findUsersByIds" parameterType="list" resultMap="BaseResultMap">
SELECT * FROM users
WHERE 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 users
SET username = #{user.username}, email = #{user.email}
WHERE id = #{user.id}
</foreach>
</update>
<!-- 复杂的foreach用法 -->
<select id="findUsersByMultipleConditions" parameterType="map" resultMap="BaseResultMap">
SELECT * FROM users
WHERE 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标签是更通用的标签,可以自定义前缀、后缀和分隔符的处理。
xml
<!-- 使用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片段
xml
<!-- 定义可重用的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片段
xml
<!-- 带参数的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片段组合
xml
<!-- 分页查询片段 -->
<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_id
LEFT 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_description
FROM 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 嵌套查询
xml
<!-- 用户结果映射,包含延迟加载的角色信息 -->
<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.description
FROM roles r
INNER JOIN user_roles ur ON r.id = ur.role_id
WHERE ur.user_id = #{userId}
</select>
<!-- 查询用户(角色信息会延迟加载) -->
<select id="findUserWithLazyRoles" parameterType="long" resultMap="UserWithLazyRolesResultMap">
SELECT id, username, email, create_time
FROM users
WHERE id = #{id}
</select>
5.2 嵌套结果映射
xml
<!-- 嵌套结果映射,一次查询获取所有数据 -->
<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_time
FROM users u
LEFT JOIN user_roles ur ON u.id = ur.user_id
LEFT JOIN roles r ON ur.role_id = r.id
WHERE u.id = #{id}
</select>
6. 小结
本文深入介绍了MyBatis SQL映射文件和动态SQL的核心功能:
- 结果映射:基础映射、复杂类型映射、继承映射
- 动态SQL标签:if、where、set、choose、foreach、trim
- SQL片段复用:提高代码复用性和维护性
- 高级映射:嵌套查询和嵌套结果映射
掌握这些技能可以:
- 编写灵活的动态SQL语句
- 处理复杂的查询需求
- 优化SQL性能和代码结构
- 实现高效的对象关系映射
🔗 下一篇预告
下一篇文章将探讨MyBatis缓存机制与性能优化,学习如何使用MyBatis的一级缓存和二级缓存来提升应用性能。
相关文章: