MyBatis基础详解

O泡李华 10

MyBatis 基础详解

本章位置:第二阶段 Java 核心框架
前置知识:JavaSE、JDBC、DAO 思想、配置文件、JavaBean、反射
下一篇:Servlet
学习目标:掌握 MyBatis 项目搭建、核心配置、Mapper 接口、Mapper XML、CRUD、参数传递、结果映射、日志、事务与常见报错。


一、MyBatis 是什么

MyBatis 是一个 Java 持久层框架,主要帮助我们:

减少 JDBC 重复代码
自动绑定 SQL 参数
自动封装查询结果
使用 Mapper 接口操作数据库
把 SQL 从 Java 代码中分离出来

它底层仍然使用 JDBC:

Java
↓
MyBatis
↓
JDBC
↓
MySQL Driver
↓
MySQL

所以学 MyBatis 并不是抛弃 JDBC,而是让框架帮我们封装 JDBC 模板代码。


二、为什么需要 MyBatis

JDBC 查询通常要自己完成:

获取 Connection
创建 PreparedStatement
绑定参数
执行 SQL
遍历 ResultSet
创建 JavaBean
给属性赋值
关闭资源
处理异常

而 MyBatis 让我们把重点放到:

SQL
参数
结果映射

例如 JDBC:

PreparedStatement ps =
        connection.prepareStatement(
                "SELECT id,name FROM student WHERE id=?"
        );

ps.setLong(
        1,
        id
);

ResultSet rs =
        ps.executeQuery();

MyBatis:

Student student =
        studentMapper.findById(
                id
        );

三、MyBatis 核心对象

初学阶段先记住:

SqlSessionFactoryBuilder

SqlSessionFactory

SqlSession

Mapper

关系:

mybatis-config.xml
↓
SqlSessionFactoryBuilder
↓
SqlSessionFactory
↓
SqlSession
↓
Mapper
↓
SQL
↓
数据库

四、SqlSessionFactoryBuilder

负责:

读取配置
创建 SqlSessionFactory

通常只在应用初始化阶段使用。


五、SqlSessionFactory

负责:

创建 SqlSession

一般整个应用复用一个:

SqlSessionFactory

不要每次查询都重新 build。


六、SqlSession

SqlSession 可以理解为:

一次数据库会话

主要负责:

获取 Mapper
执行 SQL
提交事务
回滚事务

七、Mapper

Mapper 是数据库访问接口。

例如:

public interface StudentMapper {

    Student findById(
            Long id
    );
}

正常情况下不用写:

StudentMapperImpl

MyBatis 会动态生成代理对象。


八、为什么 Mapper 没有实现类还能运行

StudentMapper mapper =
        sqlSession.getMapper(
                StudentMapper.class
        );

这里得到的是:

MyBatis 代理对象

调用:

mapper.findById(
        1L
);

MyBatis 会:

找到对应 SQL
绑定参数
执行 JDBC
封装结果
返回 Student

九、创建 Maven 项目

IDEA:

File
→ New
→ Project
→ Maven

建议:

JDK 17

项目名:

mybatis-demo

十、推荐目录结构

mybatis-demo
│
├─ pom.xml
│
└─ src
   └─ main
      ├─ java
      │  └─ com.example.mybatis
      │     ├─ entity
      │     │  └─ Student.java
      │     ├─ mapper
      │     │  └─ StudentMapper.java
      │     ├─ util
      │     │  └─ MyBatisUtil.java
      │     └─ MyBatisDemo.java
      │
      └─ resources
         ├─ jdbc.properties
         ├─ mybatis-config.xml
         └─ mapper
            └─ StudentMapper.xml

十一、Maven 依赖

<dependencies>

    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.19</version>
    </dependency>

    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.4.0</version>
    </dependency>

</dependencies>

版本不是学习重点,项目已有兼容版本时不必机械修改。


十二、创建数据库

