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 00:00>

Last updated: <2018-09-23 Sun 12:01>

Creator: Emacs 25.3.1 (Org mode 9.1.4)