简单配置让MyBatis跑起来

这一节将通过一个 MyBatis 的简单例子让大家对 MyBatis 有一个初步的了解。在操作过程中,建议初学者按照书中的步骤进行,如果已经了解各项配置之间的关系,也可以按照自己习惯的方式对配置进行调整。

准备数据库

首先创建一个数据库,编码方式设为 UTF-8,可以使用 MySQL 客户端工具 Navicat 来实现。通过执行下面的 SQL 语句创建一个名为 mybatis 的数据库。

CREATE DATABASE mybatis DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

然后再创建一个名为 country 的表并插入一些简单的数据,代码如下。

# 创建数据库
CREATE DATABASE mybatis2 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

# 创建表
CREATE TABLE country (
  id INT NOT NULL AUTO_INCREMENT,
  countryname VARCHAR(255) NULL ,
  countrycode VARCHAR(255) NULL ,
  PRIMARY KEY (id)
);

# 初始化表数据
INSERT country(countryname, countrycode) VALUES ('中国','CN'),('美国','US'),('俄罗斯','RU'),('英国','GB'),('法国','FR');

准备好表和简单的数据后,继续来配置 MyBatis。

配置MyBatis

配置 MyBatis 有多种方式,本节使用最基础最常用的 XML 形式进行配置。

除 XML 方式外,在后面介绍和 Spring 集成的时候还会使用 Spring bean 方式进行配置,另外还可以通过 Java 编码方式进行配置。由于 Java 编码配置方式不常用,因此在本书中没有涉及。

使用 XML 形式进行配置,首先在 src/main/resources 下面创建 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>

    <settings>
        <setting name="logImpl" value="LOG4J2"/>
    </settings>

    <typeAliases>
        <package name="com.zccoder.mybatis2.ch1.start.model"/>
    </typeAliases>

    <environments default="dev">
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mybatis2?characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>

    <mappers>
        <mapper resource="com/zccoder/mybatis2/ch1/start/mapper/CountryMapper.xml"/>
    </mappers>

</configuration>

简单讲解一下这个配置。

  • <settings> 中的 logImpl 属性配置指定使用 LOG4J 输出日志。

  • <typeAliases> 元素下面配置了一个包的别名,通常确定一个类的时候需要使用类的全限定名称,例如 tk.mybatis.simple.model.Country。在 MyBatis 中需要频繁用到类的全限定名称,为了方便使用,我们配置了 tk.mybatis.simple.model 包,这样配置后,在使用类的时候不需要写包名的部分,只使用 Country 即可。

  • <environments> 环境配置中主要配置了数据库连接,数据库的 url 为 jdbc:mysql://localhost:3306/mybatis,使用的是本机 MySQL 中的 mybatis 数据库,后面的 username 和 password 分别是数据库的用户名和密码(如果你的数据库用户名及密码和这里的不一样,请修改为自己数据库可用的用户名和密码)。

  • <mappers> 中配置了一个包含完整类路径的 CountryMapper.xml,这是一个 MyBatis 的 SQL 语句和映射配置文件,这个 XML 文件会在后面的章节中介绍。

创建实体类和Mapper.xml文件

MyBatis 是一个结果映射框架,这里创建的实体类实际上是一个数据值对象(Data Value Object),在实际应用中,一个表一般会对应一个实体,用于INSERT、UPDATE、DELETE 和简单的 SELECT 操作,所以姑且称这个简单的对象为实体类。

关于 Mapper 的命名方式:在 MyBatis 中,根据 MyBatis 官方的习惯,一般用 Mapper 作为 XML 和接口类名的后缀,这里的 Mapper 和我们常用的 DAO 后缀类似,只是一种习惯而已,本书中全部使用 Mapper 后缀。通常称 XML 为 Mapper.xml 文件,称接口为 Mapper 接口,在实际应用中可以根据自己的需要来定义命名方式。

在 src/main/java 下创建一个基础的包 tk.mybatis.simple,在这个包下面再创建 model 包。根据数据库表 country,在 model 包下创建实体类 Country,代码如下。

package com.zccoder.mybatis2.ch1.start.model;

import java.io.Serializable;

public class Country implements Serializable {

    private static final long serialVersionUID = -7360153715392794454L;

