一、Mybatis 是什么?

注意Mybatis 与 JDBC

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

这是截下来的,版本现在更高。

img

二、Mybatis 整体架构

img

img

这里的 Mybatis 配置、SqlSessionFactory、SqlSession Mapped Statement 的理解会在后面实践的过程中会更深刻。

三、快速入门

之前也写过入门mybatis的内容,这次更细节点。

image-20210909203159741

3.1 环境准备

  • 准备好数据库 Mysql

    • 创建数据库, 表

      1
      2
      3
      4
      5
      6
      7
      8
      9
      
      create database TravelFrog;
      CREATE TABLE commodity
      (
        id    int      NOT NULL AUTO_INCREMENT,
        name  varchar(255)     NOT NULL ,
        price decimal(8,2)  NOT NULL ,
        description  varchar(255),
        PRIMARY KEY(id)
      ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
      
    • 插入数据

      1
      2
      3
      4
      5
      6
      7
      8
      
      INSERT INTO commodity(id, name, price, description)
      VALUES(1001, '野葡萄烤饼', 10, '吃完还有点饿');
      INSERT INTO commodity(id, name, price, description)
      VALUES(1002, '巨石三明治', 20, '蔬菜、快速轻食');
      INSERT INTO commodity(id, name, price, description)
      VALUES(1003, '南瓜百吉饼', 50, '份大量足,可以去很远的地方');
      INSERT INTO commodity(id, name, price, description)
      VALUES(1004, '乳蛋饼', 80, '想快点吃到拍,身心都轻快了,快速回家');
      
  • 导入依赖

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
    <dependencies>
    
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>
    
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.27</version>
        </dependency>
    
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    
    </dependencies>
    

3.2 全局配置文件(mybatis-config.xml)

 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
<?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>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <!-- 选择连接数据库用的驱动 -->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <!-- 数据库地址 -->
                <property name="url" value="jdbc:mysql://localhost:3306/ssmdemo?useSSL=true&amp;characterEncoding=UTF-8"/>
                <!-- 数据库用户名 -->
                <property name="username" value="qixianxian"/>
                <!-- 数据库密码 -->
                <property name="password" value="1998"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 导入Mybatis数据库查询映射文件 -->
    <mappers>

    </mappers>
</configuration>

3.3 获取SqlSession

可以编写一个通用类

 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
28
package com.mybatis01.utils;

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 java.io.IOException;
import java.io.InputStream;

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();
    }
}

3.4 编写实体类

 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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.mybatis01.pojo;

public class Commodity {
    private int id;
    private String name;
    private Double price;
    private String description;

    public Commodity(int id, String name, Double price, String description) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.description = description;
    }

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Double getPrice() {
        return price;
    }

    public String getDescription() {
        return description;
    }

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

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

    public void setPrice(Double price) {
        this.price = price;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Commodity{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", price=" + price +
                ", description='" + description + '\'' +
                '}';
    }
}

3.4 编写dao接口和mapper配置

DAO接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
package com.mybatis01.dao;

import com.mybatis01.pojo.Commodity;

import java.util.List;

public interface CommodityMapper {

    List<Commodity> getAllCommodity();
}

XML Mapper

1
2
3
4
5
6
7
8
9
<?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.mybatis01.dao.CommodityMapper">
    <select id="getAllCommodity" resultType="com.mybatis01.pojo.Commodity">
        select * from commodity
    </select>
</mapper>

编写好这个mapper后,需要到全局配置文件中进行注册。

1
2
3
4
<!-- 导入Mybatis数据库查询映射文件 -->
    <mappers>
		<mapper resource="mapper/commodity-mapper.xml"/>
    </mappers>

3.5 编写测试类

根测试代码的类名(和包名)对应,这样好维护

 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
package com.mybatis01.dao;

import com.mybatis01.pojo.Commodity;
import com.mybatis01.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class CommodityTest {

    @Test
    public void testGetAllCommodity() {
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        try {
            CommodityMapper commodityMapper = sqlSession.getMapper(CommodityMapper.class);
            List<Commodity> commodityList = commodityMapper.getAllCommodity();
            for (Commodity commodity : commodityList) {
                System.out.println(commodity.toString());
            }
        } finally {
            sqlSession.close();
        }
    }
}

运行正常。

四、关于 Mybatis 构成的分析

image-20211228223402533

主要针对 SqlSessionFactoryBuilder, SqlSessionFactory, SqlSession 的作用域和生命周期。

官网有描述,这里的MybatisUtil 就是按照这个思想写的。SqlSessionFactoryBuilder 生成 SqlSessionFactory 后就可以删除,SqlSessionFactory 存在于整个应用期间, SqlSession 存在于当前一个任务上,也就是当前一个访问线程。

SqlSessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了 SqlSessionFactory,就不再需要它了。 因此 SqlSessionFactoryBuilder 实例的最佳作用域是方法作用域(也就是局部方法变量)。你可以重用 SqlSessionFactoryBuilder 来创建多个 SqlSessionFactory 实例,但最好还是不要一直保留着它,以保证所有的 XML 解析资源可以被释放给更重要的事情。

SqlSessionFactory

SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。 使用 SqlSessionFactory 的最佳实践是在应用运行期间不要重复创建多次,多次重建SqlSessionFactory 被视为一种代码“坏习惯”。因此SqlSessionFactory 的最佳作用域是应用作用域。有很多方法可以做到,最简单的就是使用单例模式或者静态单例模式。

SqlSession

每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的托管作用域中,比如 Servlet 框架中的 HttpSession。如果你现在正在使用一种 Web 框架,考虑将 SqlSession 放在一个和 HTTP 请求相似的作用域中。换句话说,每次收到 HTTP 请求,就可以打开一个 SqlSession,返回一个响应后,就关闭它。这个关闭操作很重要,为了确保每次都能执行关闭操作,你应该把这个关闭操作放到 finally 块中。

参考文章

MyBatis学习笔记(1)—使用篇

[Mybatis教程-实战看这一篇就够了]