全部笔记All notes

Web程序设计笔记10——第六章:初识MyBatis

阅读 12m 15s12m 15s read

MyBatis 入门完整指南

概述

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO 为数据库中的记录。

💡 核心优势:

  • 灵活性高:完全控制 SQL 语句,便于优化
  • 学习成本低:SQL 与 Java 代码分离,易于理解
  • 映射灵活:支持复杂的结果集映射
  • 缓存机制:一级缓存和二级缓存提升性能

MyBatis 简介

1. 框架定位

在典型的 SSM(Spring + SpringMVC + MyBatis)架构中:

  • Spring:负责业务层逻辑和依赖注入
  • SpringMVC:负责表现层,处理 HTTP 请求
  • MyBatis:负责持久层,处理数据库操作

2. ORM 概念

ORM(Object Relational Mapping):对象关系映射,是一种程序设计技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。

数据库概念Java 概念MyBatis 映射
表(Table)类(Class)<resultMap>
行(Row)对象(Object)实例
列(Column)属性(Field)<result>

3. 相关概念

  • POJO(Plain Old Java Object):普通的 Java 对象,没有任何限制
  • PO(Persistent Object):持久化对象,对应数据库中的记录
  • VO(Value Object):值对象,用于业务层之间的数据传输
  • DTO(Data Transfer Object):数据传输对象,用于层间数据传输

环境搭建

1. 数据库准备

-- 创建数据库
CREATE DATABASE mybatis_demo CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;

USE mybatis_demo;

