MyBatis 快速入门

记录一下最近在学习的 MyBatis 框架。


简介

MyBatis 的前身是 iBatis ,从 iBatis 3.x 开始更名为 MyBatis ,是服务于 Java Web 开发三层架构中的 DAO 层的框架。

如果对框架历史感兴趣的话可以从网上看,这里不再做更多的叙述。

这里着重分析下 MyBatis 的框架特点:

  • 摆脱了传统的 JDBC 中的注册驱动、手动获取连接、获取 PreparedStatement 、注入参数和处理结果集等流程
  • 使用符合开发习惯的 XML 文件或者注解方式实现配置和映射
  • 支持定制化 sql 和存储过程
  • 支持原始映射或高级映射,是一个半自动的 ORM 框架

对比

  • 对比 JDBC

首先要明确 MyBatis 是基于 JDBC 的,是对 JDBC 的轻量封装。

所以毫无疑问的执行效率是 JDBC 的更高一些,但是不要说写过,见过 JDBC 代码的人都知道 JDBC 的代码有多啰嗦,所以也导致了相应的 JDBC 的开发效率不高。

这也是 MyBatis 流行的原因之一:有着与 JDBC 相近的执行效率,同时带来了高效的开发效率。

  • 对比 Hibernate

Hibernate 也是基于 JDBC,但是是对 JDBC 的深度封装。一个完全 ORM 框架,采用全表映射。甚至操作数据库不再使用 SQL 而是使用 Hibernate 专属的 HQL。

在只需要轻量级操作数据库、业务场景不复杂而且完全没有变复杂趋势的情况下可以使用 Hibernate。

或者团队追求极致的开发效率的时候也可以选择使用 Hibernate,但是除非团队超过一半是 Hibernate 专家,否则前期追求到效率会随着业务复杂而成倍偿还。

所以 Hibernate 是前期极其简单,入门友好。但是随着业务场景复杂,需要频繁的多表联查的时候,再使用 Hibernate 就会显得吃力。而且由于 Hibernate 是完全 ORM 框架,对于内部生成的 SQL 很难去优化。

  • 总结

相比 JDBC 和 Hibernate,MyBatis 在权衡效率和灵活上做得十分友好。既不需要 JDBC 复杂的配置和重复的操作又可以轻松实现 SQL 定制。


XML 还是注解

和其他 Java Web 框架一样,MyBatis 也是同时支持 XML 和注解两种方式。

看过我 Spring 博客的人都知道,对于 Spring 我个人是极其喜欢使用注解方式,主要有以下几个原因:

  1. XML 配置相对于注解需要编写更多内容,多少会影响开发效率
  2. XML 文件配置一大了也就显得臃肿,哪怕能够做到将不同模块的内容分开配置,也只能做到避免,不能消除
  3. 使用 XML 文件的话从配置到代码需要跨文件跳转,而注解只会出现在与配置相关的类中,更加极简

但是对于 MyBatis 毫无疑问会选择 XML 方式。

因为 MyBatis 不同于 Spring ,IoC 容器中的 Bean 依赖配置好了几乎不会怎么变,AOP 切面也是设计好了就很少变更。

而 MyBatis 是服务于 DAO 层,直接与业务挂钩,业务的变更是很频繁的事情。

更具体的说,如果一个定制化的 SQL 查询很慢需要优化做改动,显然修改 XML 相比于修改类源码更加安全可靠。修改类源码需要重新测试,编译部署。而修改 XML 我只需要保证修改前后的 SQL 查询参数和结果往后兼容即可。无需重新部署项目。


目录结构

在进入使用环节之前,先了解一下 MyBatis 官方所推荐的目录结构:

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
/my_application
/bin
/devlib
/lib <-- MyBatis *.jar 文件在这里。
/src
/org/myapp/
/action
/data <-- MyBatis 配置文件在这里,包括映射器类、XML 配置、XML 映射文件。
/mybatis-config.xml
/BlogMapper.java
/BlogMapper.xml
/model
/service
/view
/properties <-- 在 XML 配置中出现的属性值在这里。
/test
/org/myapp/
/action
/data
/model
/service
/view
/properties
/web
/WEB-INF
/web.xml

这是使用纯 MyBatis 的时候官方所推荐的目录结构,但是日常开发中我们通常都使用 maven 构建工程。

