程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • mybatis

    • mybatis 概述
    • MyBatis 整合Spring Boot
    • MyBatis 开发环境搭建
    • MyBatis 入门案例
    • MyBatis 常用注解
    • MyBatis数据库类型与实体类型对应关系
    • MyBatis 动态参数传递与模糊查询
    • MyBatis 查询原理与字段映射
      • 一、MyBatis 查询原理
        • 1. 配置文件解析阶段
        • 2. 会话创建与准备阶段
        • 3. SQL 解析与动态处理
        • 4. 参数处理与绑定
        • 5. JDBC 层处理
        • 6. 结果集处理与对象映射
        • 7. 缓存机制
      • 二、实体类与数据库字段的映射关系
        • 1. 实体类字段与数据库字段数量关系
        • (1) 实体类字段少于数据库字段
        • (2) 实体类字段多于数据库字段
        • 2. 实体类字段命名与数据库字段不一致的处理
        • (1) 使用列别名
        • (2) 开启驼峰命名自动转换
        • (3) 使用 ResultMap 进行手动映射
        • (4) 使用 @Results 注解(注解方式)
        • 3. 开发中常见的实体类映射最佳实践
        • (1) 分层设计模式
        • (2) 查询场景的映射策略
        • (3) 使用 BeanUtils 或对象映射框架
        • (4) 处理特殊类型的映射
    • MyBatis 的resultType 和 resultMap
    • MyBatis 增删改操作
    • MyBatis 单表查询
    • MyBatis 多表查询
    • MyBatis 动态 SQL
    • MyBatis 分页插件
    • MyBatis 一级和二级缓存
    • MyBatis 逆向工程
    • MyBatis 多数据源配置
    • MyBatis 自定义拦截器
  • MyBatis
  • mybatis
scholar
2025-03-16
目录

MyBatis 查询原理与字段映射

# MyBatis 查询原理与实体类映射

# 一、MyBatis 查询原理

MyBatis 作为一个半自动化的 ORM 框架,其查询过程涉及多个层面的转换和处理,下面详细说明其工作原理:

# 1. 配置文件解析阶段

  • 初始化加载:应用启动时,MyBatis 读取并解析全局配置文件(mybatis-config.xml)和映射文件(Mapper XML)
  • 构建配置对象:解析配置后,创建 Configuration 对象,包含数据源、类型别名、映射器等信息
  • 注册映射器:将所有的 Mapper 接口和 XML 配置文件中的 SQL 语句注册到 Configuration 对象中,建立 namespace+id 到 MappedStatement 的映射关系

# 2. 会话创建与准备阶段

  • 创建 SqlSession:通过 SqlSessionFactory.openSession() 创建会话对象
  • 获取 Mapper 代理:通过 SqlSession.getMapper(Class) 获取对应的 Mapper 接口的动态代理对象
  • 方法调用拦截:当调用 Mapper 接口方法时,代理对象拦截请求,执行以下后续流程

# 3. SQL 解析与动态处理

  • 获取映射语句:根据调用的方法和接口,找到对应的 MappedStatement 对象
  • 动态 SQL 解析:
    • 解析 XML 中的 <if>、<where>、<foreach> 等动态标签
    • 根据传入的参数值判断条件是否成立
    • 移除不成立的条件,保留满足条件的 SQL 片段
    • 处理特殊字符,如自动处理 <where> 条件中的 AND/OR 前缀
  • 最终 SQL 生成:将处理后的 SQL 片段组合成完整的 SQL 语句

# 4. 参数处理与绑定

  • 参数映射:
    • 获取方法传入的参数值
    • 解析 @Param 注解,建立参数名到参数值的映射关系
    • 对于无 @Param 注解的多参数方法,使用 param1, param2 或 arg0, arg1 作为参数名
  • 参数解析:
    • 解析 SQL 中的 #{} 占位符,提取参数名称和配置(如 jdbcType、typeHandler 等)
    • 将 #{} 替换为 JDBC 预编译占位符 ?
    • 记录每个 ? 对应的参数名和位置信息
  • 类型处理:根据参数类型选择适当的 TypeHandler 来处理 Java 类型与 JDBC 类型之间的转换