-- 创建客户表
CREATE TABLE customer (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL COMMENT '客户名称',
    jobs VARCHAR(50) COMMENT '职业',
    phone VARCHAR(20) COMMENT '电话',
    created_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户信息表';

-- 插入测试数据
INSERT INTO customer (username, jobs, phone) VALUES 
('张三', '软件工程师', '13800138001'),
('李四', '产品经理', '13800138002'),
('王五', 'UI设计师', '13800138003'),
('赵六', '测试工程师', '13800138004');

2. Maven 依赖配置

<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>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    
    <!-- 单元测试 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
    
    <!-- Lombok(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.28</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

3. 项目结构

mybatis-demo/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/example/
│   │   │       ├── entity/          # 实体类
│   │   │       ├── mapper/          # Mapper接口
│   │   │       └── utils/           # 工具类
│   │   └── resources/
│   │       ├── mapper/              # Mapper XML文件
│   │       ├── mybatis-config.xml   # MyBatis核心配置
│   │       ├── db.properties        # 数据库配置
│   │       └── log4j.properties     # 日志配置
│   └── test/
└── pom.xml

核心组件

1. SqlSessionFactory

SqlSessionFactory 是 MyBatis 的核心对象,用于创建 SqlSession。

public class MyBatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    
    static {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }
}

2. SqlSession

SqlSession 代表和数据库的一次会话,用于执行 SQL 命令。

方法说明使用场景
selectOne()查询单条记录根据 ID 查询
selectList()查询多条记录列表查询
insert()插入记录新增数据
update()更新记录修改数据
delete()删除记录删除数据
commit()提交事务确认更改
rollback()回滚事务撤销更改

3. Mapper

Mapper 是 MyBatis 的映射器,负责 SQL 语句与 Java 方法的映射。

入门示例

基于 MyBatis 的客户管理系统

1. 实体类定义
package com.example.entity;

import lombok.Data;
import java.util.Date;

@Data
public class Customer {
    private Integer id;          // 客户ID
    private String username;     // 客户名称  
    private String jobs;         // 职业
    private String phone;        // 电话
    private Date createdTime;    // 创建时间
    private Date updatedTime;    // 更新时间
}
2. Mapper 接口
package com.example.mapper;

import com.example.entity.Customer;
import java.util.List;

public interface CustomerMapper {
    
    // 1. 根据id查询客户
    Customer findCustomerById(Integer id);
    
    // 2. 根据姓名模糊查询
    List<Customer> findCustomerByName(String username);
    
    // 3. 添加客户
    int addCustomer(Customer customer);
    
    // 4. 更新客户
    int updateCustomer(Customer customer);
    
    // 5. 删除客户
    int deleteCustomer(Integer id);
    
    // 6. 查询所有客户
    List<Customer> findAllCustomers();
    
    // 7. 分页查询
    List<Customer> findCustomersByPage(Map<String, Object> params);
    
    // 8. 统计客户数量
    Long countCustomers();
}
3. Mapper XML 配置
<?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.CustomerMapper">
    
    <!-- 结果映射配置 -->
    <resultMap id="customerMap" type="com.example.entity.Customer">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="jobs" column="jobs"/>
        <result property="phone" column="phone"/>
        <result property="createdTime" column="created_time"/>
        <result property="updatedTime" column="updated_time"/>
    </resultMap>
    
    <!-- 1. 根据id查询客户 -->
    <select id="findCustomerById" parameterType="Integer" 
            resultMap="customerMap">
        SELECT * FROM customer WHERE id = #{id}
    </select>
    
    <!-- 2. 根据姓名模糊查询 -->
    <select id="findCustomerByName" parameterType="String" 
            resultMap="customerMap">
        SELECT * FROM customer 
        WHERE username LIKE CONCAT('%', #{username}, '%')
    </select>
    
    <!-- 3. 添加客户 -->
    <insert id="addCustomer" parameterType="com.example.entity.Customer"
            useGeneratedKeys="true" keyProperty="id">
        INSERT INTO customer (username, jobs, phone) 
        VALUES (#{username}, #{jobs}, #{phone})
    </insert>
    
    <!-- 4. 更新客户 -->
    <update id="updateCustomer" parameterType="com.example.entity.Customer">
        UPDATE customer 
        SET username = #{username},
            jobs = #{jobs},
            phone = #{phone},
            updated_time = NOW()
        WHERE id = #{id}
    </update>
    
    <!-- 5. 删除客户 -->
    <delete id="deleteCustomer" parameterType="Integer">
        DELETE FROM customer WHERE id = #{id}
    </delete>
    
    <!-- 6. 查询所有客户 -->
    <select id="findAllCustomers" resultMap="customerMap">
        SELECT * FROM customer ORDER BY id DESC
    </select>
    
    <!-- 7. 分页查询 -->
    <select id="findCustomersByPage" parameterType="Map" 
            resultMap="customerMap">
        SELECT * FROM customer 
        ORDER BY id DESC
        LIMIT #{offset}, #{pageSize}
    </select>
    
    <!-- 8. 统计客户数量 -->
    <select id="countCustomers" resultType="Long">
        SELECT COUNT(*) FROM customer
    </select>
    
</mapper>
4. MyBatis 核心配置
<?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="db.properties"/>
    
    <!-- 全局设置 -->
    <settings>
        <!-- 开启驼峰命名自动映射 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!-- 开启延迟加载 -->
        <setting name="lazyLoadingEnabled" value="true"/>
        <!-- 开启二级缓存 -->
        <setting name="cacheEnabled" value="true"/>
        <!-- 打印SQL语句 -->
        <setting name="logImpl" value="LOG4J"/>
    </settings>
    
    <!-- 类型别名 -->
    <typeAliases>
        <!-- 单个类型别名 -->
        <typeAlias type="com.example.entity.Customer" alias="Customer"/>
        <!-- 包扫描别名 -->
        <package name="com.example.entity"/>
    </typeAliases>
    
    <!-- 数据库环境 -->
    <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>
    
    <!-- Mapper 映射文件 -->
    <mappers>
        <!-- 单个映射文件 -->
        <mapper resource="mapper/CustomerMapper.xml"/>
        <!-- 包扫描 -->
        <!-- <package name="com.example.mapper"/> -->
    </mappers>
    
</configuration>
5. 数据库配置文件
# db.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_demo?useSSL=false&serverTimezone=UTC&characterEncoding=utf8
jdbc.username=root
jdbc.password=password
6. 日志配置
# log4j.properties
log4j.rootLogger=DEBUG, Console

# Console
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.layout=org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n

# MyBatis SQL 输出
log4j.logger.com.example.mapper=DEBUG
(2)导入依赖:mybatis、mysql-connector-java
  <dependencies>
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.2</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.28</version>
<!--      版本号与自己mysql版本一致-->
    </dependency>
  </dependencies>
(3)创建持久化类(实体类)

作用:和表进行数据转换 — 增:实体类对象 转换 表中的记录 — 查:表中的记录 转换 实体类对象 注意:成员变量的名字、数据类型 需要和 表中字段的名字、数据类型 保持一致

生成setter、getter 和 toString()方法

package com.owlbay.po;

public class Customer {
    private int id;
    private String username;
    private String jobs;
    private String phone;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getJobs() {
        return jobs;
    }

    public void setJobs(String jobs) {
        this.jobs = jobs;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", jobs='" + jobs + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}
(4)在resources文件夹下创建CustomerMapper.xml文件,用来写sql语句
1.首先创建模板

在 file (文件)中的 setting (设置)中搜索 File and Code Templates (文件和代码模板),在其中首页 Files (文件)下创建文件 Mapper.xml,然后在下面填入以下代码,选择 OK (确定) 退出

<?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="">

</mapper>

image-20220405131535640

2.创建CustomerMapper.xml文件

在 resources 文件夹右击选择 new(新建)Mapper

image-20220405132203804

3.写sql语句
1.namespace属性的值:copy path(复制路径/引用)中 file name(文件名),去掉.xml
    
<mapper namespace="CustomerMapper">

2.#{id}占位符
3.parameterType是来设置占位符对应的参数的数据类型
4.resultType用来设置接收查询结果的持久化类,注意的是resultType属性值是全类名(全限定名)
    
<select id="findCustomerById" parameterType="int"resultType="com.owlbay.po.Customer">
   select * from t_customer where id=#{id}
</select>

image-20220405132451651

(5)在resources文件夹下创建mybatis-config.xml文件,该文件是MyBatis框架的配置文件
1.首先创建模板

在 file (文件)中的 setting (设置)中搜索 File and Code Templates (文件和代码模板),在其中首页 Files (文件)下创建文件 mybatis-config.xml,然后在下面填入以下代码,选择 OK (确定) 退出

<?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>
 
</configuration>

image-20220405131954150

2.创建mybatis-config.xml文件

在 resources 文件夹右击选择 new(新建)mybatis-config

image-20220405132528897

3.配置MyBatis框架
<configuration>
<!--      1.连接数据库-->
      <environments default="mysql">
            <environment id="mysql">
                  <transactionManager type="JDBC"></transactionManager>
                  <dataSource type="POOLED">
<!--                        四个成员变量不能错-->
                        <property name="driver" value="com.mysql.jdbc.Driver"/>
                        <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
                        <property name="username" value="root"/>
                        <property name="password" value="8520"/>
                  </dataSource>
            </environment>
      </environments>
<!--      2.加载Mapper文件-->
      <mappers>
<!--            resource 从 copy path(复制路径/引用)中 file name(文件名)-->
            <mapper resource="CustomerMapper.xml"/>
      </mappers>

</configuration>

image-20220405132915669

(6)单元测试:查询

在 com.owlbay.po 包下创建测试类MybatisTest

1.加载配置文件和 Mapper 文件

通过 CustomerMapper.xml 文件中的 select 的 id 的值来命名测试类

image-20220405133745512

在输入 Resources 时通过提示导入名为 org 开头的包

image-20220405134057509

在此输入完此行代码后 getResourceAsStream 会爆红,不用管后续处理

InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
2.构建会话工厂
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
3.构建会话 SqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();

因为要测试多个方法,所以将以上构造剪切到成员变量

image-20220405134848712

4.处理爆红错误

将鼠标放置到爆红的 resourceAsStream 处 Alt+回车 处理问题,选择添加类默认构造函数签名的异常

image-20220405134938312

5.导包 Test ,并写完查询类

selectOne()中的参数 = CustomerMapper.xml文件中 namespace + select id

MybatisTest.java 总体代码如下:

package com.owlbay.po;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

public class MybatisTest {
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    SqlSession sqlSession = sqlSessionFactory.openSession();

    public MybatisTest() throws IOException {
    }

    @Test
    public void findCustomerById(){
//        s: = CustomerMapper.xml文件中 namespace + select id
        Customer o = sqlSession.selectOne("CustomerMapper.findCustomerById", 1);
        System.out.println(o);
    }
}

运行结果如下:

image-20220405135447683

1.根据id查询客户

<select id="findCustomerById" parameterType="int" resultType="com.owlbay.po.Customer">
    select * from t_customer where id=#{id}
</select>
@Test
public void findCustomerById() {
   Customer o = sqlSession.selectOne("com.CustomerMapper.findCustomerById", 1);
   System.out.println(o);
}

image-20220407152029547

2.根据姓名模糊查询

<select id="findCustomerByName" parameterType="String" resultType="com.owlbay.po.Customer">
    select * from t_customer where username like '%${value}%'
</select>
@Test
public void findCustomerByName() {
    List<Customer> o = sqlSession.selectList("CustomerMapper.findCustomerByName", "j");
    for (Customer customer : o){
        System.out.println(customer);
    }
}

image-20220407152044583

3.添加客户

<insert id="addCustomer" parameterType="com.owlbay.po.Customer">
    insert  into t_customer values (#{id},#{username},#{jobs},#{phone})
</insert>
@Test
public void addCustomer(){
    Customer customer = new Customer();
    customer.setId(1);
    customer.setUsername("zhangsan");
    customer.setJobs("student");
    customer.setPhone("110");
    sqlSession.insert("CustomerMapper.addCustomer",customer);
    sqlSession.commit();//只要更改数据库中的数据,就需要调用该方法完成数据的
    sqlSession.close();//关闭数据库的连接
}

image-20220407152231144

4.更新客户

<update id="updateCustomer" parameterType="com.owlbay.po.Customer">
    update t_customer set username = #{username},jobs = #{jobs},phone = #{phone} where id = #{id}
</update>
@Test
public void updateCustomer() {
    Customer customer = new Customer();
    customer.setId(4);
    customer.setUsername("zhangsan");
    customer.setJobs("teacher");
    customer.setPhone("18888888888");
    sqlSession.update("CustomerMapper.updateCustomer", customer);
    sqlSession.commit();
    sqlSession.close();
}

image-20220407152334704

5.删除客户

<delete id="deleteCustomer" parameterType="int">
    delete from t_customer where id = #{id}
</delete>
@Test
public void deleteCustomer() {
    sqlSession.delete("CustomerMapper.deleteCustomer", 1);
    sqlSession.commit();
    sqlSession.close();
}

image-20220407152346163

如何在控制台输入SQL语句?

(1) 导包 log4j

<dependency>
      <groupId>log4j</groupId>
      <artifactId>log4j</artifactId>
      <version>1.2.17</version>
</dependency>

(2) 复制log4j.properties文件到resources.文件夹下

(3) resources下新建directory,名为com

(4) 将CustomerMapper.xml文件复制到com下

image-20220407152650898

(5) 复制后的CustomerMapper.Xml中的namespacel的值修改:com.CustomerMapper

image-20220407152719404

(6) 修改mybatis-config.xml文件中resource属性的值:

image-20220407152735127

(7) 修改测试方法中的值:

image-20220407152802930

运行测试结果如下:

image-20220407152822914

配置详解

1. MyBatis 配置文件结构

MyBatis 配置文件的元素顺序必须遵循以下顺序:

configuration(配置)
  ├─ properties(属性)
  ├─ settings(设置)
  ├─ typeAliases(类型别名)
  ├─ typeHandlers(类型处理器)
  ├─ objectFactory(对象工厂)
  ├─ plugins(插件)
  ├─ environments(环境配置)
  │   ├─ environment(环境变量)
  │       ├─ transactionManager(事务管理器)
  │       └─ dataSource(数据源)
  └─ mappers(映射器)

2. 重要设置项

设置项说明默认值
cacheEnabled全局二级缓存开关true
lazyLoadingEnabled延迟加载开关false
mapUnderscoreToCamelCase驼峰命名自动映射false
logImpl日志实现未设置
defaultExecutorType执行器类型SIMPLE

映射器开发

1. 注解方式

public interface CustomerMapper {
    
    @Select("SELECT * FROM customer WHERE id = #{id}")
    Customer findCustomerById(Integer id);
    
    @Insert("INSERT INTO customer(username, jobs, phone) VALUES(#{username}, #{jobs}, #{phone})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int addCustomer(Customer customer);
    
    @Update("UPDATE customer SET username=#{username}, jobs=#{jobs}, phone=#{phone} WHERE id=#{id}")
    int updateCustomer(Customer customer);
    
    @Delete("DELETE FROM customer WHERE id=#{id}")
    int deleteCustomer(Integer id);
    
    // 复杂查询建议使用 XML 配置
    @Select({
        "<script>",
        "SELECT * FROM customer",
        "<where>",
        "  <if test='username != null'>",
        "    AND username LIKE CONCAT('%', #{username}, '%')",
        "  </if>",
        "  <if test='jobs != null'>",  
        "    AND jobs = #{jobs}",
        "  </if>",
        "</where>",
        "</script>"
    })
    List<Customer> findCustomersByCondition(@Param("username") String username, 
                                           @Param("jobs") String jobs);
}

2. 测试类实现

public class CustomerMapperTest {
    
    private SqlSession sqlSession;
    private CustomerMapper customerMapper;
    
    @Before
    public void setUp() {
        sqlSession = MyBatisUtils.getSqlSession();
        customerMapper = sqlSession.getMapper(CustomerMapper.class);
    }
    
    @After
    public void tearDown() {
        sqlSession.close();
    }
    
    @Test
    public void testFindCustomerById() {
        Customer customer = customerMapper.findCustomerById(1);
        System.out.println(customer);
    }
    
    @Test
    public void testAddCustomer() {
        Customer customer = new Customer();
        customer.setUsername("张三");
        customer.setJobs("程序员");
        customer.setPhone("13900139000");
        
        int rows = customerMapper.addCustomer(customer);
        sqlSession.commit();
        
        System.out.println("影响行数:" + rows);
        System.out.println("新增ID:" + customer.getId());
    }
    
    @Test
    public void testUpdateCustomer() {
        Customer customer = customerMapper.findCustomerById(1);
        customer.setUsername("李四");
        customer.setJobs("高级工程师");
        
        int rows = customerMapper.updateCustomer(customer);
        sqlSession.commit();
        
        System.out.println("更新行数:" + rows);
    }
}

动态 SQL

1. if 标签

<select id="findCustomersByCondition" resultMap="customerMap">
    SELECT * FROM customer
    WHERE 1=1
    <if test="username != null and username != ''">
        AND username LIKE CONCAT('%', #{username}, '%')
    </if>
    <if test="jobs != null and jobs != ''">
        AND jobs = #{jobs}
    </if>
</select>

2. where 标签

<select id="findCustomersByCondition" resultMap="customerMap">
    SELECT * FROM customer
    <where>
        <if test="username != null and username != ''">
            AND username LIKE CONCAT('%', #{username}, '%')
        </if>
        <if test="jobs != null and jobs != ''">
            AND jobs = #{jobs}
        </if>
    </where>
</select>

3. choose-when-otherwise

<select id="findCustomersByChoose" resultMap="customerMap">
    SELECT * FROM customer
    <where>
        <choose>
            <when test="username != null">
                username = #{username}
            </when>
            <when test="jobs != null">
                jobs = #{jobs}
            </when>
            <otherwise>
                1=1
            </otherwise>
        </choose>
    </where>
</select>

4. foreach 标签

<select id="findCustomersByIds" resultMap="customerMap">
    SELECT * FROM customer
    WHERE id IN
    <foreach collection="ids" item="id" open="(" separator="," close=")">
        #{id}
    </foreach>
</select>

最佳实践

1. 配置优化

<settings>
    <!-- 开启驼峰命名转换 -->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <!-- 开启延迟加载 -->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!-- 积极加载关闭 -->
    <setting name="aggressiveLazyLoading" value="false"/>
    <!-- 开启二级缓存 -->
    <setting name="cacheEnabled" value="true"/>
</settings>

2. 性能优化

  • 使用缓存:合理配置一级和二级缓存
  • 批量操作:使用 batch 执行器
  • 延迟加载:避免 N+1 查询问题
  • SQL 优化:使用索引,避免全表扫描

3. 开发建议

  • 简单 SQL 使用注解,复杂 SQL 使用 XML
  • 利用 IDE 插件:MyBatis Plugin 等
  • 代码生成器:MyBatis Generator
  • 日志调试:开启 SQL 日志输出

常见问题

问题 1:TypeException

错误信息:TypeException: Could not resolve type alias

解决方案:

  1. 检查类型别名配置是否正确
  2. 确认类路径存在
  3. 使用全限定类名

问题 2:BindingException

错误信息:BindingException: Invalid bound statement

解决方案:

  1. 检查 namespace 和接口全限定名是否一致
  2. 检查方法名和 id 是否匹配
  3. 确认 mapper.xml 已正确加载

问题 3:中文乱码

解决方案:

jdbc.url=jdbc:mysql://localhost:3306/db?characterEncoding=utf8&useUnicode=true

问题 4:事务不生效

解决方案:

// 手动提交事务
sqlSession.commit();

// 或者开启自动提交
SqlSession sqlSession = sqlSessionFactory.openSession(true);

总结

MyBatis 作为一个半自动化的 ORM 框架,具有以下特点:

🎯 核心优势

  1. SQL 控制力强:完全掌控 SQL 语句,便于优化
  2. 简单易学:XML 和注解两种配置方式
  3. 动态 SQL:强大的动态 SQL 生成能力
  4. 二级缓存:提升查询性能

📊 适用场景

  • 需求变化频繁:SQL 语句需要经常调整
  • 性能要求高:需要精细优化 SQL
  • 复杂查询多:大量的联表查询和统计
  • 团队 SQL 能力强:DBA 参与开发

🚀 学习路线

  1. 基础使用:配置、CRUD 操作
  2. 动态 SQL:if、where、foreach 等标签
  3. 关系映射:一对一、一对多、多对多
  4. 性能优化:缓存、延迟加载、批处理
  5. 框架整合:Spring + MyBatis 集成

相关文章

前后章节导航