CREATE DATABASE IF NOT EXISTS mybatis_demo
CHARACTER SET utf8mb4;

使用:

USE mybatis_demo;

创建表:

CREATE TABLE student (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    student_no VARCHAR(30) NOT NULL UNIQUE,
    name VARCHAR(50) NOT NULL,
    age INT,
    major VARCHAR(100),
    score DECIMAL(5,2)
);

测试数据:

INSERT INTO student
(
    student_no,
    name,
    age,
    major,
    score
)
VALUES
(
    '20260001',
    '张三',
    20,
    '软件工程',
    90.50
),
(
    '20260002',
    '李四',
    21,
    '计算机科学与技术',
    85.00
);

十三、Student 实体类

package com.example.mybatis.entity;

import java.math.BigDecimal;

public class Student {

    private Long id;

    private String studentNo;

    private String name;

    private Integer age;

    private String major;

    private BigDecimal score;

    public Student() {

    }

    public Long getId() {
        return id;
    }

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

    public String getStudentNo() {
        return studentNo;
    }

    public void setStudentNo(
            String studentNo
    ) {
        this.studentNo = studentNo;
    }

    public String getName() {
        return name;
    }

    public void setName(
            String name
    ) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(
            Integer age
    ) {
        this.age = age;
    }

    public String getMajor() {
        return major;
    }

    public void setMajor(
            String major
    ) {
        this.major = major;
    }

    public BigDecimal getScore() {
        return score;
    }

    public void setScore(
            BigDecimal score
    ) {
        this.score = score;
    }

    @Override
    public String toString() {
        return "Student{"
                + "id=" + id
                + ", studentNo='" + studentNo + '\''
                + ", name='" + name + '\''
                + ", age=" + age
                + ", major='" + major + '\''
                + ", score=" + score
                + '}';
    }
}

MyBatis 经常通过反射创建对象,所以传统 JavaBean 风格:

无参构造
Getter
Setter

非常常见。


十四、jdbc.properties

位置:

src/main/resources/jdbc.properties

内容:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_demo
jdbc.username=root
jdbc.password=123456

把账号密码改成自己的实际配置。


十五、mybatis-config.xml

位置:

src/main/resources/mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>

    <properties
            resource="jdbc.properties"/>

    <settings>

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

        <setting
                name="logImpl"
                value="STDOUT_LOGGING"/>

    </settings>

    <typeAliases>

        <package
                name="com.example.mybatis.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>

    <mappers>

        <mapper
                resource="mapper/StudentMapper.xml"/>

    </mappers>

</configuration>

十六、配置标签顺序

MyBatis 核心配置标签有固定顺序,常见:

properties
settings
typeAliases
typeHandlers
objectFactory
plugins
environments
databaseIdProvider
mappers

顺序错了可能导致 XML 校验失败。


十七、properties

<properties
        resource="jdbc.properties"/>

负责加载外部配置。

后面:

${jdbc.url}

就是读取对应属性。


十八、settings

用于调整 MyBatis 全局行为。

例如:

驼峰映射
日志
缓存
懒加载

十九、驼峰映射

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

可以把:

student_no
→ studentNo

create_time
→ createTime

这是实际项目非常常见的配置。


二十、日志

<setting
        name="logImpl"
        value="STDOUT_LOGGING"/>

学习阶段可以看到:

Preparing
Parameters
Total

非常适合排查 SQL。


二十一、typeAliases

<typeAliases>

    <package
            name="com.example.mybatis.entity"/>

</typeAliases>

配置后 XML 可以写:

resultType="Student"

而不用每次写全限定名。


二十二、environments

用于配置:

数据库运行环境

例如:

development
test
production

二十三、transactionManager

<transactionManager
        type="JDBC"/>

表示使用 JDBC 事务管理。

后面整合 Spring 后,事务一般由 Spring 接管。


二十四、dataSource

<dataSource
        type="POOLED">

表示使用 MyBatis 自带简单连接池。