# 5. JDBC 层处理

  • 获取数据库连接:从数据源获取数据库连接
  • 预编译 SQL:创建 PreparedStatement 对象,预编译 SQL 语句
  • 设置参数:
    • 遍历之前记录的参数信息
    • 使用对应的 TypeHandler 将 Java 参数值设置到 PreparedStatement 的对应位置
  • 执行查询:调用 PreparedStatement.executeQuery() 执行查询,获取 ResultSet

# 6. 结果集处理与对象映射

  • 结果集元数据分析:
    • 读取 ResultSet 的元数据信息(列名、类型等)
    • 根据结果类型决定映射策略(简单映射、嵌套映射等)
  • 结果映射过程:
    • 遍历 ResultSet 中的每一行数据
    • 创建目标对象实例(如 User 对象)
    • 对于每个列:
      • 找到列名对应的对象属性(考虑配置的命名策略,如驼峰转换)
      • 使用适当的 TypeHandler 将 JDBC 类型转换为 Java 类型
      • 通过反射或其他机制设置属性值
    • 处理关联查询(一对一、一对多)关系,可能涉及额外的查询
    • 返回填充后的对象集合

# 7. 缓存机制

  • 一级缓存(SqlSession 级别):
    • 默认开启,作用范围为同一 SqlSession 内
    • 执行查询前先检查缓存,命中则直接返回缓存结果
    • 同一 SqlSession 内的增删改操作会清空一级缓存
  • 二级缓存(Mapper 级别):
    • 需要手动配置开启,作用范围为同一 Mapper 的所有 SqlSession
    • 当 SqlSession 关闭时,一级缓存结果会被推送到二级缓存
    • 可配置各种缓存策略(LRU、FIFO 等)和刷新间隔

# 二、实体类与数据库字段的映射关系

# 1. 实体类字段与数据库字段数量关系

MyBatis 对接收实体类的字段数量没有严格要求,具有很高的灵活性:

# (1) 实体类字段少于数据库字段

// 数据库表:user(id, username, password, email, phone, address, create_time)
// 实体类只关心部分字段
public class UserBasicInfo {
    private Integer id;
    private String username;
    private String email;
    // getter 和 setter 方法
}
1
2
3
4
5
6
7
8

处理方式:

  • MyBatis 只会尝试填充实体类中存在的属性,对于数据库中多出的字段会自动忽略
  • 这种情况非常常见于只需要部分字段数据的查询场景
  • 适用于视图对象(VO)、数据传输对象(DTO)等只关心部分字段的情况

# (2) 实体类字段多于数据库字段

// 数据库表:user(id, username, email)
// 实体类包含额外计算字段或非数据库字段
public class UserInfo {
    private Integer id;
    private String username;
    private String email;
    private Boolean isAdmin; // 非数据库字段,可能通过业务逻辑计算得出
    private String fullAddress; // 非数据库字段,可能通过其他表数据组合生成
    // getter 和 setter 方法
}
1
2
3
4
5
6
7
8
9
10

处理方式:

  • 数据库不存在的字段会保持默认值(对象类型为 null,基本类型为默认值)
  • 可以通过以下方式设置这些额外字段的值:
    • 在 Java 代码中查询后手动设置
    • 使用 MyBatis 的 <select> 中的表达式或函数计算
    • 通过 ResultMap 的 <constructor> 元素使用构造方法注入
    • 通过关联查询填充(<association> 或 <collection>)

# 2. 实体类字段命名与数据库字段不一致的处理

# (1) 使用列别名

最直接的方法是在 SQL 查询中使用列别名:

<select id="getUser" resultType="User">
    SELECT 
        id,
        username AS userName,
        birth_date AS birthDate,
        is_active AS active
    FROM user
</select>
1
2
3
4
5
6
7
8

# (2) 开启驼峰命名自动转换

处理下划线命名到驼峰命名的转换:

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
1
2
3

开启后自动实现以下映射:

  • user_name → userName
  • create_time → createTime

# (3) 使用 ResultMap 进行手动映射

对于复杂或不规则的命名:

<resultMap id="userMap" type="User">
    <id property="id" column="user_id"/>
    <result property="name" column="user_name"/>
    <result property="createdAt" column="create_time"/>
    <result property="status" column="user_status"/>
</resultMap>

<select id="getUser" resultMap="userMap">
    SELECT user_id, user_name, create_time, user_status FROM user
