MyBatis完全学习指南

目录

1. MyBatis简介

1.1 什么是MyBatis?

1.2 MyBatis架构

1.3 MyBatis与其他持久层框架对比

2. 环境搭建与配置

2.1 Maven依赖配置

2.2 项目结构

3. 核心配置文件详解

3.1 mybatis-config.xml主配置文件

3.2 database.properties配置文件

3.3 重要设置详解

4. 映射文件(Mapper)详解

4.1 基本映射文件结构

4.2 实体类示例

4.3 Mapper接口

5. 基本CRUD操作

5.1 查询操作(Select)

5.2 插入操作(Insert)

5.3 更新操作(Update)

5.4 删除操作(Delete)

5.5 使用示例

6. 参数传递详解

6.1 单个参数

6.2 多个参数

方式1:使用@Param注解(推荐)

方式2:使用Map传参

方式3:使用实体对象传参

6.3 集合参数

7. 结果映射详解

7.1 基础结果映射

自动映射(列名与属性名一致)

列名与属性名不一致的处理

7.2 自定义结果映射(ResultMap)

7.3 复杂对象映射

一对一关联

一对多关联

7.4 分步查询(延迟加载)

8. 动态SQL详解

8.1 if标签

8.2 where标签

8.3 choose、when、otherwise标签

8.4 trim标签

8.5 set标签

8.6 foreach标签

8.7 bind标签

8.8 sql标签和include标签

9. 缓存机制

9.1 一级缓存(SqlSession级别)

9.2 二级缓存(Mapper级别)

开启二级缓存

二级缓存配置详解

控制缓存行为

9.3 自定义缓存

集成EhCache

9.4 缓存最佳实践

10. 注解开发

10.1 基本CRUD注解

10.2 复杂查询注解

动态SQL

SQL提供者类

10.3 关联映射注解

一对一关联

一对多关联

10.4 注解配置缓存

10.5 混合使用XML和注解

11. 插件机制

11.1 插件工作原理

11.2 自定义插件示例

SQL执行时间监控插件

分页插件

11.3 插件注册

在mybatis-config.xml中注册

使用示例

11.4 常用开源插件

PageHelper分页插件

12. 与Spring集成

12.1 Spring配置方式

Maven依赖

Spring配置文件

12.2 Spring Boot配置方式

依赖配置

application.yml配置

启动类配置

12.3 Service层实现

12.4 Controller层实现

13. 最佳实践与常见问题

13.1 最佳实践

命名规范

性能优化

代码组织

13.2 常见问题及解决方案

问题1:参数传递问题

问题2:结果映射问题

问题3:SQL注入问题

问题4:事务失效问题

问题5:缓存问题

问题6:连接池配置问题

13.3 调试技巧

开启SQL日志

使用MyBatis日志插件

性能监控

总结

优点

适用场景

学习建议


1. MyBatis简介

1.1 什么是MyBatis?

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

核心特点:

  • 简单易学:本身就很小且简单
  • 灵活:不会对应用程序或者数据库的现有设计强加任何影响
  • 解除sql与程序代码的耦合:通过提供DAO层,将业务逻辑和数据访问逻辑分离
  • 支持动态SQL:根据不同条件拼接SQL语句

1.2 MyBatis架构

应用程序
    ↓
SqlSession (会话)
    ↓
Executor (执行器)
    ↓
StatementHandler (语句处理器)
    ↓
ParameterHandler (参数处理器) → ResultSetHandler (结果处理器)
    ↓
JDBC
    ↓
数据库

1.3 MyBatis与其他持久层框架对比

特性 MyBatis Hibernate JPA
学习难度 简单 复杂 中等
SQL控制 完全控制 部分控制 部分控制
性能优化 容易 复杂 中等
开发效率 中等

2. 环境搭建与配置

2.1 Maven依赖配置

<!-- pom.xml -->
<dependencies>
    <!-- MyBatis核心依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.13</version>
    </dependency>
    
    <!-- 数据库驱动,以MySQL为例 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    
    <!-- 日志依赖(可选) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.36</version>
    </dependency>
</dependencies>

2.2 项目结构

src/main/java/
├── com/example/
│   ├── entity/          # 实体类
│   ├── mapper/          # Mapper接口
│   ├── service/         # 业务层
│   └── utils/           # 工具类
src/main/resources/
├── mybatis-config.xml   # MyBatis主配置文件
├── mapper/              # SQL映射文件
└── log4j.properties     # 日志配置

3. 核心配置文件详解

