MyBatis (Java Persistence Framework)

Table of Contents

1. MyBatis 简介

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。

和 JPA 产品相比,MyBatis 更加灵活,比如 MyBatis 很容易实现多表联合查询等功能。

1.1. 安装

要在 Maven 工程中使用 MyBatis,在 pom.xml 文件中加入下面依赖即可:

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>x.x.x</version>
</dependency>

2. MyBatis 基本使用

2.1. 第一步:创建 SqlSessionFactory

每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的,SqlSessionFactory 的实例可以通过 SqlSessionFactoryBuilder 获得。

有两种方式可以创建 SqlSessionFactory。方式一:从 XML 文件创建 SqlSessionFactory;方式二:从配置类创建 SqlSessionFactory。

这里仅介绍从 XML 中构建 SqlSessionFactory 的方法:

// 注:应用(只访问一个数据库)往往只需要一个sqlSessionFactory对象,所以常常设计为单例模式
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

上面例子中,数据库相关配置保存在文件"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>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/your_db_name"/>
        <property name="username" value="root"/>
        <property name="password" value="your_pw"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="BlogMapper.xml"/>
  </mappers>
</configuration>

2.2. 第二步:创建 SQL 语句映射

上一步中,文件"mybatis-config.xml"中下面代码片断:

  <mappers>
    <mapper resource="BlogMapper.xml"/>
  </mappers>

文件"BlogMapper.xml"指定的是 SQL 语句映射,下面是该文件的一个实例:

<?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="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="org.mybatis.example.Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

注:我们不仅可以在 xml 文件中指定 SQL 语句映射,还可以在 Java 类中指定 SQL 语句映射,下面是它的一个简单实例:

package org.mybatis.example;

public interface BlogMapper {
  @Select("SELECT * FROM blog WHERE id = #{id}")
  Blog selectBlog(int id);
}

本文不介绍这种方式。

2.3. 第三步:创建和数据库表对应的 POJO

假设数据库表 blog 有两个字段 id 和 title,我们需要构建与之对应的 Java 类。如:

package org.mybatis.example;

public class Blog {
    private int id;
    private String title;

    public int getId() {
        return id;
    }

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

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

2.4. 第四步:从 SqlSessionFactory 获取 SqlSession,执行 SQL 语句

从 SqlSessionFactory 获取 SqlSession,执行 SQL 语句:

SqlSession session = sqlSessionFactory.openSession();
try {
  // 下面这一句可以把数据库中id为101的记录查询出来,并根据结果创建一个Blog对象
  Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
                                       // \_________________________/   \_______/
                                       //            ^                       ^
                                       //            |                       |
                                       // namespace in BlogMapper.xml     id in BlogMapper.xml
} finally {
  session.close();
}

2.5. 实例:对用户信息进行增删改查

链接 MyBatis Tutorial – CRUD Operations and Mapping Relationships – Part 1 中有一个使用 MyBatis 对用户信息进行增删改查的例子,比较简单,可作为入门学习的资料。

3. Tips

3.1. Order By 排序问题(MyBatis 中#和$的区别)

假设数据库表 tbl1 的内容如下:

mysql> select * from tbl1;
+-------+------+
| name  | age  |
+-------+------+
| Herry |   20 |
| Jack  |   40 |
| Alice |   35 |
+-------+------+
3 rows in set (0.00 sec)

现在我们想按列 age 排序。首先在 Mapper XML 文件中定义下面内容:

  <select id="selectAll" resultType="com.xxx.yyy.User">
    SELECT * FROM tbl1 ORDER BY #{columnName}              <!-- 错! -->
  </select>

然后,Java 代码中传参 columnName 为'age'。

String columnName = "age";
List<User> users = session.selectList("org.mybatis.example.UserMapper.selectAll", columnName);

但是,你会发现,得到的结果并没有按 age 排序!

要得到排序后的结果,可以把 Mapper XML 文件中的 #{columnName} 改为 ${columnName} ,即:

  <select id="selectAll" resultType="com.xxx.yyy.User">
    SELECT * FROM tbl1 ORDER BY ${columnName}              <!-- 正确! -->
  </select>

为什么会这样呢,这是因为 在 MyBatis 中, #{} 相当于 jdbc 中的 prepareStatement 中的占位符(会自动转义,不会有 SQL 注入风险,这个例子中自动加上了单引号);而 ${} 是直接输出变量的值(它有 SQL 注入风险,所以用户的输入数据千万不能直接作为这些变量值的来源)。

也就是说 SELECT * FROM tbl1 ORDER BY #{columnName}SELECT * FROM tbl1 ORDER BY ${columnName} 会分别转换为:

select * from tbl1 order by 'age';
select * from tbl1 order by age;

下面在 MySQL 中分别测试一下这两个语句:

mysql> select * from tbl1 order by 'age';     -- 结果并没有按age排序,错误地使用order by
+-------+------+
| name  | age  |
+-------+------+
| Herry |   20 |
| Jack  |   40 |
| Alice |   35 |
+-------+------+
3 rows in set (0.00 sec)

mysql> select * from tbl1 order by age;       -- 结果按age排序
+-------+------+
| name  | age  |
+-------+------+
| Herry |   20 |
| Alice |   35 |
| Jack  |   40 |
+-------+------+
3 rows in set (0.00 sec)

Author: cig01

Created: <2018-08-11 Sat>

Last updated: <2018-09-23 Sun>

Creator: Emacs 27.1 (Org mode 9.4)