从上面这篇文章中,我们知道,mybatis为dao类生成了一个代理对象,最终调用SqlSession的方法来执行sql。那SqlSession是如何产生的呢,当然是通过SqlSessionFactory创建的啦,那SqlSessionFactory又是如何产生的呢,当然是通过SqlSessionFactoryBuilder创建的啦。
SqlSessionFactory有2个实现类,DefaultSqlSessionFactory和SqlSessionManager。SqlSessionManager比较特殊,它同时还实现SqlSession,后面再分析它。
在分析解析mybatis-config.xml的时候,我们知道SqlSessionFactoryBuilder返回的是DefaultSqlSessionFactory实例。
SqlSession有3个实现类,分别是DefaultSqlSession、SqlSessionManager、SqlSessionTemplate,后2者其实也是通过代理的方式,最终由DefaultSqlSession实现功能。
那么,SqlSession是如何得到的呢?答案是DefaultSqlSessionFactory.openSession,如下
@Override
public SqlSession openSession() {
return openSessionFromDataSource(configuration.getDefaultExecutorType(), null, false);
}
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);
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();
}
}
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有很多重写的方法,最根本还是openSessionFromDataSource和openSessionFromConnection,一个是通过数据源打开,另一个是通过连接打开。
从上面代码可以看到,首先通过environment获取事务管理器,在解析mybatis-config.xml的时候,已经设置了事务管理器为SpringManagedTransactionFactory,然后获取Transaction,从下面代码可以看到,通过connection获取事务的方法是不支持的,所以走的还是openSessionFromDataSource。这里返回的是SpringManagedTransaction。
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
return new SpringManagedTransaction(dataSource);
}
@Override
public Transaction newTransaction(Connection conn) {
throw new UnsupportedOperationException("New Spring transactions require a DataSource");
}
接着,通过事务Transaction和ExecutorType创建Executor,如下
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
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);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
可以看到,不同的ExecutorType对应不用的Executor,这里用的是策略模式。同时,还会把Executor绑定到拦截器的链条里。
最后 ,把Executor绑定到DefaultSqlSession,并设置是否自动提交,然后返回。
好了,获取到了DefaultSqlSession,那怎么执行sql呢,我们回到MapperProxy.invoke看起。如下
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else if (isDefaultMethod(method)) {
return invokeDefaultMethod(proxy, method, args);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
MapperMethod mapperMethod = methodCache.get(method);
if (mapperMethod == null) {
mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
methodCache.put(method, mapperMethod);
}
return mapperMethod;
}
重点在new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());,这里的缓存也很重要,因为new MapperMethod做了很多事情,如下
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new SqlCommand(config, mapperInterface, method);
this.method = new MethodSignature(config, mapperInterface, method);
}
这里初始化SqlCommand和MethodSignature。
SqlCommand是MapperMethod的一个内部类,2个属性,name和type,如下
public static class SqlCommand {
private final String name;
private final SqlCommandType type;
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
String statementName = mapperInterface.getName() + "." + method.getName();
MappedStatement ms = null;
if (configuration.hasStatement(statementName)) {
ms = configuration.getMappedStatement(statementName);
} else if (!mapperInterface.equals(method.getDeclaringClass())) { // issue #35
String parentStatementName = method.getDeclaringClass().getName() + "." + method.getName();
if (configuration.hasStatement(parentStatementName)) {
ms = configuration.getMappedStatement(parentStatementName);
}
}
if (ms == null) {
if(method.getAnnotation(Flush.class) != null){
name = null;
typ