1. 什么是 MyBatis?

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的工作。

起源与发展:MyBatis 的前身是 Apache 的 iBatis 项目,2010年迁移至 Google Code 并更名为 MyBatis,2013年正式迁移至 Github。

核心思想:通过 XML 或注解来配置和映射原始类型、接口和 Java POJO (Plain Old Java Objects) 为数据库中的记录。

ORM 程度:MyBatis 属于半自动化 ORM 框架。

- O (Object):Java 实体类对象

- R (Relation):关系型数据库中的表

- M (Mapping):建立实体类属性与表字段之间的映射关系

为什么需要半自动化?

- 灵活性:对于复杂的多表关联查询、分页查询、复杂条件查询等,全自动 ORM(如 Hibernate)生成的 SQL 可能不够优化或非常复杂

- 性能:直接编写 SQL 可以更好地利用数据库的特性(如存储过程、特定函数的调用),有利于性能调优

 2. 【代理模式】下开发 Mapper 层的规则

MyBatis 的代理模式使得开发者只需定义接口,MyBatis 会动态生成其实现类,极大简化了开发。

需要遵守的规则:

1. 接口与 XML 文件同名且在同一个包下

   - `UserMapper.java` 接口与 `UserMapper.xml` 文件必须同名

   - 位于相同的包目录结构中(Maven 项目通常将 xml 文件放在 resources 的对应目录下)

2. XML 中的 namespace 配置

   - XML 文件中的 namespace 属性必须是接口的全限定名

   - 示例:`namespace="com.example.dao.UserMapper"`

3. 方法名与 SQL ID 一致

   - 接口中的方法名必须与 XML 文件中 SQL 语句的 id 属性值一致

4. 输入参数类型一致

   - 接口方法的输入参数类型必须与 XML 中 SQL 语句的 parameterType 指定类型一致

   - 使用 @Param 注解或多参数时可不指定 parameterType

5. 返回值类型一致

   - 接口方法的返回值类型必须与 XML 中 SQL 语句的 resultType(或 resultMap)指定类型一致

   - 返回集合时,resultType 指定的是集合中元素的类型

使用代理模式的好处:

- 代码更简洁:无需编写接口的实现类

- 类型安全:编译时就能发现错误

- 解耦:SQL 与 Java 代码完全分离,职责清晰

代理模式原理:MyBatis 通过 JDK 动态代理技术,在运行时为 Mapper 接口创建代理对象。当调用接口方法时,代理对象会拦截方法调用,根据方法名找到对应的 SQL 语句并执行。

 3. 常用的【动态 SQL】标签

MyBatis 提供了丰富的动态 SQL 标签来处理复杂的查询条件:

1. if:条件判断,用于动态包含 WHERE 或 SET 子句的一部分

2. choose、when、otherwise:类似于 Java 中的 switch-case-default 语句

3. trim:自定义字符串的修剪(前缀、后缀)

4. where:智能生成 WHERE 子句,自动去除开头的 AND 或 OR

5. set:智能生成 SET 子句,用于更新语句

6. foreach:遍历集合,常用于 IN 条件或批量操作

7. bind:创建变量并绑定到上下文

8. sql:定义可重用的 SQL 代码片段

9. include:引用通过 sql 定义的代码片段

10. script:在注解中使用动态 SQL

foreach 标签详解:

```xml

<select id="selectUsersByIds" resultType="User">

    SELECT  FROM user

    WHERE id IN

    <foreach collection="ids" item="id" open="(" separator="," close=")">

        {id}

    </foreach>

</select>

```

对应 Java 接口:

```java

List<User> selectUsersByIds(@Param("ids") List<Integer> idList);

```

foreach 属性:

- `collection`:要遍历的集合属性名

- `item`:每次遍历时生成的变量名

- `index`:遍历 List 或数组时的索引

- `open`:循环内容开始时的字符串

- `close`:循环内容结束时的字符串

- `separator`:每次循环之间的分隔符

动态 SQL 的好处:

- 灵活性:根据运行时条件动态构建 SQL

- 可维护性:条件判断逻辑写在 XML 中,与 Java 代码分离

- 减少代码冗余:避免字符串拼接组装 SQL

 4. 【关联查询】:多表查询

