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的核心功能:
- 结果映射:基础映射、复杂类型映射、继承映射
- 动态SQL标签:if、where、set、choose、foreach、trim
- SQL片段复用:提高代码复用性和维护性
- 高级映射:嵌套查询和嵌套结果映射
掌握这些技能可以:
- 编写灵活的动态SQL语句
- 处理复杂的查询需求
- 优化SQL性能和代码结构
- 实现高效的对象关系映射
🔗 下一篇预告
下一篇文章将探讨MyBatis缓存机制与性能优化,学习如何使用MyBatis的一级缓存和二级缓存来提升应用性能。
相关文章:
- 上一篇:MyBatis配置文件详解
- 下一篇:MyBatis缓存机制与性能优化
- 返回目录