常见:

UNPOOLED
POOLED
JNDI

二十五、创建 StudentMapper

package com.example.mybatis.mapper;

import com.example.mybatis.entity.Student;

import java.util.List;

public interface StudentMapper {

    Student findById(
            Long id
    );

    List<Student> findAll();

    int insert(
            Student student
    );

    int update(
            Student student
    );

    int deleteById(
            Long id
    );
}

二十六、创建 Mapper XML

位置:

src/main/resources/mapper/StudentMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "https://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper
        namespace="com.example.mybatis.mapper.StudentMapper">

</mapper>

二十七、namespace

namespace="com.example.mybatis.mapper.StudentMapper"

必须对应:

Mapper 接口全限定名

可以记:

namespace → Mapper 接口

二十八、id

例如:

<select
        id="findById">

id 对应:

findById(...)

所以:

id → Mapper 方法名

二十九、查询单个学生

<select
        id="findById"
        resultType="Student">

    SELECT
        id,
        student_no,
        name,
        age,
        major,
        score
    FROM student
    WHERE id = #{id}

</select>

三十、#{id}

#{} 是 MyBatis 最重要的参数绑定语法。

WHERE id = #{id}

底层类似:

WHERE id = ?

再由 PreparedStatement 绑定值。


三十一、第一个运行程序

package com.example.mybatis;

import com.example.mybatis.entity.Student;
import com.example.mybatis.mapper.StudentMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;

import java.io.InputStream;

public class MyBatisDemo {

    public static void main(
            String[] args
    )
            throws Exception {

        InputStream input =
                Resources
                        .getResourceAsStream(
                                "mybatis-config.xml"
                        );

        SqlSessionFactory factory =
                new SqlSessionFactoryBuilder()
                        .build(
                                input
                        );

        try (
                SqlSession sqlSession =
                        factory.openSession()
        ) {

            StudentMapper mapper =
                    sqlSession.getMapper(
                            StudentMapper.class
                    );

            Student student =
                    mapper.findById(
                            1L
                    );

            System.out.println(
                    student
            );
        }
    }
}

三十二、运行过程

读取 mybatis-config.xml
↓
创建 SqlSessionFactory
↓
打开 SqlSession
↓
获取 Mapper 代理
↓
调用 findById
↓
找到 Mapper XML
↓
执行 SQL
↓
ResultSet 映射 Student
↓
返回对象

三十三、Could not find resource

如果出现:

Could not find resource mybatis-config.xml

重点检查:

文件是否在 src/main/resources

文件名是否一致

大小写是否一致

target/classes 是否有该文件

正确:

Resources.getResourceAsStream(
        "mybatis-config.xml"
);

不要:

Resources.getResourceAsStream(
        "src/main/resources/mybatis-config.xml"
);

因为 Resources 按 classpath 查找。


三十四、查询全部

Mapper:

List<Student> findAll();

XML:

<select
        id="findAll"
        resultType="Student">

    SELECT
        id,
        student_no,
        name,
        age,
        major,
        score
    FROM student
    ORDER BY id

</select>

MyBatis 会自动把每一行映射成 Student,再组合成 List。


三十五、新增

Mapper:

int insert(
        Student student
);

XML:

<insert
        id="insert">

    INSERT INTO student
    (
        student_no,
        name,
        age,
        major,
        score
    )
    VALUES
    (
        #{studentNo},
        #{name},
        #{age},
        #{major},
        #{score}
    )

</insert>

三十六、对象参数为什么能直接写 #{name}

因为参数是:

Student student

MyBatis 会按照 JavaBean 属性读取:

studentNo
name
age
major
score

这和之前学习的:

JavaBean
反射
内省

直接相关。


三十七、增删改为什么要 commit

默认:

factory.openSession()

一般不会自动提交。

所以:

mapper.insert(
        student
);

sqlSession.commit();

否则可能:

代码没报错
数据库却没有数据

