继上一篇博客https://blog.youkuaiyun.com/Riztal/article/details/109000564我们讲了mybatis加载全局配置文件和映射文件的过程。
以下是mybatis简单的使用步骤。
①final String resource = "org/apache/ibatis/builder/MapperConfig.xml";
②final Reader reader = Resources.getResourceAsReader(resource);
③manager = SqlSessionManager.newInstance(reader);
④SqlSession sqlSession = manager.openSession();
⑤BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
⑥List<Map> maps = mapper.selectAllPosts();
接下来我们讲步骤④SqlSession sqlSession = manager.openSession();
1)sqlsession执行主流程
①SQLSessionFactory:开启SQLSession会话的工厂
/**
* 构建SqlSession的工厂.工厂模式
*
*/
public interface SqlSessionFactory {
//8个方法可以用来创建SqlSession实例
SqlSession openSession();
//自动提交
SqlSession openSession(boolean autoCommit);
//连接
SqlSession openSession(Connection connection);
//事务隔离级别
SqlSession openSession(TransactionIsolationLevel level);
//执行器的类型
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType, Connection connection);
Configuration getConfiguration();
}
②SQLSessionFactory的实现类:DefaultSqlSessionFactory
提供了openSessionFromDataSource,openSessionFromConnection两个方法
openSessionFromDataSource
1)获取环境
2)创建事务
3)生成执行器
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
//通过事务工厂来产生一个事务
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
//生成一个执行器(事务包含在执行器里)
final Executor executor = configuration.newExecutor(tx, execType);
//然后产生一个DefaultSqlSession
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
//如果打开事务出错,则关闭它
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
//最后清空错误上下文
ErrorContext.instance().reset();
}
}
1)和2)都是在加载全局配置文件时已经创建好了XmlConfigBuilder.parse()#environmentsElement方法
//7.环境
// <environments default="development">
// <environment id="development">
// <transactionManager type="JDBC">
// <property name="..." value="..."/>
// </transactionManager>
// <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>
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
//循环比较id是否就是指定的environment
if (isSpecifiedEnvironment(id)) {
//7.1事务管理器
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//7.2数据源
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)//事务
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());//环境
}
}
}
}
3)生成执行器,我们发现BatchExecutor/ReuseExecutor/SimpleExecutor/CachingExecutor四种执行器
//产生执行器
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
//这句再做一下保护,囧,防止粗心大意的人将defaultExecutorType设成null?
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
//然后就是简单的3个分支,产生3种执行器BatchExecutor/ReuseExecutor/SimpleExecutor
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
//如果要求缓存,生成另一种CachingExecutor(默认就是有缓存),装饰者模式,所以默认都是返回CachingExecutor
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
//此处调用插件,通过插件可以改变Executor行为
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
openSessionFromConnection原理也是一样
private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {
try {
boolean autoCommit;
try {
autoCommit = connection.getAutoCommit();
} catch (SQLException e) {
// Failover to true, as most poor drivers
// or databases won't support transactions
autoCommit = true;
}
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
final Transaction tx = transactionFactory.newTransaction(connection);
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
这就是openSession的执行过程,一句话概括就是:通过配置的环境信息获取事务,并且生成执行器,用于执行sql语句。
接下来我们讲步骤⑤BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);获取mapper对象,不用看就知道用了代理的方式。补充一点,其实我们获取到SQLSession以后,也可以通过
Statement statement = sqlSession.getConnection().createStatement();
statement.executeQuery("sqlcode ");
这种方式去执行sql语句,这是原生jdbc代码方式。
2)获取mapper代理对象流程
进入getMapper方法我们可以定位到,可以发现是通过jdk动态代理获取mapper对象,面试必考
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingExcept