1、安装 MyBatis

要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。如果使用 构建工具 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

  1. <dependency>
  2. <groupId>org.mybatis</groupId>
  3. <artifactId>mybatis</artifactId>
  4. <version>x.x.x</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>mysql</groupId>
  8. <artifactId>mysql-connector-java</artifactId>
  9. <version>8.0.21</version>
  10. </dependency>

同时不要忘了添加 JDBC 驱动,这里使用 MySQL-JDBC 作为演示

2、构建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为核心的。SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。而 SqlSessionFactoryBuilder 则可以从 XML 配置文件或一个预先配置的 Configuration 实例来构建出 SqlSessionFactory 实例。

从 XML 文件中构建 SqlSessionFactory 的实例非常简单,建议使用类路径下的资源文件进行配置。 但也可以使用任意的输入流(InputStream)实例,比如用文件路径字符串或 file:// URL 构造的输入流。MyBatis 包含一个名叫 Resources 的工具类,它包含一些实用方法,使得从类路径或其它位置加载资源文件更加容易。

  1. String resource = "mybatis-config.xml";
  2. InputStream inputStream = Resources.getResourceAsStream(resource);
  3. SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

XML 配置文件中包含了对 MyBatis 系统的核心设置,包括获取数据库连接实例的数据源(DataSource)以及决定事务作用域和控制方式的事务管理器(TransactionManager)。后面会再探讨 XML 配置文件的详细内容,这里先给出一个简单的示例:

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <environments default="default">
  7. <!--默认环境-->
  8. <environment id="default">
  9. <transactionManager type="JDBC"/>
  10. <dataSource type="POOLED">
  11. <property name="driver" value="${jdbc.driver}"/>
  12. <property name="url" value="${jdbc.url}"/>
  13. <property name="username" value="${jdbc.username}"/>
  14. <property name="password" value="${jdbc.password}"/>
  15. </dataSource>
  16. </environment>
  17. </environments>
  18. <!--映射文件配置-->
  19. <mappers>
  20. <mapper resource="com/zhoutao123/source/mybatis/mapper/UserMapper.xml"/>
  21. </mappers>
  22. </configuration>

UserMapper.xml 文件

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.zhoutao123.source.mybatis.mapper.UserMapper">
  6. <select id="getOneById" resultType="com.zhoutao123.source.mybatis.model.UserDAO">
  7. SELECT * FROM userWHERE id = #{id}
  8. </select>
  9. </mapper>

当然,还有很多可以在 XML 文件中配置的选项,上面的示例仅罗列了最关键的部分。 注意 XML 头部的声明,它用来验证 XML 文档的正确性。environment 元素体中包含了事务管理和连接池的配置。mappers 元素则包含了一组映射器(mapper),这些映射器的 XML 映射文件包含了 SQL 代码和映射定义信息。

更多关于 dtd 文件的格式,请访问 https://www.runoob.com/dtd/dtd-tutorial.html

3、获取 SqlSession

既然有了 SqlSessionFactory,顾名思义,我们可以从中获得 SqlSession 的实例。SqlSession 提供了在数据库执行 SQL 命令所需的所有方法。你可以通过 SqlSession 实例来直接执行已映射的 SQL 语句。例如:

  1. try (SqlSession session = sqlSessionFactory.openSession()) {
  2. // 第一个参数是 Mapper 文件中 namespace + id 拼接而成
  3. UserDAO user = (UserDAO) session.selectOne("com.zhoutao123.source.mybatis.mapper.UserMapper.getOneById", 1);
  4. }

诚然,这种方式能够正常工作,对使用旧版本 MyBatis 的用户来说也比较熟悉。但现在有了一种更简洁的方式——使用和指定语句的参数和返回值相匹配的接口(比如UserMapper.class),现在你的代码不仅更清晰,更加类型安全,还不用担心可能出错的字符串字面值以及强制类型转换

  1. try (SqlSession session = sqlSessionFactory.openSession()) {
  2. UserMapper mapper = session.getMapper(UserMapper.class);
  3. UserDAO blog = mapper.getOneById(101);
  4. }

接口映射文件

  1. public interface UserMapper {
  2. /** 根据ID查询用户信息 */
  3. UserDAO getOneById(Long id);
  4. // 甚至可以通过注解的方式直接定义SQL,但是这种方式由于不利于维护项目,并不推荐使用
  5. @Select("SELECT * FROM user")
  6. List<UserDAO> all();
  7. }

在 Mapper 文件中,我们定义了命名空间,其作用有两个,一个是利用更长的全限定名来将不同的语句隔离开来,同时也实现了你上面见到的接口绑定。

就算你觉得暂时用不到接口绑定,你也应该遵循这里的规定,以防哪天你改变了主意。 长远来看,只要将命名空间置于合适的 Java 包命名空间之中,你的代码会变得更加整洁,也有利于你更方便地使用 MyBatis。