3.1 mybatis-config.xml主配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <!-- 属性配置 -->
    <properties resource="database.properties"/>
    
    <!-- 设置 -->
    <settings>
        <!-- 开启驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 设置超时时间 -->
        <setting name="defaultStatementTimeout" value="30"/>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
    </settings>
    
    <!-- 类型别名 -->
    <typeAliases>
        <!-- 单个别名 -->
        <typeAlias type="com.example.entity.User" alias="User"/>
        <!-- 包扫描 -->
        <package name="com.example.entity"/>
    </typeAliases>
    
    <!-- 类型处理器 -->
    <typeHandlers>
        <typeHandler handler="com.example.handler.MyTypeHandler"/>
    </typeHandlers>
    
    <!-- 环境配置 -->
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
                <property name="url" value="${jdbc.url}"/>
                <property name="username" value="${jdbc.username}"/>
                <property name="password" value="${jdbc.password}"/>
            </dataSource>
        </environment>
    </environments>
    
    <!-- 映射器 -->
    <mappers>
        <!-- 使用相对于类路径的资源引用 -->
        <mapper resource="mapper/UserMapper.xml"/>
        <!-- 使用完全限定资源定位符(URL) -->
        <!-- <mapper url="file:///var/mappers/UserMapper.xml"/> -->
        <!-- 使用映射器接口实现类的完全限定类名 -->
        <!-- <mapper class="com.example.mapper.UserMapper"/> -->
        <!-- 将包内的映射器接口实现全部注册为映射器 -->
        <!-- <package name="com.example.mapper"/> -->
    </mappers>
</configuration>

3.2 database.properties配置文件

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT%2B8
jdbc.username=root
jdbc.password=123456

3.3 重要设置详解

设置名 描述 有效值 默认值
mapUnderscoreToCamelCase 开启驼峰命名转换 true/false false
lazyLoadingEnabled 延迟加载的全局开关 true/false false
cacheEnabled 全局地开启或关闭配置文件中的所有映射器已经配置的任何缓存 true/false true
defaultStatementTimeout 设置超时时间 任意正整数 未设置

4. 映射文件(Mapper)详解

4.1 基本映射文件结构

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 这里写SQL映射 -->
</mapper>

4.2 实体类示例

public class User {
    private Long id;
    private String username;
    private String password;
    private String email;
    private Date createTime;
    
    // 构造方法
    public User() {}
    
    public User(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }
    
    // getter和setter方法
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getUsername() { return username; }
    public void setUsername(String username) { this.username = username; }
    
    // ... 其他getter/setter方法
    
    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", email='" + email + '\'' +
                ", createTime=" + createTime +
                '}';
    }
}

4.3 Mapper接口

public interface UserMapper {
    // 根据ID查询用户
    User selectById(Long id);
    
    // 查询所有用户
    List<User> selectAll();
    
    // 添加用户
    int insert(User user);
    
    // 更新用户
    int update(User user);
    
    // 删除用户
    int deleteById(Long id);
    
    // 根据用户名查询
    User selectByUsername(String username);
    
    // 分页查询
    List<User> selectByPage(@Param("offset") int offset, @Param("limit") int limit);
}

5. 基本CRUD操作

5.1 查询操作(Select)

<!-- 根据ID查询用户 -->
<select id="selectById" resultType="User">
    SELECT id, username, password, email, create_time
    FROM user 
    WHERE id = #{id}
</select>

<!-- 查询所有用户 -->
<select id="selectAll" resultType="User">
    SELECT id, username, password, email, create_time
    FROM user
    ORDER BY create_time DESC
</select>

<!-- 根据用户名查询 -->
<select id="selectByUsername" resultType="User">
    SELECT id, username, password, email, create_time
    FROM user 
    WHERE username = #{username}
</select>

<!-- 分页查询 -->
<select id="selectByPage" resultType="User">
    SELECT id, username, password, email, create_time
    FROM user
    ORDER BY create_time DESC
    LIMIT #{offset}, #{limit}
</select>

5.2 插入操作(Insert)