    /**
     * 主键
     */
    private Long id;
    /**
     * 国家名称
     */
    private String countryname;
    /**
     * 国家代码
     */
    private String countrycode;

    @Override
    public String toString() {
        return "Country{" +
                "id=" + id +
                ", countryname='" + countryname + '\'' +
                ", countrycode='" + countrycode + '\'' +
                '}';
    }

    /**
     * 获取 主键
     *
     * @return id 主键
     */
    public Long getId() {
        return this.id;
    }

    /**
     * 设置 主键
     *
     * @param id 主键
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * 获取 国家名称
     *
     * @return countryname 国家名称
     */
    public String getCountryname() {
        return this.countryname;
    }

    /**
     * 设置 国家名称
     *
     * @param countryname 国家名称
     */
    public void setCountryname(String countryname) {
        this.countryname = countryname;
    }

    /**
     * 获取 国家代码
     *
     * @return countrycode 国家代码
     */
    public String getCountrycode() {
        return this.countrycode;
    }

    /**
     * 设置 国家代码
     *
     * @param countrycode 国家代码
     */
    public void setCountrycode(String countrycode) {
        this.countrycode = countrycode;
    }
}

在 src/main/resources 下面创建 tk/mybatis/simple/mapper 目录,再在该目录下面创建 CountryMapper.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.zccoder.mybatis2.ch1.start.mapper.CountryMapper">

    <select id="listAll" resultType="Country">
        SELECT
            id,
            countryname,
            countrycode
        FROM country
    </select>

</mapper>

SQL 定义在 CountryMapper.xml 文件中,里面的配置作用如下。

  • <mapper>:XML 的根元素,属性 namespace 定义了当前 XML 的命名空间。

  • <select>元素:我们所定义的一个 SELECT 查询。

  • id属性:定义了当前 SELECT 查询的唯一一个id。

  • resultType:定义了当前查询的返回值类型,此处就是指实体类 Country,前面配置中提到的别名主要用于这里,如果没有设置别名,此处就需要写成 resultType="tk.mybatis.simple.model.Country"。

  • select id,…​:查询 SQL 语句。

创建好实体和 Mapper.xml 后,接下来要有针对性地配置 Log4j,让 MyBatis 在执行数据库操作的时候可以将执行的 SQL 和其他信息输出到控制台。

配置Log4j以便查看MyBatis操作数据库的过程

在 src/main/resources 中添加 log4j2.xml 配置文件,输入如下内容。

<?xml version="1.0" encoding="UTF-8"?>
<!-- status:log4j内部console日志级别,monitorInterval:自动更新配置(单位:秒) -->
<configuration status="error" monitorInterval="3600">
    <!--自定义一些常量,之后使用${变量名}引用-->
    <properties>
        <Property name="baseDir">./</Property>
    </properties>
    <!--appender:定义输出内容,输出格式,输出方式,日志保存策略等,常用其下三种标签[console,File,RollingRandomAccessFile]-->
    <!--Appender可以理解为日志的输出目的地-->
    <appenders>
        <!-- 标准console输出 -->
        <Console name="stdOut" target="SYSTEM_OUT">
            <PatternLayout pattern="%c{1} %d{yyyy-MM-dd HH:mm:ss SSS} [%t] %T %-5p %m%n"/>
        </Console>
    </appenders>

    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <loggers>
        <Root level="debug">
            <AppenderRef ref="stdOut"/>
        </Root>
        <Logger name="com.zccoder.mybatis2" level="debug" additivity="false">
            <AppenderRef ref="stdOut"/>
        </Logger>
    </loggers>
</configuration>

用过 Log4j 日志组件的人可能都会知道,配置中的 log4j.logger.tk.mybatis.simple.mapper 对应的是 tk.mybatis.simple.mapper 包,但是在这个例子中,Java 目录下并没有这个包名,只在资源目录下有 mapper 目录。

在 MyBatis 的日志实现中,所谓的包名实际上是 XML 配置中的 namespace 属性值的一部分。后面章节中介绍结合接口使用的相关内容时,由于 namespace 属性值必须和接口全限定类名相同,因此才会真正对应到 Java 中的包。当使用纯注解方式时,使用的就是纯粹的包名。

MyBatis 日志的最低级别是 TRACE,在这个日志级别下,MyBatis 会输出执行 SQL 过程中的详细信息,这个级别特别适合在开发时使用。

