返回博客列表

第01篇:Mybatis学习之环境搭建

2026-01-29
4 min read
Mybatis

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

什么是 MyBatis?官方文档

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

重构Mybatis博客

最早使用Mybatis是在2016年,那时候学习只会用,根本不知道什么原理,不懂的原理的地方只能死记。搭建一个小的demo放到github,工作需要的时候就直接复制黏贴,虽然也能满足工作需要但是总感觉到不带劲。在找到工作后大概2017年时候就对Mybatis的源码进行了阅读,其实相对于Dubbo,Spring,之类的源码Mybatis的源码还是非常简单的,现在也经常推荐给刚开始学习看源码的同学。 早在2019年小编就开始写Mybatis的博客了,那时候博客主要还是自己学习为主,现在回头看看,感觉有些粗糙。这里准备回炉重造一波。将原博客进行优化。对Mybaits的核心功能,核心处理类重新重构下博客。

一、环境搭建

1.1 数据库脚本

sql
SET NAMES utf8;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
--  Table structure for `T_USER`
-- ----------------------------
DROP TABLE IF EXISTS `T_USER`;
CREATE TABLE `T_USER` (
  `uid` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `name` char(32) DEFAULT NULL,
  `token_id` char(64) NOT NULL,
  PRIMARY KEY (`uid`,`token_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

1.2 POM依赖

xml
<plugin>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-maven-plugin</artifactId>
    <version>1.3.2</version>
    <configuration>
        <configurationFile>${basedir}/src/main/resources/generator/generatorConfig.xml</configurationFile>
        <overwrite>true</overwrite>
        <verbose>true</verbose>
    </configuration>
</plugin>

1.3 执行脚本生成代码

mysql数据库记得要安装好,如果clone代码下来学习,记得要改成自己的数据库。 当上面的步骤都完成后,并输入自己的mysql地址和用户信息后。就可以执行下面脚本了。

mvn mybatis-generator:generate

执行后就会生成我们本节要说的所有内容代码了。

➜ tree
.
├── LICENSE
├── README.md
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── orm
    │   │       └── example
    │   │           └── dal
    │   │               ├── mapper
    │   │               │   └── TUserMapper.java
    │   │               └── model
    │   │                   └── TUser.java
    │   └── resources
    │       ├── generator
    │       │   └── generatorConfig.xml
    │       └── mapper
    │           └── TUserMapper.xml
    └── test
        └── java

13 directories, 7 files


二、原生jdbc知识复习

2.1 JDBC是什么?

JDBC代表Java数据库连接(Java Database Connectivity),它是用于Java编程语言和数据库之间的数据库无关连接的标准Java API,换句话说:JDBC是用于在Java语言编程中与数据库连接的API。

  1. 连接到数据库
  2. 创建SQL或MySQL语句
  3. 在数据库中执行SQL或MySQL查询
  4. 查看和修改结果记录

2.1.1 代码示例

java
    @Test
    public void jdbc() throws Exception {
        String dbUrl = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String pass = "123456";
        Connection connection = DriverManager.getConnection(dbUrl, user, pass);
        Statement statement = connection.createStatement();
        ResultSet resultSet = statement.executeQuery("select * from T_User");
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            System.out.println(name);
        }
        statement.close();
        resultSet.close();
        connection.close();
    }

2.2 jdbc知识点

2.2.1 jdbc驱动

通过前面的例子,我们看到我们都是调用 java.sql的包下面的类创建的与数据库交互的工具。那么我们试想一下。 java怎么知道我们用的数据库是什么呢? 如果不知道他怎么知道如何进行交互呢?

其实就是 java.sql定义了一系列的接口定义, 由具体的第三方数据库来实现这些定义。从而进行底层的交互。 这里因为我们使用的是mysql数据库,所以 Connection的具体实现就是mysql的数据驱动类 ConnectionImpl。

DriverManager 怎么知道我们要用mysql的实现呢? 这里面用到的数据就是java原生的spi能力。

java
 private static void loadInitialDrivers() {
    ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
    Iterator<Driver> driversIterator = loadedDrivers.iterator();
 }

当获得了与数据库的连接后,就可以与数据库进行交互了。 JDBC Statement,CallableStatement和PreparedStatement接口定义了可用于发送SQL或PL/SQL命令,并从数据库接收数据的方法和属性。 它们还定义了有助于在Java和SQL数据类型的数据类型差异转换的方法。

接口说明特点
Statement用于对数据库进行通用访问,在运行时使用静态SQL语句时很有用。 Statement接口不能接受参数。Statement每次的执行都需要编译SQL
PreparedStatement当计划要多次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。PreparedStatement会预编译,会被缓冲,在缓存区中可以发现预编译的命令,虽然会被再次解析,但不会被再次编译,能够有效提高系统性能
CallableStatement当想要访问数据库存储过程时使用。CallableStatement接口也可以接受运行时输入参数。CallableStatement支持存储过程

2.2.2 Statement

Statement对象后,可以使用它来执行一个SQL语句,它有三个执行方法可以执行

  • boolean execute (String SQL) : 如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQLDDL语句或需要使用真正的动态SQL,可使用于执行创建数据库,创建表的SQL语句等等。
  • int executeUpdate (String SQL): 返回受SQL语句执行影响的行数。使用此方法执行预期会影响多行的SQL语句,例如:INSERT,UPDATE或DELETE语句。
  • ResultSet executeQuery(String SQL):返回一个ResultSet对象。 当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。
java
    @Test
    public void statement() throws Exception {
        String dbUrl = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String pass = "123456";
        Connection connection = DriverManager.getConnection(dbUrl, user, pass);
        connection.setAutoCommit(false);
        Statement statement = connection.createStatement();
        // true
        System.out.println(statement.execute("insert into t_user (name,token_id) values ('孙武空','007')"));
        ResultSet resultSet = statement.executeQuery("select * from t_user");
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            System.out.println(name);
        }
        connection.rollback();
        statement.close();
        connection.close();
    }

2.2.3 PreparedStatement

PreparedStatement接口扩展了Statement接口,它添加了比Statement对象更好一些优点的功能。 此语句可以动态地提供/接受参数。

java
    @Test
    public void prepareStatement() throws Exception {
        String dbUrl = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String pass = "123456";
        Connection connection = DriverManager.getConnection(dbUrl, user, pass);
        connection.setAutoCommit(false);
        PreparedStatement preparedStatement = connection.prepareStatement("insert into t_user (name,token_id) values (?,?)");
        preparedStatement.setString(1, "唐三藏");
        preparedStatement.setString(2, "tok");
        preparedStatement.execute();

        ResultSet resultSet = preparedStatement.executeQuery("select * from t_user");
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            System.out.println(name);
        }
        connection.rollback();
        preparedStatement.close();
        connection.close();
    }

2.2.4 CallableStatement

类似Connection对象创建Statement和PreparedStatement对象一样,它还可以使用同样的方式创建CallableStatement对象,该对象将用于执行对数据库存储过程的调用。

java
    @Test
    public void callableStatement() throws Exception {
        String dbUrl = "jdbc:mysql://127.0.0.1:3306/test";
        String user = "root";
        String pass = "123456";
        Connection connection = DriverManager.getConnection(dbUrl, user, pass);
        // 1. 创建一个存储过程
        String call =
                "CREATE PROCEDURE delete_matches(IN del_name varchar(64))
" +
                        "begin
" +
                        "    delete from t_user where name = del_name;
" +
                        "end;";
        Statement statement = connection.createStatement();
        statement.execute("DROP PROCEDURE IF EXISTS delete_matches;");
        statement.execute(call);

        // 执行存储过程
        CallableStatement callableStatement = connection.prepareCall("call delete_matches(?)");
        callableStatement.setString(1, "孙武空");
        callableStatement.execute();

        // 查询结果检查存储过程是否成功
        ResultSet resultSet = statement.executeQuery("select * from t_user");
        while (resultSet.next()) {
            String name = resultSet.getString("name");
            System.out.println(name);
        }
        statement.close();
        callableStatement.close();
        connection.close();
    }

!!!tip jdbc主要提供跟数据库的交互,其主要的类就是上面演示的。通过上面的复习。我们要清楚下面几个类的作用。 后面我们在学习mybatis时候,我们看mybatis是如何对下面类的封装,从而实现orm映射的。 !!!

关键类说明
Connection数据库连接
Statement静态sql执行
PreparedStatement预处理sql
CallableStatement存储过程执行
ResultSet返回结果集

三、抛转引玉

前面我们首先搭建了mybaits的开发环境,然后又对jdbc的知识进行了复习。下面我们就开始学习mybait的源码,看mybatis是如何对 jdbc一步一步进行封装从而实现了orm的映射吧。 首先我们先看下下面演示代码。

java
    @Test
    public void mapper() {
        // 读取配置信息(为什么路径前不用加/,因为是相对路径。maven编译后的资源文件和class文件都是在一个包下,所以不用加/就是当前包目录)
        InputStream mapperInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatisConfig.xml");
        // 生成SqlSession工厂,SqlSession从名字上看就是,跟数据库交互的会话信息,负责将sql提交到数据库进行执行
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(mapperInputStream, "development");
        // 获取Mybatis配置信息
        Configuration configuration = sqlSessionFactory.getConfiguration();
        // 参数: autoCommit,从名字上看就是是否自动提交事务
        SqlSession sqlSession = sqlSessionFactory.openSession(false);
        // 获取Mapper
        TUserMapper mapper = configuration.getMapperRegistry().getMapper(TUserMapper.class, sqlSession);
        TUser tUser = new TUser();
        tUser.setName("testUser1");
        tUser.setTokenId("testTokenId1");
        mapper.insert(tUser);
        // 获取插入的数据
        System.out.println(mapper.selectAll());
        // 数据插入后,执行查询,然后回滚数据
        sqlSession.rollback();
    }

jdbc的原生操作基本已经看不到了。我们已经使用Mybatis实现了与数据库的交互。可以看到并没有看到sql信息。 因为sql信息都维护在TUserMapper.xml里面,Mybatis帮我们把TUserMapper.xml和TUserMapper建立了关系。 最终将原本要通过jdbc实现的操作通过代理的方式,并最终通过TUserMapper这个接口进行交互了。

请问到这里勾起你的好奇心了没有呢? 想不想知道为什么能这样吗? 想不想知道mybaits究竟做了什么,以及是怎么做的呢? 本系列文章会带你一探究竟。在开始之前我们先指定一下学习目标吧。

3.1 学习目标制定

  • 配置文件是如何解析成 Configuration ?
  • sql 和数据库是如何交互的 SqlSession ?
  • mapper.xmlMapper 是如何绑定的MapperRegistry ?
  • Mybatis 是如何做动态代理的 ?
  • Mybatis中如何利用插件实现扩展的?
  • Jdbc的Statement在Mybatis是如何封装的?
  • 以及Mybatis中很多好用的工具类.

3.2 学习后我们能得到什么

  • 从配置文件解析中我们能学会,如果解析占位符。并将占位符填充真实数据。
  • 通过对 SqlSession 的学习,我们会了解到Mybatis的缓存设计,批处理任务,事务等操作。
  • 通过对 MapperRegistry 的学习, 我们会了解到如何实现 orm(对象关系映射) 框架。
  • 我们会收货很多设计的思路,而思路决定出路。
返回博客列表
最后更新于 2026-01-29
想法或问题?在 GitHub Issue 下方参与讨论
去评论