<!-- 插入用户 -->
<insert id="insert" parameterType="User">
    INSERT INTO user(username, password, email, create_time)
    VALUES(#{username}, #{password}, #{email}, NOW())
</insert>

<!-- 插入用户并返回主键 -->
<insert id="insertAndReturnKey" parameterType="User" useGeneratedKeys="true" keyProperty="id">
    INSERT INTO user(username, password, email, create_time)
    VALUES(#{username}, #{password}, #{email}, NOW())
</insert>

<!-- 批量插入 -->
<insert id="insertBatch" parameterType="list">
    INSERT INTO user(username, password, email, create_time)
    VALUES
    <foreach collection="list" item="user" separator=",">
        (#{user.username}, #{user.password}, #{user.email}, NOW())
    </foreach>
</insert>

5.3 更新操作(Update)

<!-- 更新用户 -->
<update id="update" parameterType="User">
    UPDATE user 
    SET username = #{username},
        password = #{password},
        email = #{email}
    WHERE id = #{id}
</update>

<!-- 动态更新(只更新不为空的字段) -->
<update id="updateSelective" parameterType="User">
    UPDATE user
    <set>
        <if test="username != null and username != ''">
            username = #{username},
        </if>
        <if test="password != null and password != ''">
            password = #{password},
        </if>
        <if test="email != null and email != ''">
            email = #{email},
        </if>
    </set>
    WHERE id = #{id}
</update>

5.4 删除操作(Delete)

<!-- 根据ID删除用户 -->
<delete id="deleteById" parameterType="long">
    DELETE FROM user WHERE id = #{id}
</delete>

<!-- 批量删除 -->
<delete id="deleteBatch" parameterType="list">
    DELETE FROM user WHERE id IN
    <foreach collection="list" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

<!-- 根据条件删除 -->
<delete id="deleteByCondition">
    DELETE FROM user 
    WHERE create_time &lt; #{beforeDate}
</delete>

5.5 使用示例

public class UserService {
    private SqlSession sqlSession;
    private UserMapper userMapper;
    
    public UserService() {
        // 获取SqlSession
        sqlSession = MyBatisUtils.getSqlSession();
        userMapper = sqlSession.getMapper(UserMapper.class);
    }
    
    // 查询用户
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
    
    // 添加用户
    public boolean addUser(User user) {
        try {
            int result = userMapper.insert(user);
            sqlSession.commit(); // 提交事务
            return result > 0;
        } catch (Exception e) {
            sqlSession.rollback(); // 回滚事务
            e.printStackTrace();
            return false;
        }
    }
    
    // 关闭资源
    public void close() {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }
}

6. 参数传递详解

6.1 单个参数

<!-- 基本类型参数 -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

<!-- 字符串参数 -->
<select id="selectByUsername" resultType="User">
    SELECT * FROM user WHERE username = #{username}
</select>
// 接口方法
User selectById(Long id);
User selectByUsername(String username);

6.2 多个参数

方式1:使用@Param注解(推荐)
// 接口方法
List<User> selectByUsernameAndEmail(@Param("username") String username, 
                                   @Param("email") String email);
<select id="selectByUsernameAndEmail" resultType="User">
    SELECT * FROM user 
    WHERE username = #{username} AND email = #{email}
</select>
方式2:使用Map传参
// 接口方法
List<User> selectByMap(Map<String, Object> params);
<select id="selectByMap" resultType="User">
    SELECT * FROM user 
    WHERE username = #{username} AND email = #{email}
</select>
// 使用示例
Map<String, Object> params = new HashMap<>();
params.put("username", "admin");
params.put("email", "[email protected]");
List<User> users = userMapper.selectByMap(params);
方式3:使用实体对象传参
// 查询条件实体
public class UserQuery {
    private String username;
    private String email;
    private Date startDate;
    private Date endDate;
    // getter/setter...
}

// 接口方法
List<User> selectByQuery(UserQuery query);
<select id="selectByQuery" resultType="User">
    SELECT * FROM user 
    WHERE 1=1
    <if test="username != null and username != ''">
        AND username LIKE CONCAT('%', #{username}, '%')
    </if>
    <if test="email != null and email != ''">
        AND email = #{email}
    </if>
    <if test="startDate != null">
        AND create_time >= #{startDate}
    </if>
    <if test="endDate != null">
        AND create_time &lt;= #{endDate}
    </if>
</select>

6.3 集合参数

// 接口方法
List<User> selectByIds(@Param("ids") List<Long> ids);
int deleteBatch(@Param("ids") List<Long> ids);
<!-- 根据ID列表查询 -->
<select id="selectByIds" resultType="User">
    SELECT * FROM user WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

<!-- 批量删除 -->
<delete id="deleteBatch">
    DELETE FROM user WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</delete>

7. 结果映射详解

7.1 基础结果映射

自动映射(列名与属性名一致)
<select id="selectById" resultType="User">
    SELECT id, username, password, email FROM user WHERE id = #{id}
</select>
列名与属性名不一致的处理

方式1:使用别名

<select id="selectById" resultType="User">
    SELECT 
        id,
        username,
        password,
        email,
        create_time AS createTime
    FROM user 
    WHERE id = #{id}
</select>

方式2:开启驼峰命名转换

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值