</select>
1
2
3
4
5
6
7
8
9
10

# (4) 使用 @Results 注解(注解方式)

@Select("SELECT user_id, user_name, create_time, user_status FROM user")
@Results({
    @Result(property = "id", column = "user_id"),
    @Result(property = "name", column = "user_name"),
    @Result(property = "createdAt", column = "create_time"),
    @Result(property = "status", column = "user_status")
})
User getUser();
1
2
3
4
5
6
7
8

# 3. 开发中常见的实体类映射最佳实践

# (1) 分层设计模式

在实际项目中,通常会采用分层实体类设计:

  • DO (Data Object):与数据库表结构完全对应的实体类
  • DTO (Data Transfer Object):用于服务层之间传输数据的对象
  • VO (View Object):用于展示层的视图对象
  • BO (Business Object):业务对象,包含业务逻辑的实体类
// 数据库对象 - 与表结构完全对应
public class UserDO {
    private Long id;
    private String username;
    private String password;  // 敏感信息,仅数据层使用
    private String email;
    private Date createTime;
    // getter 和 setter
}

// 数据传输对象 - 服务间传输,不包含敏感信息
public class UserDTO {
    private Long id;
    private String username;
    private String email;
    // getter 和 setter
}

// 视图对象 - 返回给前端,包含额外的展示信息
public class UserVO {
    private Long id;
    private String username;
    private String email;
    private Boolean isAdmin;
    private String lastLoginTime;  // 格式化后的时间
    // getter 和 setter
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

# (2) 查询场景的映射策略

简单查询场景:

  • 使用驼峰命名转换配置,保持实体类和数据库命名规范一致
  • 对于临时需求,可以直接使用 Map 接收查询结果
// 直接使用 Map 接收查询结果
@Select("SELECT id, username, email FROM user WHERE id = #{id}")
Map<String, Object> getUserById(@Param("id") Long id);
1
2
3

复杂查询场景:

  • 定义专用的结果实体类,只包含需要的字段
  • 使用 ResultMap 处理复杂映射关系
// 定义专用的结果类
public class UserStatistics {
    private String username;
    private Integer articleCount;  // 文章数量
    private Date lastPostTime;     // 最后发帖时间
    // getter 和 setter
}

// 映射配置
<resultMap id="userStatsMap" type="UserStatistics">
    <result property="username" column="username"/>
    <result property="articleCount" column="article_count"/>
    <result property="lastPostTime" column="last_post_time"/>
</resultMap>

<select id="getUserStatistics" resultMap="userStatsMap">
    SELECT u.username, COUNT(a.id) as article_count, MAX(a.create_time) as last_post_time
    FROM user u LEFT JOIN article a ON u.id = a.user_id
    GROUP BY u.id
</select>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

# (3) 使用 BeanUtils 或对象映射框架

对于不同实体类之间的转换,可以使用:

  • Apache BeanUtils
  • Spring BeanUtils
  • MapStruct
  • ModelMapper
// 使用 Spring BeanUtils 进行对象转换
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userDO, userVO);

// 或使用 MapStruct 定义映射接口
@Mapper
public interface UserMapper {
    UserVO doToVo(UserDO userDO);
    UserDTO doToDto(UserDO userDO);
}
1
2
3
4
5
6
7
8
9
10

# (4) 处理特殊类型的映射

枚举类型:

// 自定义 TypeHandler 处理枚举
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, UserStatus parameter, JdbcType jdbcType) throws SQLException {
        ps.setInt(i, parameter.getCode());
    }
    
    @Override
    public UserStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int code = rs.getInt(columnName);
        return UserStatus.fromCode(code);
    }
    // 其他必要方法实现...
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

JSON 数据:

// 处理 JSON 字段
public class JsonTypeHandler<T> extends BaseTypeHandler<T> {
    private Class<T> clazz;
    // 构造函数和方法实现...
}

// 在 XML 中配置
<result property="attributes" column="attributes" typeHandler="com.example.JsonTypeHandler"/>
1
2
3
4
5
6
7
8
编辑此页 (opens new window)
上次更新: 2025/03/16, 13:51:03
MyBatis 动态参数传递与模糊查询
MyBatis 的resultType 和 resultMap

← MyBatis 动态参数传递与模糊查询 MyBatis 的resultType 和 resultMap→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式