目前项目中大多采用springboot那一套,今天想具体了解mybatis中的工作原理。
mybatis中有两个主要的工作对象,一个是SqlSessionFactory,一个是SqlSession。
这两者有什么关系呢?
具体的大致步骤是这样的:
- 程序中预先配置mybatis的xml文件
- SqlSessionFactoryBuilder通过配置好的xml文件来构建SqlSessionFactory,SQLSessionFactory是线程安全的,一旦被创建,应用在执行期间都会存在
- SqlSessionFactory来创建SqlSession对象
- SqlSession是mybatis的关键对象,是应用程序与持久储存层交互的一个单线程对象(因为它是非线程安全的),sqlSession去操作sql语句(观察上面的流程图,我估计sqlSession就是去调用我们常写的mapper里的那些增删改查),使用结束之后,还需要使用finally进行关闭连接
/*获取当前mybatis配置文件,通过Mybatis包中的Resources对象很轻松的获取到配置文件*/
//InputStream rs = Thread.currentThread().getContextClassLoader().getResourceAsStream("mybatis-config.xml");这种方式也 可以
Reader rs= Resources.getResourceAsReader("mybatis-config.xml");
/*创建sqlSessionFactory对象*/
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(rs);
/*创建SQLSession对象操作持久层对象*/
SqlSession sqlSession = sqlSessionFactory.openSession();
sqlSession.getMapper(xxxMaper.class).getXXXList();
那接着来看,mybatis的这个预先的配置文件中应该有哪些内容呢?
主要包括这几个方面:
- properties:将数据库信息单独写入一个jdbc.properties文件中,方便维护
- settings:一般设置一些全局配置参数,比如开启二级缓存,开启延迟加载等
- typeAliases:存在的意义仅在于用来减少类完全限定名的冗余
<typeAliases>
<typeAlias type="com.hp.entity.member" alias="mb"/>
</typeAliases>
我们在mapper文件中就可以使用它的别名:
<select id="Login" resultType="mb">
select * from userinfo where username=#{0} and password=#{1} and role=#{2};
</select>
- environments、environment、dataSource
<environments default="development">
<!--数据库配置环境1 -->
<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/xxx"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
<!--数据库配置环境2 -->
<environment id="release">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
...
</dataSource>
</environment>
</environments>
environments default 属性来决定使用哪个数据库的配置
DataSource 这里介绍它的几个类型,分别是UNPOOLED、POOLED和JNDI。
UNPOOLED 它只有在每次请求的时候才打开和关闭连接。
POOLED 利用“池”的概念将JDBC的连接对象组织起来,不需要每次请求创建新的连接实例,节约了初始化和认证时间,对于要求快速响应的程序,推荐使用这个连接池类型。
JNDI
- mappers:定义sql映射语句。需要告诉 MyBatis 到哪里去找到这些语句,一般两种方案:
<mappers>
<!--直接映射到相应的mapper文件-->
<mapper resource="com/xhm/mapper/UserMapper.xml"/>
<!--扫描包路径下所有xxMapper.xml文件-->
<package name="com.xhm.mapper"/>
</mappers>
完整的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>
<!--
properties标签用来引入外部properties文件
resource:引入类路径下的资源
url:引入网络路径或者磁盘路经下的资源
-->
<properties resource="jdbc.properties" />
<!--
settings包含了很多重要的设置项
name:项目名
value:项目值
可在官方的参考文档中查询
-->
<settings>
<setting name="jdbcTypeForNull" value="NULL" />
<setting name="lazyLoadingEnabled" value="true" />
<setting name="aggressiveLazyLoading" value="false" />
</settings>
<typeAliases>
<!--
typeAlias为某个java类起别名
type:指定要起别名的类的全限定名;默认别名为类名小写
alias:自定义别名
-->
<typeAlias type="mytest.domain.Product" alias="product"/>
<!--
package为某个包中的所有类批量起别名(包括子包下的类,别名默认为类名小写)
name:指定包名
-->
<package name="mytest.domain" />
<!-- 在批量其别名情况下可以使用@Alias注解为特定的类自定义别名 -->
</typeAliases>
<!--
environments环境配置 {default:指定环境(指定环境的id)}
environment环境 {id:环境的唯一标识}
transactionManager:事务管理器 {type:事务管理器类型}
dataSource:数据源 {type:数据源类型}
-->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}" />
<property name="url" value="${mysql.url}" />
<property name="username" value="${mysql.username}" />
<property name="password" value="${mysql.password}" />
</dataSource>
</environment>
<environment id="oracle">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${oracle.driver}" />
<property name="url" value="${oracle.driver}" />
<property name="username" value="${oracle.driver}" />
<property name="password" value="${oracle.driver}" />
</dataSource>
</environment>
</environments>
<!--
databaseIdProvider支持多数据库厂商
type="DB_VENDOR":VendorDatabaseIdProvider
作用是得到数据库厂商的标识,mybatis就能执行不同的Sql语句
-->
<databaseIdProvider type="DB_VENDOR">
<!-- 为不同的数据库厂商起别名 -->
<property name="MySQL" value="mysql" />
<property name="Oracle" value="oracle" />
</databaseIdProvider>
<mappers>
<!--
mapper注册一个Sql映射
注册映射文件
resource:引用类路径下的sql映射文件;
url:引用本地磁盘或网络路径的sql映射文件;
注册接口
class:引用(注册)接口
条件:1.有sql映射文件,映射文件需要和接口同名且在同一个目录下
2.无sql映射文件,使用注解@Select(Sql)...写在方法上
package批量注册(class批量化,条件和class一样)
name:批量注册指定包中的接口
-->
<package name="mytest.dao" />
</mappers>
</configuration>
后面再学习的过程中,接触了ssm,那么spring中是怎么整合的mybatis的呢,通过引入了SqlSessionFactoryBean,下面来了解一下SqlSessionFactoryBean:
SqlSessionFactoryBean来代替SqlSessionFactoryBuilder创建sqlSession。
SqlSessionFactoryBean有一个必须属性dataSource,另外其还有一个通用属性configLocation,结合我之前写的代码:
SqlSessionFactoryBean实现了FactoryBean接口,重写了getObject接口 ,通过该方法返回SqlSessionFactory对象
SqlSessionFactoryBean实现了InitializingBean接口,spring初始化的时候会执行实现了InitializingBean接口的afterPropertiesSet方法
SqlSessionFactoryBean实现了ApplicationListener接口,在spring容器执行的各个阶段进行监听,SqlSessionFactoryBean实现这个接口是为了容器刷新的时候,更新sqlSessionFactory
简单看一下入口方法afterPropertiesSet():
@Override
public void afterPropertiesSet() throws Exception {
//dataSource是必须要配置的
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
//configuration是bean,configLocation是配置文件,两者不能同时配置
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
//主要逻辑都在buildSqlSessionFactory方法,创建sqlSessionFactory,getObject就是返回的sqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
@Override
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
关键方法buildSqlSessionFactory(),主要包含下面几步:
- 构建configuration对象
- 设置数据源dataSource
- 解析configLocation
- 解析mapperLocations
- 设置其他属性
- 构建sqlSessionFactory对象
(具体细节下次在分析)