java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > MyBatis 数据封装

MyBatis 数据封装全攻略(告别空值与映射混乱问题)

作者:lsp程序员010

本文系统介绍MyBatis数据封装的常见问题及解决方案,涵盖resultType、resultMap、驼峰转换、嵌套处理、懒加载等核心机制,并推荐MyBatis-Plus简化开发,提升效率与可维护性,感兴趣的朋友跟随小编一起看看吧

MyBatis 数据封装全攻略:告别空值与映射混乱

在日常开发中,使用 MyBatis 进行数据库操作时,你是否经常遇到以下问题?

这些问题本质上都是 数据封装(Result Mapping) 的问题。本文将系统性地为你梳理 MyBatis 中数据封装的核心机制,并提供最佳实践方案,助你彻底解决封装难题!

一、MyBatis 数据封装的两种方式

1. resultType —— 简单粗暴

适用于字段名与 Java 属性名完全匹配的情况。

<select id="getUserById" resultType="com.example.User">
    SELECT id, username, email FROM user WHERE id = #{id}
</select>

✅ 优点:简洁、易用
❌ 缺点:无法处理驼峰命名、多表关联、嵌套对象等复杂场景

⚠️ 注意:若数据库字段是 user_name,而 Java 属性是 userName,则不会自动映射!

二、开启驼峰命名转换(推荐基础配置)

mybatis-config.xml 或 Spring Boot 配置中开启:

# application.yml (Spring Boot)
mybatis:
  configuration:
    map-underscore-to-camel-case: true

或 XML 配置:

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

开启后,user_nameuserNamecreate_timecreateTime 自动映射。

三、resultMap —— 精准控制映射关系

resultType 无法满足需求时,必须使用 <resultMap>

基础字段映射

<resultMap id="UserResultMap" type="com.example.User">
    <id property="id" column="user_id"/> <!-- 主键建议用 id 标签 -->
    <result property="username" column="user_name"/>
    <result property="email" column="email_addr"/>
    <result property="createTime" column="gmt_create"/>
</resultMap>
<select id="getUserById" resultMap="UserResultMap">
    SELECT user_id, user_name, email_addr, gmt_create 
    FROM user_info 
    WHERE user_id = #{id}
</select>

💡 id 标签用于主键,有助于性能优化(缓存、嵌套查询去重)

四、处理对象嵌套 —— association

当 User 包含一个 Profile 对象:

public class User {
    private Long id;
    private String username;
    private Profile profile; // 嵌套对象
}
public class Profile {
    private String avatar;
    private String bio;
}

XML 映射:

<resultMap id="UserWithProfileResultMap" type="User">
    <id property="id" column="user_id"/>
    <result property="username" column="username"/>
    <!-- 嵌套对象映射 -->
    <association property="profile" javaType="Profile">
        <result property="avatar" column="avatar_url"/>
        <result property="bio" column="user_bio"/>
    </association>
</resultMap>
<select id="getUserWithProfile" resultMap="UserWithProfileResultMap">
    SELECT u.id as user_id, u.username,
           p.avatar as avatar_url, p.bio as user_bio
    FROM user u
    LEFT JOIN profile p ON u.id = p.user_id
    WHERE u.id = #{id}
</select>

五、处理集合嵌套 —— collection

当 User 包含多个 Role:

public class User {
    private Long id;
    private String username;
    private List<Role> roles; // 集合
}
public class Role {
    private Long id;
    private String roleName;
}

XML 映射:

<resultMap id="UserWithRolesResultMap" type="User">
    <id property="id" column="user_id"/>
    <result property="username" column="username"/>
    <!-- 集合映射 -->
    <collection property="roles" ofType="Role">
        <id property="id" column="role_id"/>
        <result property="roleName" column="role_name"/>
    </collection>
</resultMap>
<select id="getUserWithRoles" resultMap="UserWithRolesResultMap">
    SELECT u.id as user_id, u.username,
           r.id as role_id, r.name as role_name
    FROM user u
    LEFT JOIN user_role ur ON u.id = ur.user_id
    LEFT JOIN role r ON ur.role_id = r.id
    WHERE u.id = #{id}
</select>

📌 注意:使用 ofType 指定集合元素类型,而不是 javaType

六、避免 N+1 查询问题

上面的写法虽然能正确封装,但可能引发 N+1 查询。推荐使用 JOIN 查询 + 分组封装分步查询(懒加载)

方案一:分步查询(推荐用于懒加载)

<resultMap id="UserLazyLoadResultMap" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
    <collection property="roles" 
                select="selectRolesByUserId" 
                column="id" 
                fetchType="lazy"/>
</resultMap>
<select id="selectUserById" resultMap="UserLazyLoadResultMap">
    SELECT id, username FROM user WHERE id = #{id}
</select>
<select id="selectRolesByUserId" resultType="Role">
    SELECT id, name as roleName FROM role 
    WHERE id IN (SELECT role_id FROM user_role WHERE user_id = #{userId})
</select>

在配置中开启懒加载:

mybatis:
  configuration:
    lazy-loading-enabled: true
    aggressive-lazy-loading: false

七、使用注解简化映射(可选)

对于简单场景,也可使用注解:

@Results(id = "UserResult", value = {
    @Result(property = "id", column = "user_id"),
    @Result(property = "username", column = "user_name"),
    @Result(property = "profile", column = "user_id",
            one = @One(select = "selectProfileByUserId"))
})
@Select("SELECT user_id, user_name FROM user WHERE id = #{id}")
User getUserById(Long id);

八、实战技巧 & 避坑指南

✅ 技巧 1:统一别名规范

在 SQL 中使用 AS 给字段起别名,避免列名冲突:

SELECT u.id AS user_id, r.id AS role_id, ...

✅ 技巧 2:复用 ResultMap

使用 <extend> 继承已有的 ResultMap:

<resultMap id="BaseUserMap" type="User">
    <id property="id" column="id"/>
    <result property="username" column="username"/>
</resultMap>
<resultMap id="UserWithProfileMap" extends="BaseUserMap">
    <association property="profile" ... />
</resultMap>

✅ 技巧 3:自动映射策略

<resultMap autoMapping="true" ...>

开启后,未明确指定的匹配字段也会自动映射(需配合驼峰转换)。

❌ 避坑 1:不要混用 resultType 和 resultMap

在一个 select 标签内只能使用其一。

❌ 避坑 2:集合属性未初始化

确保 Java 实体中的 List 属性已初始化:

private List<Role> roles = new ArrayList<>(); // 避免 null

九、终极解决方案:MyBatis-Plus(可选进阶)

如果你觉得原生 MyBatis 配置繁琐,可以考虑 MyBatis-Plus

示例:

// MP 自动处理字段映射 + 驼峰
List<User> users = userMapper.selectList(
    Wrappers.<User>lambdaQuery()
        .eq(User::getUsername, "admin")
);

总结

问题类型解决方案
字段名不匹配开启驼峰 / 使用 <result>
对象嵌套<association>
集合嵌套<collection>
性能优化分步查询 + 懒加载
复杂联查ResultMap + SQL 别名
快速开发MyBatis-Plus

掌握以上方法,你将能从容应对 MyBatis 中 99% 的数据封装问题!记得收藏本文,下次遇到映射失败时,按图索骥,轻松解决!

到此这篇关于MyBatis 数据封装全攻略(告别空值与映射混乱问题)的文章就介绍到这了,更多相关MyBatis 数据封装内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文