MyBatis 是什么?
MyBatis 是一款一流的支持自定义SQL、存储过程和高级映射的持久化框架。MyBatis 几乎消
除了所有的JDBC 代码,也基本不需要手工去设置参数和获取检索结果。MyBatis 能够使用简单的
XML 格式或者注解进行来配置,能够映射基本数据元素、Map 接口和POJOs(普通 java 对象)到
数据库中的记录。
准备开始
所有的MyBatis 应用都以 SqlSessionFactory 实例为中心。SqlSessionFactory 实例通过
SqlSessionFactoryBuilder 来获得,SqlSessionFactoryBuilder 能够从XML 配置文件或者通过自
定义编写的配置类(Configuration class),来创建一个 SqlSessionFactory 实例。
从 XML 中创建 SqlSessionFactory 实例
从 XML 中创建 SqlSessionFactory 实例非常简单。建议您使用类资源路径(classpath
resource)来加载配置文件,但是您也能够使用任何方式,包括文本文件路径或者以file:// 开
头 URL 的方式。MyBatis 包括一个叫做 Resources 的工具类(utility class),其中包含了一系
列方法,使之能简单地从 classpath 或其它地方加载配置文件。
String resource = "org/mybatis/example/Configuration.xml";
Reader reader = Resources.getResourceAsReader(resource);
sqlMapper = new SqlSessionFactoryBuilder().build(reader);
XML 配置文件包含 MyBatis 框架的核心设置,包括获取数据库连接的 DataSource 实例,和包
括决定事务作用域范围和控制的事务管理等。您将能够在后面的章节中找到详细的 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="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
MyBatis 3 - User Guide
7
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
虽然 XML 配置文件中还有很多其它的配置细节,但是,上面的示例显示了最重要的部分。注
意 XML 配置文件的头部,会使用DTD 验证文档来验证该 XML 配置文件。body 部分的environment
元素,包含了事务管理和连接池配置。Mappers 元素指定了映射配置文件--包含SQL 语句和映射
定义的XML 文件。
如何不使用 XML 来创建 SqlSessionFactory
如果您喜欢直接通过 java 代码而不是通过 XML 创建配置选项,或者想创建您自己的配置生成
器。MyBatis 提供了一个完整的配置类(Configuration class),它提供了与XML 文件相同的配
置选项。
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment =
new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =
new SqlSessionFactoryBuilder().build(configuration);
请注意,这种方式下的配置添加一个映射类(mapper class)。映射类是包含 SQL 映射注解
的 Java 类,从而避免了使用 XML。但是,由于注解的一些局限性以及 MyBatis 映射的复杂性,
XML 仍然是一些高级的映射功能(如嵌套连接映射,Nested Join Mapping)所必须的方式。基于
这个原因,如果存在 XML 文件,MyBatis 自动寻找并加载这个 XML 文件。在这种情况下,
BlogMapper.xml 将会被类路径下名称为BlogMapper.class 的类加载。详述请见后面章节。
从 SqlSessionFactory 获取 SqlSession
现在您已经创建了一个SqlSessionFactory(指上面的sqlMapper),正如它名字暗示那样,
您可以通过它来创建一个 SqlSession 实例。SqlSession 包含了所有执行数据库SQL 语句的方
法。您能够直接地通过SqlSession 实例执行映射 SQL 语句。例如:
SqlSession session = sqlMapper.openSession();
try {
Blog blog = (Blog) session.selectOne(
"org.mybatis.example.BlogMapper.selectBlog", 101);
} finally {
session.close();
}
虽然这种方法很有效,MyBatis 以前版本的用户对此也可能很熟悉,但现在有一个更简便的
方式,那就是对给定的映射语句,使用一个正确描述参数与返回值的接口(如
BlogMapper.class),您就能更清晰地执行类型安全的代码,从而避免错误和异常。 如:
MyBatis 3 - User Guide
8
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
} finally {
session.close();
}
现在,让我们一起探索它们究竟是如何执行的。
探索映射 SQL 语句
此时,您可能想知道 SqlSession 或者映射器类(Mapper class)是怎样执行的。映射SQL
语句是一个很大的主题,该主题将可能占据本文档的大部分内容。但是,为了让您看到它是怎样
运行的,这里举两个例子。
在上面的例子中,映射语句已经在 XML 配置文件或注解中定义。让我们首先来看看XML 配置
文件,所有MyBatis 提供的功能特性都可以通过基于XML 映射配置文件配置来实现。如果您以前
使用过MyBatis,对此就会很熟悉。但许多改进使 XML 映射文件变得更简洁清晰。下面是一个基
于 XML 映射配置的例子,满足上述SqlSession 的调用。
<?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" parameterType="int" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>
这个简单的例子看起来有不少的开销,但它确实非常地轻巧,只要您喜欢,您可以在一个映
射 XML 文件中定义许多映射语句。虽然会有一些XML 头部和DOCTYPE 声明,但该文件余下的部分
是自解(self explanatory,可理解为不加解释就能明白)的。它定义了映射语句的名称
“selectBlog”,在命名空间“org.mybatis.example.BlogMapper”,允许您通过指定完整类名
“org.mybatis.example.BlogMapper”来访问上面的例子:
Blog blog = (Blog) session.selectOne(
"org.mybatis.example.BlogMapper.selectBlog", 101);
这非常类似 java 中通过完整类名来调用方法,而这样做是有原因的。这个名称可以直接映射
到一个具在相同命名空间的映射类,这个映射类有一个方法的名称、参数及返回类型都与select
映射语句相匹配。正如您前面看到的,这使您很简单地调用映射类里的方法,下面是另外的一个
例子:
MyBatis 3 - User Guide
9
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
第二种方法有很多好处。第一,它不依赖于字符串,所以更安全。第二,如果您的 IDE 有自
动完成功能,您可以利用这功能很快导航到您的映射SQL 语句。第三,您不需要关注返回类型,
不需要进行强制转换,因为使用BlogMapper 接口已经限定了返回类型,它会安全地返回。
关于命名空间
对映射类还有一个更好的方法,就像前面的BlogMapper。它们的映射语句不需要完全在 XML
中配置。相反,它们可以使用 Java 注解。例如上面的 XML 配置可以替换为:
package org.mybatis.example;
public interface BlogMapper {
@Select("SELECT * FROM blog WHERE id = #{id}")
Blog selectBlog(int id);
}
对简单的映射语句,使用注解可以显得非常地清晰。但是 java 注解本身的局限难于应付更复
杂的语句。如果您准备要做某些复杂的事情,最好使用XML 文件来配置映射语句。
� 命名空间:在以前的版本中是可选的、复杂的且没多大用处。但在这个版本中,命名空间是必
须的,并且不仅仅是简单地使用完整类名来隔离区分语句。
正如您看到的那样,命名空间能够进行接口绑定,即使您认为现在不会使用到它,但您应该
按照这些准则来做。一旦使用了命名空间并且放入适当的 java 包命名空间中将会使您的代码清
晰,并提高MyBatis 的长期可用性。
� 名称解析: 为了减少大量地输入, MyBatis 对所有的配置元素,包括statements、result
maps、 caches 等使用下面的名称解析规则:
•
完整类名 :(例如: “com.mypackage.MyMapper.selectAllThings”)可用来直接查找并
使用。
•
短名称: (例如: “selectAllThings”)可用来引用明确的实体对象。但是,如果出现有
两个或更多(例如“com.foo.selectAllThings 和 com.bar.selectAllThings”)实体对
象,您将收到一个错误报告(短名称含糊不清),因此,这时就必须使用完整类名。
MyBatis 3 - User Guide
10
这将由您和您的项目小组来确定哪种方式是适合的,以一致的方式来定义映射语句是非常重
要的。也就是说,您不会被限于仅用一种方式。您能够非常容易地从基于注解的映射语句移植到
XML 配置文件中,反之亦然。
作用域和生命周期
理解到目前为止所讨论的类的作用域和生命周期是非常重要的。如果使用不当可导致严重的
并发性问题。
SqlSessionFactoryBuilder
这个类可以在任何时候被实例化、使用和销毁。一旦您创造了 SqlSessionFactory 就不需要
再保留它了。所以 SqlSessionFactoryBuilder 实例的最好的作用域是方法体内(即一个本地方法
变量)。您能重用 SqlSessionFactoryBuilder 创建多个 SqlSessionFactory 实例,但最好不要把
时间、资源放在解析 XML 文件上,而是要从中解放出来做最重要事情。
SqlSessionFactory
一旦创建,SqlSessionFactory 将会存在于您的应用程序整个运行生命周期中。很少或根本
没有理由去销毁它或重新创建它。最佳实践是不要在一个应用中多次创建SqlSessionFactory。
这样做会被视为“没品味”。所是SqlSessionFactory 最好的作用域范围是一个应用的生命周期
范围。这可以由多种方式来实现,最简单的方式是使用Singleton 模式或静态 Singleton 模式。
但这不是被广泛接受的最佳做法,相反,您可能更愿意使用像 Google Guice 或 Spring 的依赖注
入方式。这些框架允许您创造一个管理器,用于管理SqlSessionFactory 的生命周期。
SqlSession
每个线程都有一个 SqlSession 实例,SqlSession 实例是不被共享的,并且不是线程安全
的。因此最好的作用域是 request 或者 method。决不要用一个静态字段或者一个类的实例字段来
保存 SqlSession 实例引用。也不要用任何一个管理作用域,如 Servlet 框架中的HttpSession,
来保存SqlSession 的引用。如果您正在用一个WEB 框架,可以把 SqlSession 的作用域看作类似
于 HTTP 的请求范围。也就是说,在收到一个 HTTP 请求,您可以打开一个SqlSession,当您把
response 返回时,就可以把 SqlSession 关闭。关闭会话是非常重要的,您应该要确保会话在一
个 finally 块中被关闭。
SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
MyBatis 3 - User Guide
11
在您的代码里都使用这一模式将保证所有的数据库资源被正确地关闭(假如您没有把您自己
的数据库连接传递给 MyBatis 管理,这就对 MyBatis 表明您希望自己管理连接)。
Mapper 实例
Mappers 是创建来绑定映射语句的接口,该 Mapper 实例是从 SqlSession 得到的。因此,所
有 mapper 实例的作用域跟创建它的 SqlSession 一样。但是,mapper 实例最好的作用域是
method,也就是它们应该在方法内被调用,使用完即被销毁。并且mapper 实例不用显式地被关
闭。虽然把mapper 实例保持在一个request 范围(与 SqlSession 相似)不会产生太大的问题,
但是您可能会发现,在这个层次上管理太多资源可能会失控。保持简单,就是让Mappers 保持在
一个方法内。下面的例子演示了这种做法。
SqlSession session = sqlSessionFactory.openSession();
try {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// do work
} finally {
session.close();
}