mybatis 工作原理
几个核心类
参见:
SqlSessionFactory
mybatis 应用以一个 sqlSessionFactory 实例为核心,即一个应用中有一个单例 SqlSessionFactory
,所以数据库 session 都从这里获得。
SqlSessionFactory
可以通过 SqlSessionFactoryBuilder
获得,builder 负责从 xml 配置或 java configuration 类获得。xml (或相应的 java configuration 类) 配置了 datasource(数据库连接信息)、mappers 等信息
SqlSessionFactoryBuilder
它主要就是用来获取 SqlSessionFactory
,可以从 xml 或 Java Configuration 类加载配置并构建。提供如下几种方式来获取(参见java api):
1 | // 从 xml 获取,其中配置了 environment,datasource,mappers |
SqlSessionFactoryBuilder
只是为了创建 SqlSessionFactory
,创建完成就可以丢弃 builder 了。所以一般它的生命周期是方法级,是其中的一个局部变量
使用 xml
先配置一个 config.xml
:
1 |
|
上边的配置中,environments
配置的是各个环境下的数据库配置。每个环境下,都可以配置 TransactionManager、datasource 等,连接数据库、包括操作数据库的 driver 等信息都是在这里配置的)。
${driver}
这种写法是用的 property。property 可以直接在这个 xml 中配置(使用 <properties>
标签),也是 java 中的 System.getProperties()
中的 property,还可以是在 SqlSessionBuilder
中传递的 props
参数。
构建 SqlSessionFactory
:
1 | String resource = "org/mybatis/example/mybatis-config.xml"; |
使用 java Configuration 类
1 | DataSource dataSource = BlogDataSourceFactory.getBlogDataSource(); |
SqlSession
SqlSession
是执行 sql 命令的接口:
1 | SqlSession session = sqlSessionFactory.openSession(); |
SqlSession
可以执行所有的 sql 命令、做事务提交、回滚、获取 Mapper 实例等,非常强大。SqlSession
也是方法作用域级别的,并且必须被正确关闭:
每个线程都应该有它自己的 SqlSession 实例。SqlSession 的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将 SqlSession 实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。也绝不能将 SqlSession 实例的引用放在任何类型的管理作用域中,比如 Servlet 架构中的 HttpSession。如果你现在正在使用一种 Web 框架,要考虑 SqlSession 放在一个和 HTTP 请求对象相似的作用域中。换句话说,每次收到的 HTTP 请求,就可以打开一个 SqlSession,返回一个响应,就关闭它。这个关闭操作是很重要的,你应该把这个关闭操作放到 finally 块中以确保每次都能执行关闭。下面的示例就是一个确保 SqlSession 关闭的标准模式:
1
2
3
4
5
6 SqlSession session = sqlSessionFactory.openSession();
try {
// do work
} finally {
session.close();
}
Mapper
Mapper
是资源和数据库实例的映射,提供了相关操作来做转换。可以用两种方式写:xml 或 annotation。
mapper 实例从 SqlSession
获得,所以生命周期和 SqlSession 相同。
xml
1 |
|
访问:
1 | Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); |
当然,我们希望通过 java 接口来调用这些方法,所以可以写相应的 mapper
接口 ,只要保证 namespace
是一致的即可。namespace
是实现接口绑定的方式。mybatis 基于命名空间的命名解析规则如下:
- 完全限定名(比如“com.mypackage.MyMapper.selectAllThings”)将被直接查找并且找到即用。
- 短名称(比如“selectAllThings”)如果全局唯一也可以作为一个单独的引用。如果不唯一,有两个或两个以上的相同名称(比如“com.foo.selectAllThings ”和“com.bar.selectAllThings”),那么使用时就会收到错误报告说短名称是不唯一的,这种情况下就必须使用完全限定名。
一旦绑定了接口,就可以用如下方式访问 mapper 方法了:
1 | BlogMapper mapper = session.getMapper(BlogMapper.class); |
annotation
也可以不依赖于 xml,直接在 mapper 接口上通过 annotation 定义 sql:
1 | package org.mybatis.example; |
annotation 可能更简洁一些,但是 mybatis 目前还是 xml 更强大。大家可以依据需求自由的在两种方式之间切换,mybatis 会自己检测。
由于 Java 注解的一些限制加之某些 MyBatis 映射的复杂性,XML 映射对于大多数高级映射(比如:嵌套 Join 映射)来说仍然是必须的。有鉴于此,如果存在一个对等的 XML 配置文件的话,MyBatis 会自动查找并加载它(这种情况下, BlogMapper.xml 将会基于类路径和 BlogMapper.class 的类名被加载进来)