目录
一、前言
MyBatis作为一款轻量级的持久层框架,凭借其灵活的SQL编写能力和强大的结果映射机制,成为Java开发中数据库操作的重要工具。动态SQL和结果映射是MyBatis的核心特性,能够显著提升开发效率并简化复杂业务场景下的数据库交互。
本系列文章将深入剖析MyBatis的实战应用,从动态SQL的编写技巧到结果映射的高级用法,结合实际案例帮助开发者掌握MyBatis的核心功能。通过解析常见场景和最佳实践,为高效、优雅的数据库操作提供实用指导。
二、结果集映射
MyBatis的结果集映射是将数据库查询结果转换为Java对象的核心机制,尤其适用于字段名和属性名不一致、嵌套对象、关联表等复杂场景。主要通过XML配置或注解实现。以下是常见映射方式与技巧:
1.自动映射
如果字段名和java类的属性名大部分一致,可以开启自动映射
<resultMap id="BaseResultMap" type="org.wal.userdemo.entity.UserEntity">
<id column="id" property="id" />
<!-- name 、age 、birthday此处可省略映射 -->
<result column="name" property="name" />
<result column="age" property="age" />
<result column="birthday" property="birthday" />
<result column="create_time" property="createTime" />
<result column="update_time" property="updateTime" />
<result column="create_by" property="createBy" />
<result column="update_by" property="updateBy" />
<result column="del_flag" property="delFlag" />
</resultMap>
<select id="getAllUsers" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select * from user
</select>
2.复杂结果集映射
新建QualificationEntity实体类
/**
* 学历表
* @TableName qualification
*/
@Data
public class QualificationEntity {
/**
* 主键
*/
private Integer id;
/**
* user表主键
*/
private Integer userId;
/**
* 毕业院校
*/
private String school;
/**
* 入学时间
*/
private Date enterData;
/**
* 毕业时间
*/
private Date graduateData;
/**
* 专业
*/
private String magor;
}
在UserEntity添加属性 学历QualificationEntity
@Data
public class UserEntity extends BaseEntity{
/**
* id 主键
*/
private Integer id;
/**
* name 姓名
*/
private String name;
/**
* age 年龄
*/
private Integer age;
/**
* birthday 生日
*/
private Date birthday;
/**
* Qualification 学历
*/
private List<QualificationEntity> qualList;
}
在xml中添加映射子结果集
<resultMap id="BaseResultMap" type="org.wal.userdemo.entity.UserEntity">
<id column="id" property="id" />
<!-- name 、age 、birthday此处可省略映射 -->
<result column="name" property="name" />
<result column="age" property="age" />
<result column="birthday" property="birthday" />
<result column="create_time" property="createTime" />
<result column="update_time" property="updateTime" />
<result column="create_by" property="createBy" />
<result column="update_by" property="updateBy" />
<result column="del_flag" property="delFlag" />
<!-- 映射用户所对应的学历信息 -->
<collection property="qualList" ofType="org.wal.userdemo.entity.QualificationEntity">
<result column="user_id" property="userId"/>
<result column="school" property="school"/>
<result column="enter_data" property="enterData"/>
<result column="graduate_data" property="graduateData"/>
<result column="magor" property="magor"/>
</collection>
</resultMap>
<select id="getAllUsers" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select u.*,
q.*
from user u
left join qualification q on u.id = q.user_id
where u.name = '张'
and u.del_flag = 0;
</select>
启动项目,用postman模拟http请求
3.子映射嵌套子查询
在映射结果中可定义子查询sql。
用postman测试结果如下:
优点:
1.sql简单,子查询sql可多处调用。
2.可开启懒加载,按需使用。
注:什么是mybatis的懒加载?
代码解释示例如下:
UserEntity user = userMapper.getAllUsers();
List<QualificationEntity> qualList = user.getQualList(); // 此时才触发子查询
注:懒加载需要再yml文件中添加如下配置:
configuration:
lazy-loading-enabled: true #启用延迟加载
aggressive-lazy-loading: false #是否在访问任意属性时触发延迟加载(建议关闭)
4.结果集映射注意事项
1.父子映射字段不要重复
子映射结果集(qualification表)映射字段不要跟主映射结果集(user表)有重复映射字段,如下:
这样会影响映射结果集映射,postman测试如下:
可以发现子结果的id和主结果的id值一样,并且只有一个子结果(也可能出现异常或其他问题)
解决方案一: 重复id定义前缀,sql中具体字段对应上别名,如下:
2.子映射字段必须要有映射(如果需要这个字段的值)
子映射结果字段必须要映射,因为子映射结果是默认不支持自动映射的,如下:
不能像主映射(user表)那样java类和数据库字段一样就省略,不然会造成如下结果(省略了school就映射不到school):
解决方案一:子结果集映射补全需要的字段(过程略)。
解决方案二:配置mybatis全自动映射 :如下,在yml文件中添加配置
注: mybatis的映射配置分三种:无自动映射、部分自动映射(默认)、全自动映射,区别如下
需要的同学可自行配置。
3.结果集映射继承
mybatis的映射结果集是支持继承的,如下:
三、动态SQL
1.<where>
用于构建动态查询条件,自动去除开头的 AND 或 OR。
<select id="getAllUsers" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select * from user
<where>
<if test="name != null">and name like concat('%',#{name},'%')</if>
<if test="age != null">and age <= #{age}</if>
<if test="birthday != null">and birthday = #{birthday}</if>
</where>;
</select>
2.<if>
条件判断语句,根据参数是否存在添加相应的 SQL 片段。
<select id="getAllUsers" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select * from user
<where>
<if test="name != null">and name like concat('%',#{name},'%')</if>
<if test="age != null">and age <= #{age}</if>
<if test="birthday != null">and birthday = #{birthday}</if>
</where>;
</select>
3.<choose>、<when>、<otherwise>
类似于 Java 的 switch-case语法,选择一个匹配的条件。
<select id="getAllUsers" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select * from user
<where>
<choose>
<when test="name != null">
and name like concat('%',#{name},'%')
</when>
<when test="age != null">
and age < #{age}
</when>
<otherwise> <!-- 不满足上面的where,则默认 -->
and del_flag = 0
</otherwise>
</choose>
</where>
;
4.<set>
用于更新操作,自动去除末尾的逗号。
<update id="updateUser" parameterType="org.wal.userdemo.entity.UserEntity">
update
<set>
<if test="name != null">name = #{name},</if>
<if test="age != null">age = #{age}</if>
</set>
where id = #{id};
</update>
5.<trim>
可以在 SQL 语句前后添加或移除特定的字符串(如 AND, OR, WHERE, SET 等)。
<select id="getUsersByPage" resultMap="BaseResultMap" parameterType="String">
select *
from user
<trim prefix="LIMIT " suffixOverrides=",">
#{pageNum}, #{pageSize}
</trim>;
</select>
6.<foreach>
遍历集合,常用于 IN 子句或批量插入/更新。
<select id="getUsersByIds" resultMap="BaseResultMap" parameterType="list">
select * from user
<where>
id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</where>;
</select>
7.<bind>
创建一个变量并绑定到上下文,可以在 SQL 中使用。
<select id="searchUsers" resultMap="BaseResultMap">
<bind name="pattern" value="'%' + name + '%'" />
select * from user where name like #{pattern};
</select>
8.<include> 和 <sql>
定义可重用的 SQL 片段。
<sql id="userColumns">id, name, age, birthday, create_time, update_time, create_by, update_by, del_flag</sql>
<select id="getAllUsers" resultMap="BaseResultMap">
select <include refid="userColumns"/> from user;
</select>
9.示例整合
可以根据需要自行选择标签
<select id="complexQuery" resultMap="BaseResultMap" parameterType="org.wal.userdemo.DTO.req.UserDTOReq">
select <include refid="userColumns"/>
from user
<where>
<choose>
<when test="name != null">
<bind name="pattern" value="'%' + name + '%'" />
name like #{pattern}
</when>
<when test="age != null">
age < #{age}
</when>
<otherwise>
del_flag = 0
</otherwise>
</choose>
<if test="ids != null and ids.size() > 0">
and id in
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</if>
</where>
<trim prefix="ORDER BY " suffixOverrides=",">
id DESC
</trim>;
</select>
四、结语
MyBatis的动态SQL和结果映射功能为复杂数据库操作提供了强大支持。通过合理运用自动映射、嵌套查询和懒加载等特性,可以显著提升开发效率和代码可维护性。动态SQL的灵活条件组合让查询逻辑更清晰,而结果映射机制则简化了对象关系转换的过程。掌握这些核心技巧,能够应对各种业务场景下的数据持久化需求。