三十八、自动提交

也可以:

factory.openSession(
        true
);

表示:

autoCommit=true

但学习阶段建议手动使用 commit,理解事务。


三十九、修改

<update
        id="update">

    UPDATE student

    SET
        name = #{name},
        age = #{age},
        major = #{major},
        score = #{score}

    WHERE id = #{id}

</update>

四十、删除

<delete
        id="deleteById">

    DELETE
    FROM student
    WHERE id = #{id}

</delete>

四十一、CRUD 标签

必须掌握:

<select>

<insert>

<update>

<delete>

四十二、主键回填

如果 id 是:

AUTO_INCREMENT

插入后希望:

student.getId()

直接获得数据库生成的主键。

可以:

<insert
        id="insert"
        useGeneratedKeys="true"
        keyProperty="id">

    INSERT INTO student
    (
        student_no,
        name,
        age,
        major,
        score
    )
    VALUES
    (
        #{studentNo},
        #{name},
        #{age},
        #{major},
        #{score}
    )

</insert>

四十三、useGeneratedKeys

获取数据库自动生成主键

keyProperty:

把主键回填到 JavaBean 对应属性

四十四、多参数

例如:

Student findByNameAndAge(
        String name,
        Integer age
);

推荐:

Student findByNameAndAge(
        @Param("name")
        String name,

        @Param("age")
        Integer age
);

XML:

WHERE
    name = #{name}
    AND age = #{age}

四十五、@Param

作用:

明确 Mapper 方法参数
在 SQL 中叫什么

多参数方法建议养成使用 @Param 的习惯。


四十六、Map 参数

也可以:

List<Student> find(
        Map<String, Object> params
);

Map 中:

params.put(
        "name",
        "张三"
);

params.put(
        "age",
        20
);

SQL:

#{name}
#{age}

四十七、查询对象参数

条件较多时,可以创建:

public class StudentQuery {

    private String name;

    private Integer minAge;

    private Integer maxAge;

}

Mapper:

List<Student> findByCondition(
        StudentQuery query
);

SQL 可以直接:

#{name}
#{minAge}
#{maxAge}

四十八、#{ } 和 ${ } 区别

必须掌握:

#{}
参数占位

${}
字符串直接拼接

四十九、#{ } 更安全

WHERE name = #{name}

底层类似:

WHERE name = ?

使用 PreparedStatement,可以有效降低 SQL 注入风险。


五十、${ } 直接拼接

例如:

ORDER BY ${column}

如果:

column=score

最终:

ORDER BY score

它不是 PreparedStatement 参数。


五十一、什么时候可能用 ${ }

例如:

动态列名
动态表名
ORDER BY 字段

因为 SQL 标识符通常不能通过 ? 参数绑定。

但一定:

做白名单校验

不要直接使用用户输入。


五十二、模糊查询

接口:

List<Student> findByName(
        @Param("keyword")
        String keyword
);

XML:

<select
        id="findByName"
        resultType="Student">

    SELECT
        id,
        student_no,
        name,
        age,
        major,
        score
    FROM student

    WHERE
        name LIKE CONCAT(
            '%',
            #{keyword},
            '%'
        )

</select>

不要写成:

LIKE '%${keyword}%'

五十三、分页查询

Mapper:

List<Student> findPage(
        @Param("offset")
        int offset,

        @Param("pageSize")
        int pageSize
);

XML:

<select
        id="findPage"
        resultType="Student">

    SELECT
        id,
        student_no,
        name,
        age,
        major,
        score
    FROM student

    ORDER BY id

    LIMIT
        #{offset},
        #{pageSize}

</select>

offset:

int offset =
        (pageNum - 1)
        * pageSize;

五十四、resultType

简单查询:

resultType="Student"

MyBatis 会根据:

列名
属性名

自动映射。


五十五、数据库下划线和 Java 驼峰

数据库:

student_no

Java:

studentNo

可以用:

AS 别名

驼峰映射

resultMap

解决。


五十六、SQL 别名

SELECT
    student_no AS studentNo
FROM student;

五十七、resultMap

复杂映射:

<resultMap
        id="studentResultMap"
        type="Student">

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

    <result
            property="studentNo"
            column="student_no"/>

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

    <result
            property="age"
            column="age"/>

    <result
            property="major"
            column="major"/>

    <result
            property="score"
            column="score"/>

</resultMap>

五十八、使用 resultMap

<select
        id="findById"
        resultMap="studentResultMap">

    SELECT
        id,
        student_no,
        name,
        age,
        major,
        score
    FROM student
    WHERE id = #{id}

</select>

五十九、resultType 和 resultMap 怎么选

resultType
简单自动映射

resultMap
字段差异大
多表查询
一对一
一对多
复杂对象结构

六十、统计查询

Mapper:

long count();

XML:

<select
        id="count"
        resultType="long">

    SELECT
        COUNT(*)
    FROM student

</select>

六十一、查询单列 List

List<String> findAllMajors();

XML:

<select
        id="findAllMajors"
        resultType="string">

    SELECT DISTINCT
        major
    FROM student

</select>

六十二、事务

提交:

sqlSession.commit();

回滚:

sqlSession.rollback();

标准思路:

try {

    mapper.update(...);

    mapper.insert(...);

    sqlSession.commit();

} catch (
        Exception e
) {

    sqlSession.rollback();

    throw e;
}

六十三、一个事务要用同一个 SqlSession

如果两次数据库操作必须:

一起成功
或
一起失败

就要确保它们属于:

同一个数据库事务

基础 MyBatis 中通常意味着:

同一个 SqlSession

六十四、MyBatisUtil

package com.example.mybatis.util;

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.*;

import java.io.InputStream;

public final class MyBatisUtil {

    private static final
    SqlSessionFactory FACTORY;

    static {

        try {

            InputStream input =
                    Resources
                            .getResourceAsStream(
                                    "mybatis-config.xml"
                            );

            FACTORY =
                    new SqlSessionFactoryBuilder()
                            .build(
                                    input
                            );

        } catch (
                Exception e
        ) {

            throw new ExceptionInInitializerError(
                    e
            );
        }
    }

    private MyBatisUtil() {

    }

    public static SqlSession
    openSession() {

        return FACTORY
                .openSession();
    }
}

六十五、使用工具类

try (
        SqlSession sqlSession =
                MyBatisUtil.openSession()
) {

    StudentMapper mapper =
            sqlSession.getMapper(
                    StudentMapper.class
            );

    Student student =
            mapper.findById(
                    1L
            );

    System.out.println(
            student
    );
}

六十六、Mapper 调用底层流程

mapper.findById()
↓
Mapper 动态代理拦截
↓
根据接口名 + 方法名
查找 MappedStatement
↓
得到 SQL
↓
绑定参数
↓
JDBC 执行
↓
处理 ResultSet
↓
创建 Student
↓
属性映射
↓
返回结果

六十七、MappedStatement

MyBatis 解析:

<select>
<insert>
<update>
<delete>

后会保存成内部的:

MappedStatement

其中包含:

SQL
参数映射
结果映射
缓存配置
Statement 类型

当前了解概念即可。


六十八、为什么前面的反射很重要

现在可以看到:

Mapper 代理
结果自动封装
JavaBean 属性读取
动态对象创建

都和:

反射
JavaBean
内省
动态代理

有关。


六十九、一级缓存简单了解

MyBatis 默认:

一级缓存

作用范围:

SqlSession

同一个 SqlSession 里重复查询相同数据,有可能直接使用缓存。

例如:

mapper.findById(
        1L
);

mapper.findById(
        1L
);

第二次不一定再次访问数据库。

缓存详细原理放到 MyBatis 进阶。


七十、常见错误:Invalid bound statement

报错:

Invalid bound statement (not found)

优先检查:

namespace 是否正确

XML id 是否等于方法名

Mapper XML 是否加载

XML 路径是否正确

接口全限定名是否一致

七十一、常见错误:Parameter not found

例如:

Parameter 'name' not found

多参数时推荐:

@Param("name")
String name

然后:

#{name}

七十二、常见错误:属性值为 null

例如数据库:

student_no 有值

Java:

studentNo=null

检查:

驼峰映射

resultMap

SQL AS

属性 Getter/Setter

七十三、常见错误:增删改后数据库没有变化

最常见:

忘记 commit

七十四、常见错误:Mapper XML 找不到

例如:

Could not find resource mapper/StudentMapper.xml

检查:

resources/mapper

文件名

resource 路径

大小写

target/classes

七十五、常见错误:namespace 写错

不推荐:

namespace="StudentMapper"

推荐:

namespace=
"com.example.mybatis.mapper.StudentMapper"

七十六、常见错误:resultType 别名无法识别

如果:

resultType="Student"

但没有配置 typeAliases,可能找不到。

解决:

配置 typeAliases

或写全限定类名。


七十七、常见错误:XML 小于号

例如:

age < 18

XML 中可以写:

age &lt; 18

或者:

<![CDATA[

    age < 18

]]>

七十八、常见错误:SQL 语法错误

开启:

STDOUT_LOGGING

重点看:

Preparing

Parameters

把 SQL 复制到 MySQL 客户端直接执行,是非常有效的排查方式。


七十九、常见错误:使用 ${} 接收用户输入

不推荐:

WHERE name = '${name}'

推荐:

WHERE name = #{name}

八十、常见错误:长期使用 SELECT *

学习时可以:

SELECT *

项目中更推荐:

SELECT
    id,
    student_no,
    name,
    age,
    major,
    score
FROM student;

原因:

字段明确
减少无用数据
映射清晰
表结构变化风险更低

八十一、Mapper 和 DAO 的关系

JDBC 阶段:

StudentDao
StudentDaoImpl

MyBatis 阶段:

StudentMapper

Mapper 本质上承担:

DAO 数据访问职责

但实现类由 MyBatis 代理生成。


八十二、完整 Mapper 接口

public interface StudentMapper {

    Student findById(
            Long id
    );

    List<Student> findAll();

    List<Student> findByName(
            @Param("keyword")
            String keyword
    );

    List<Student> findPage(
            @Param("offset")
            int offset,

            @Param("pageSize")
            int pageSize
    );

    int insert(
            Student student
    );

    int update(
            Student student
    );

    int deleteById(
            Long id
    );

    long count();
}

八十三、推荐练习顺序

不要一开始把全部 CRUD 一次写完。

推荐:

1. findById

2. findAll

3. insert

4. update

5. delete

6. @Param 多参数

7. 模糊查询

8. 分页

9. resultMap

10. 事务

每完成一步:

马上运行
马上验证

八十四、IDEA 快捷操作

生成 Getter/Setter:

Alt + Insert

构造方法:

Alt + Insert
→ Constructor

toString:

Alt + Insert
→ toString()

八十五、练习题

建议依次完成:

1. 根据 id 查询学生

2. 查询全部学生

3. 姓名模糊查询

4. 新增学生

5. 自增主键回填

6. 修改学生

7. 删除学生

8. @Param 多条件查询

9. resultMap 显式映射

10. 开启驼峰映射

11. LIMIT 分页

12. 同一 SqlSession 事务回滚测试

八十六、必须掌握的 API

Resources
        .getResourceAsStream()

new SqlSessionFactoryBuilder()
        .build(...)

SqlSessionFactory
        .openSession()

SqlSession
        .getMapper()

SqlSession
        .commit()

SqlSession
        .rollback()

八十七、必须掌握的 XML 标签