4、MyBatis 配置项

通过在MyBatis的配置文件中定义配置项我们可以更好地使用MyBatis,常用的MyBatis 配置项主要有如下的几种(这里仅仅展示,不做深入的讲解,后面涉及到配置会专门作出讲解

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

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

在上面的 MyBatis-Config.xml 文件中,我们已经看到了部分示例,下面简单的了解下其作用以及使用的场景

4.1 properties(属性)

properties(属性) 用户导入外部属性,可以从本地文件系统的导入或者从远程URL中导入。

  1. <properties resource="jdbc-config.properties"/>
  2. <properties url="http://www.zhoutao123.com/mybatis/config.properties"/>

例如 jdbc-config.properties 中的配置如下

  1. jdbc.driver=com.mysql.cj.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/test
  3. jdbc.username=root
  4. jdbc.password=825638

在后面的配置中,通过 ${KEY} 的方式使用,比如 ${jdbc.url}

4.2 Setting (设置)

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 下表描述了设置中各项设置的含义、默认值等。

  1. <!--设置项目-->
  2. <settings>
  3. <!--开启自动映射下划线到驼峰--> <setting name="mapUnderscoreToCamelCase" value="true"/>
  4. </settings>

_

4.3 类型别名(typeAliases)

类型别名可为 Java 类型设置一个缩写名字。 它仅用于 XML 配置,意在降低冗余的全限定类名书写。在上面的示例中 Mapper resultType 使用了全类名, <select id="getOneById" resultType="com.zhoutao123.source.mybatis.model.UserDAO"> 事实上我们可以定义个别名,方便使用。

  1. <typeAliases>
  2. <package name="com.zhoutao123.source.mybatis.model"/>
  3. <typeAlias alias="user" type="com.zhoutao123.source.mybatis.model.UserDAO"/>
  4. </typeAliases>
  • 可以通过 package 批量定义别名,如果自动批量定义 可以能出现冲突,这时候需要添加注解 @Alias 手动指定别名解决
  • 也可以通过 typeAlias 标签手动指定别名

这样在Mapper文件中我们就可以通过 `<select id="getOneById" resultType="user"> 直接使用UserDAO,而非写复杂的全类名

同时,为了方便,MyBatis 内置了一些基本类型和常用类型的别名,如下所示,那么在书写SQL的时候,我们可以直接使用这些别名

image.png

4.4 类型处理器(typeHandlers)

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型或者将参数转换为合适的列值。下表描述了一些默认的类型处理器。
image.png

在特殊的情况下,我们可以通过自定义类处理器来实现自动转换,比如

  1. public class TestTypeHandle extends BaseTypeHandler<UserDAO> {
  2. @Override
  3. public void setNonNullParameter(PreparedStatement preparedStatement, int i, UserDAO userDAO,JdbcType jdbcType) throws SQLException {}
  4. @Override
  5. public UserDAO getNullableResult(ResultSet resultSet, String s) throws SQLException { return null; }
  6. @Override
  7. public UserDAO getNullableResult(ResultSet resultSet, int i) throws SQLException { return null; }
  8. @Override
  9. public UserDAO getNullableResult(CallableStatement callableStatement, int i) throws SQLException { return null; }
  10. }

然后需要在XML配置文件中指定新的类处理器

  1. <typeHandlers>
  2. <typeHandler handler="com.zhoutao123.source.mybatis.UUIDHandleType.TestTypeHandle" javaType="java.lang.String"/>
  3. </typeHandlers>

4.5 插件(plugins)

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为在试图修改或重写已有方法的行为时,很可能会破坏 MyBatis 的核心模块。 这些都是更底层的类和方法,所以使用插件的时候要特别当心。
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

  1. <!-- mybatis-config.xml -->
  2. <plugins>
  3. <plugin interceptor="org.mybatis.example.ExamplePlugin">
  4. <property name="someProperty" value="100"/>
  5. </plugin>
  6. </plugins>

这个笔者后续中会详细讲解,这里只做简单的说明

4.6 环境配置(environments)

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:

  • 每个数据库对应一个 SqlSessionFactory 实例

为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。可以接受环境配置的两个方法签名是:

  1. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
  2. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

如果忽略了环境参数,那么将会加载默认环境,如下所示:

  1. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
  2. SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);


  • environments 元素定义了如何配置环境。

    1. <environments default="development">
    2. <environment id="development">
    3. <transactionManager type="JDBC">
    4. <property name="..." value="..."/>
    5. </transactionManager>
    6. <dataSource type="POOLED">
    7. <property name="driver" value="${driver}"/>
    8. <property name="url" value="${url}"/>
    9. <property name="username" value="${username}"/>
    10. <property name="password" value="${password}"/>
    11. </dataSource>
    12. </environment>
    13. </environments>

    注意一些关键点:

  • 默认使用的环境 ID(比如:default=”development”)。

  • 每个 environment 元素定义的环境 ID(比如:id=”development”)。
  • 事务管理器的配置(比如:type=”JDBC”)。
  • 数据源的配置(比如:type=”POOLED”)。

默认环境和环境 ID 顾名思义。 环境可以随意命名,但务必保证默认的环境 ID 要匹配其中一个环境 ID。

4.6.1 事务管理器(transactionManager)

在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):

  • JDBC – 这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接。然而一些容器并不希望连接被关闭,因此需要将 closeConnection 属性设置为 false 来阻止默认的关闭行为。例如:
    1. <transactionManager type="MANAGED">
    2. <property name="closeConnection" value="false"/>
    3. </transactionManager>

    提示** 如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器,因为 Spring 模块会使用自带的管理器来覆盖前面的配置。

4.6.2 数据源(dataSource)

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

  • 大多数 MyBatis 应用程序会按示例中的例子来配置数据源。虽然数据源配置是可选的,但如果要启用延迟加载特性,就必须配置数据源。

有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

UNPOOLED**– 这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接可用性要求不高的简单应用程序来说,是一个很好的选择。 性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合这种情形。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8 这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8 的 encoding 属性给数据库驱动。


POOLED**– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnections 与 poolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

4.7 数据库厂商标识(databaseIdProvider)

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。 MyBatis 会加载带有匹配当前数据库 databaseId 属性和所有不带 databaseId 属性的语句。 如果同时找到带有 databaseId 和不带 databaseId 的相同语句,则后者会被舍弃。 为支持多厂商特性,只要像下面这样在 mybatis-config.xml 文件中加入 databaseIdProvider 即可:

  1. <databaseIdProvider type="DB_VENDOR" />

databaseIdProvider 对应的 DB_VENDOR 实现会将 databaseId 设置为 DatabaseMetaData#getDatabaseProductName() 返回的字符串。 由于通常情况下这些字符串都非常长,而且相同产品的不同版本会返回不同的值,你可能想通过设置属性别名来使其变短:

  1. <databaseIdProvider type="DB_VENDOR">
  2. <property name="SQL Server" value="sqlserver"/>
  3. <property name="DB2" value="db2"/>
  4. <property name="Oracle" value="oracle" />
  5. </databaseIdProvider>

在提供了属性别名时,databaseIdProvider 的 DB_VENDOR 实现会将 databaseId 设置为数据库产品名与属性中的名称第一个相匹配的值,如果没有匹配的属性,将会设置为 “null”。 在这个例子中,如果 getDatabaseProductName() 返回“Oracle (DataDirect)”,databaseId 将被设置为“oracle”。在下面的示例中,我么可以为相同ID的 SELECT 配置不同的 databaseId.

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE mapper
  3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
  5. <mapper namespace="com.zhoutao123.source.mybatis.mapper.UserMapper">
  6. <!-- 使用 MySQL 数据库的时候-->
  7. <select id="getOneById" resultType="user" databaseId="mysql">
  8. SELECT *
  9. FROM user
  10. WHERE id = #{id}
  11. </select>
  12. <!-- 使用 Oracle 数据库的时候-->
  13. <select id="getOneById" resultType="user" databaseId="oracle">
  14. SELECT *
  15. FROM USER
  16. WHERE ID = #{id}
  17. </select>
  18. </mapper>

4.8 映射器(mappers)

既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等。例如:

  1. <!-- 使用相对于类路径的资源引用 -->
  2. <mappers>
  3. <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  4. <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  5. <mapper resource="org/mybatis/builder/PostMapper.xml"/>
  6. </mappers>
  7. <!-- 使用完全限定资源定位符(URL) -->
  8. <mappers>
  9. <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  10. <mapper url="file:///var/mappers/BlogMapper.xml"/>
  11. <mapper url="file:///var/mappers/PostMapper.xml"/>
  12. </mappers>
  13. <!-- 使用映射器接口实现类的完全限定类名 -->
  14. <mappers>
  15. <mapper class="org.mybatis.builder.AuthorMapper"/>
  16. <mapper class="org.mybatis.builder.BlogMapper"/>
  17. <mapper class="org.mybatis.builder.PostMapper"/>
  18. </mappers>
  19. <!-- 将包内的映射器接口实现全部注册为映射器 -->
  20. <mappers>
  21. <package name="org.mybatis.builder"/>
  22. </mappers>

这些配置会告诉 MyBatis 去哪里找映射文件,剩下的细节就应该是每个 SQL 映射文件了,也就是接下来我们要讨论的。