MyBatis 处理多表关联查询主要有两种方式:

 方式一:使用 ResultMap 进行关联映射

1. 编写多表联查的 SQL

2. 修改 POJO:在"一"的一方添加"多"的集合属性,在"多"的一方添加"一"的对象属性

3. 配置 ResultMap:使用 association 和 collection 标签

```xml

<resultMap id="userWithOrdersMap" type="User">

    <id property="id" column="user_id"/>

    <result property="name" column="user_name"/>

    <collection property="orders" ofType="Order">

        <id property="id" column="order_id"/>

        <result property="orderNumber" column="order_number"/>

    </collection>

</resultMap>

<select id="selectUserWithOrders" resultMap="userWithOrdersMap">

    SELECT

        u.id AS user_id, u.name AS user_name,

        o.id AS order_id, o.order_number

    FROM user u

    LEFT JOIN orders o ON u.id = o.user_id

    WHERE u.id = {userId}

</select>

```

 方式二:使用 ResultType 映射到 VO/DTO

1. 编写多表联查的 SQL

2. 创建 VO/DTO:包含所有需要查询返回的字段

3. 配置 ResultType:直接映射到自定义的 VO 类

```java

public class UserOrderVO {

    private Integer userId;

    private String userName;

    private Integer orderId;

    private String orderNumber;

    // getters & setters

}

```

```xml

<select id="selectUserOrderVO" resultType="com.example.vo.UserOrderVO">

    SELECT

        u.id AS userId, u.name AS userName,

        o.id AS orderId, o.order_number AS orderNumber

    FROM user u

    LEFT JOIN orders o ON u.id = o.user_id

    WHERE u.id = {userId}

</select>

```

对比:

- ResultMap:更面向对象,适合关系复杂的领域模型映射

- VO/ResultType:更轻量灵活,适合定制化的查询结果

 5. 延迟加载(懒加载)

概念:在关联查询时,先加载主对象,只有在程序真正需要使用关联对象时,才发起第二次 SQL 查询加载关联数据。

好处:

- 性能提升:减少不必要的数据库查询

- 降低数据库压力:加快初始查询速度

坏处:

- "N+1 查询问题":可能导致性能下降

- 调试困难:异常可能不会立即抛出

应用场景:适用于可能不需要立即加载的大对象

 6. 查询缓存

MyBatis 提供了查询缓存来提高查询速度,减少数据库压力。

其他常见的数据库优化手段:

1. 数据库连接池 (如 HikariCP, Druid)

2. 使用索引:为查询条件字段建立合适的索引

3. 优化 SQL:避免 SELECT ,使用 LIMIT 分页

4. 应用层缓存:MyBatis 的查询缓存

 MyBatis 缓存分为两级:

一级缓存:

- 范围:SqlSession 级别

- 状态:默认开启,无法关闭

- 失效时机:执行增删改操作、调用 clearCache() 或关闭 SqlSession

二级缓存:

- 范围:Mapper (Namespace) 级别

- 状态:默认关闭,需要手动配置开启

- 工作机制:SqlSession 关闭或提交后,一级缓存内容转存到二级缓存

开启二级缓存的步骤:

1. 核心配置文件开启全局开关:

   ```xml

   <settings>

       <setting name="cacheEnabled" value="true"/>

   </settings>

   ```

2. 在具体的 Mapper.xml 中配置:

   ```xml

   <cache/>

   ```

3. POJO 实现 Serializable 接口

使用第三方缓存(如 Ehcache):

1. 添加依赖:mybatis-ehcache

2. 配置使用 Ehcache:

   ```xml

   <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

   ```

3. 配置 ehcache.xml(可选)

缓存选择建议:

- 读多写少且数据实时性要求不高的场景:使用二级缓存

- 读写频繁或数据实时性要求高的场景:关闭二级缓存

 总结

MyBatis 作为一个半自动化的 ORM 框架,在灵活性和性能之间找到了良好的平衡点。通过掌握代理模式下的开发规则、动态 SQL 的使用、关联查询的处理方式以及缓存机制,可以充分发挥 MyBatis 的优势,构建高效、可维护的数据访问层。

在实际项目中,应根据具体业务需求选择合适的查询方式和缓存策略,在保证数据一致性的前提下提升系统性能。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