这时候更为普遍接受的目录架构为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.
├── mybatis-project.iml
├── pom.xml
└── src
├── main
│   ├── java
│   │   └── com
│   │   └── peterxx
│   │   └── manager
│   │   └── mapper
│   │   ├── UserMapper.java
│   │   └── UserMapper.xml
│   └── resources
│   ├── jdbc.properties
│   └── mybatis-config.xml
└── test
└── java
└── TestMyBatis.java


基本使用

以 Mysql 为例,演示一下 MyBatis 的基本使用。

  1. 导入 maven 依赖

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

  2. 创建 MyBatis 的核心配置文件

    通常约定命名为 mybatis-config.xml

    真实开发中,我们的 mybatis-config.xml 并不会有这么多内容,因为与数据源和事务相关的配置,通常会整合到 Spring 中。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    <?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>
    <!-- 允许多个配置 以 </environment> 做区分,default 属性指定默认使用的配置 -->
    <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/test"/>
    <property name="username" value="root"/>
    <property name="password" value=""/>
    </dataSource>
    </environment>
    </environments>
    <!-- 注册 Mapper 文件 -->
    <mappers>
    <mapper resource="UserMapper.xml"/>
    </mappers>
    </configuration>

  3. 创建映射文件

    通常约定命名为 XXMapper.xml ,XX 对应 POJO 名称,也可以说是对应数据库表名称。
    e.g. UserMapper.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <?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.peterxx.manager.mapper.UserMapper">
    <!--
    在 SQL 中使用 #{} 传递参数,会使用 ? 进行占位,再填充得到完整的 SQL
    也就是对应 JDBC 中的 PreparedStatement 进行执行
    这时候 {} 里面填充的是参数名称 e.g. #{uid}
    -->
    <!--
    在 SQL 中使用 ${} 传递参数,会使用拼接的方式得到完整的 SQL
    也就是对应 JDBC 中的 Statement 进行执行
    这时候 {} 里面填充的是 value e.g. ${value}
    -->
    <!-- 推荐使用 #{} 传递参数 -->
    <select id="getUser" resultType="com.peterxx.domain.User">
    SELECT *
    FROM t_user
    WHERE uid = #{uid}
    </select>
    </mapper>

    注意:如果想要将 mapper.xml 文件和接口文件放在一起需要修改 pom.xml 文件。因为 maven 默认打包时只会打包 src/main/java 目录中的源文件,其他文件不会被打包。*

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    ...
    <build>
    <resources>
    <resource>
    <directory>src/main/java</directory>
    <includes>
    <include>**/*.xml</include>
    </includes>
    </resource>
    <resource>
    <directory>src/main/resources</directory>
    </resource>
    </resources>
    </build>
    ...

  4. 创建 mapper 接口,进行绑定

    1
    2
    3
    4
    5
    6
    7
    8
    public interface UserMapper {
    /**
    * 获取用户
    * @param uid 用户id
    * @return User 对象
    */
    User getUser(Integer uid);
    }

  5. 获取 MyBatis 操作数据库的 sqlSession 进行 CRUD

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public static void main(String[] args) throws IOException {
    InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
    SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
    try (SqlSession sqlSession = sessionFactory.openSession()) {
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    User user = mapper.getUser(1);
    System.out.println(user);
    }
    }

    SqlSessionFactoryBuilder :一旦使用之后就不再需要,因此使用 SqlSessionFactoryBuilder 的最佳作用域是方法作用域。随用随创建,随用随销毁

    SqlSessionFactory : 一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或重新创建另一个实例。最佳实践是在应用运行期间不要重复创建多次,最佳作用域为应用作用域

    SqlSession :每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域

    XXXMapper :映射器接口的实例是从 SqlSession 中获得的,任何映射器实例的最大作用域与请求它们的 SqlSession 相同。但方法作用域才是映射器实例的最合适的作用域。


    在调用 getMapper 方法之后,MyBatis 会在底层通过动态代理生成一个 UserMapper 的实现类。

    调用实现类的方法会执行我们在 UserMapper.xml 编写的 SQL ,并帮我们处理好 JDBC 相关的链接数据库、释放资源(连接池取还操作)和处理结果集等工作。


相关阅读

深入了解 MyBatis :详解配置

深入了解 MyBatis :ResultMap

深入了解 MyBatis :动态 SQL

深入了解 MyBatis :缓存

深入了解 MyBatis :详解配置 《画意》- 王菀之

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×