配置好 Log4j 后,接下来就可以编写测试代码让 MyBatis 跑起来了。

编写测试代码让MyBatis跑起来

首先在 src/test/java 中创建 tk.mybatis.simple.mapper 包,然后创建 CountryMapperTest 测试类,代码如下。

package com.zccoder.mybatis2.ch1.start.mapper;

import com.zccoder.mybatis2.ch1.start.model.Country;
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.BeforeClass;
import org.junit.Test;

import java.io.IOException;
import java.io.Reader;
import java.util.List;

public class CountryMapperTest {

    private static SqlSessionFactory sqlSessionFactory;

    @BeforeClass
    public static void init() {
        try {
            Reader reader = Resources.getResourceAsReader("mybatis.cfg.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testQueryAll() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<Country> countryList = sqlSession.selectList("listAll");
        this.printCountryList(countryList);
        sqlSession.close();
    }

    private void printCountryList(List<Country> countryList) {
        for (Country country : countryList) {
            System.out.printf("%-4d%4s%4s\n",country.getId(),country.getCountryname(),country.getCountrycode());
        }
    }
}

对上面这段代码做一个简单的说明,具体如下。

  • 通过 Resources 工具类将 mybatis-config.xml 配置文件读入 Reader。

  • 再通过 SqlSessionFactoryBuilder 建造类使用 Reader 创建 SqlSessionFactory 工厂对象。在创建 SqlSessionFactory 对象的过程中,首先解析 mybatis-config.xml 配置文件,读取配置文件中的 mappers 配置后会读取全部的 Mapper.xml 进行具体方法的解析,在这些解析完成后,SqlSessionFactory 就包含了所有的属性配置和执行 SQL 的信息。

  • 使用时通过 SqlSessionFactory 工厂对象获取一个 SqlSession。

  • 通过 SqlSession 的 selectList 方法查找到 CountryMapper.xml中id="selectAll" 的方法,执行 SQL 查询。

  • MyBatis 底层使用 JDBC 执行 SQL,获得查询结果集 ResultSet 后,根据 resultType 的配置将结果映射为 Country 类型的集合,返回查询结果。

  • 这样就得到了最后的查询结果 countryList,简单将结果输出到控制台。

  • 最后一定不要忘记关闭 SqlSession,否则会因为连接没有关闭导致数据库连接数过多,造成系统崩溃。

上面的测试代码成功执行后,会输出如下日志。

listAll 2024-05-20 19:39:27 712 [Test worker] 1 DEBUG ==>  Preparing: SELECT id, countryname, countrycode FROM country
listAll 2024-05-20 19:39:27 745 [Test worker] 1 DEBUG ==> Parameters:
listAll 2024-05-20 19:39:27 775 [Test worker] 1 TRACE <==    Columns: id, countryname, countrycode
listAll 2024-05-20 19:39:27 775 [Test worker] 1 TRACE <==        Row: 1, 中国, CN
listAll 2024-05-20 19:39:27 779 [Test worker] 1 TRACE <==        Row: 2, 美国, US
listAll 2024-05-20 19:39:27 779 [Test worker] 1 TRACE <==        Row: 3, 俄罗斯, RU
listAll 2024-05-20 19:39:27 780 [Test worker] 1 TRACE <==        Row: 4, 英国, GB
listAll 2024-05-20 19:39:27 780 [Test worker] 1 TRACE <==        Row: 5, 法国, FR
listAll 2024-05-20 19:39:27 781 [Test worker] 1 DEBUG <==      Total: 5
1     中国  CN
2     美国  US
3    俄罗斯  RU
4     英国  GB
5     法国  FR

从日志中可以看到完整的 SQL 输出和结果输出,从日志对应的级别可以发现 SQL、参数、结果数都是 DEBUG 级别,具体的查询结果列和数据都是 TRACE 级别。

通过一系列的操作,我们让一个简单的 MyBatis 例子跑了起来,相信大家现在对 MyBatis 已经有了初步的了解。

simple 项目下载地址:http://mybatis.tk/book/simple-start.zip。

在学习这部分代码时,如果程序无法运行,或者不知道这些配置和测试类该写到哪里,都可以从该网址下载这部分的完整代码,通过对比来解决问题,或者直接使用这部分基础代码来继续学习接下来的内容。