<configuration>
<properties>
<settings>
<typeAliases>
<environments>
<transactionManager>
<dataSource>
<mappers>
<mapper>
<select>
<insert>
<update>
<delete>
<resultMap>
<id>
<result>

八十八、必须掌握的参数方式

单参数

@Param

JavaBean 参数

Map 参数

#{}

${}

八十九、必须回答的问题

学完后应该能够回答:

1. MyBatis 是什么?

2. MyBatis 和 JDBC 是什么关系?

3. SqlSessionFactory 是什么?

4. SqlSession 是什么?

5. Mapper 接口为什么不需要实现类?

6. namespace 有什么作用?

7. Mapper XML 的 id 对应什么?

8. #{} 和 ${} 有什么区别?

9. 为什么 #{} 更安全?

10. 多参数为什么推荐 @Param?

11. resultType 和 resultMap 有什么区别?

12. 驼峰映射是什么?

13. 增删改为什么要 commit?

14. useGeneratedKeys 有什么作用?

15. MyBatis 为什么能自动封装 JavaBean?

16. Invalid bound statement 怎么排查?

17. Could not find resource 怎么排查?

九十、MyBatis 知识结构

MyBatis
│
├─ 配置
│   ├─ properties
│   ├─ settings
│   ├─ typeAliases
│   ├─ environments
│   └─ mappers
│
├─ 核心对象
│   ├─ SqlSessionFactoryBuilder
│   ├─ SqlSessionFactory
│   └─ SqlSession
│
├─ Mapper
│   ├─ Interface
│   ├─ namespace
│   └─ id
│
├─ SQL
│   ├─ select
│   ├─ insert
│   ├─ update
│   └─ delete
│
├─ 参数
│   ├─ @Param
│   ├─ JavaBean
│   ├─ Map
│   ├─ #{}
│   └─ ${}
│
├─ 映射
│   ├─ resultType
│   ├─ resultMap
│   └─ 驼峰映射
│
└─ 事务
    ├─ commit
    └─ rollback

九十一、从 JDBC 到 MyBatis

JDBC:

自己创建 Statement
自己绑定参数
自己处理 ResultSet
自己封装对象

MyBatis:

写 Mapper 接口
写 SQL
定义结果映射
框架负责 JDBC 模板代码

但底层仍然是:

JDBC

九十二、从反射到 MyBatis

前面学过:

JavaBean
反射
内省
动态代理

现在都能看到实际作用:

Mapper 动态代理
JavaBean 属性读取
结果自动映射
参数绑定
对象创建

九十三、从 MyBatis 到 SpringBoot

后面 SpringBoot 整合 MyBatis 后,通常不需要自己:

build SqlSessionFactory
openSession
getMapper

Spring 会帮助管理:

DataSource
SqlSessionFactory
Mapper Bean
事务

所以现在学习原生 MyBatis 的意义是:

理解以后 SpringBoot 到底帮你省略了什么

九十四、本章总结

MyBatis 最核心的一句话:

MyBatis 是建立在 JDBC 之上的持久层框架,它减少 JDBC 模板代码,同时保留开发者对 SQL 的控制。

核心流程:

mybatis-config.xml
↓
SqlSessionFactory
↓
SqlSession
↓
Mapper 代理
↓
Mapper XML
↓
SQL
↓
JDBC
↓
数据库
↓
结果映射
↓
JavaBean

必须牢记:

namespace
对应 Mapper 接口

id
对应 Mapper 方法

#{}
优先用于参数绑定

${}
直接拼接,必须谨慎

resultType
适合简单映射

resultMap
适合复杂映射

commit
提交增删改事务

常见报错建议按:

配置文件路径
↓
Mapper 是否加载
↓
namespace
↓
id
↓
参数
↓
SQL
↓
结果映射
↓
事务

逐层排查。

下一篇按照课程表正式进入:

Servlet

开始学习:

Tomcat
HTTP
请求
响应
Servlet 生命周期
浏览器与 Java 后端交互