可见,sqlSession对象的初始化是new 出了SqlSessionTemplate对象,我们看SqlSessionTemplate源码:
首先,看其属性:
private final SqlSessionFactory sqlSessionFactory;
private final ExecutorType executorType;
private final SqlSession sqlSessionProxy;
private final PersistenceExceptionTranslator exceptionTranslator;
可以看出,SqlSessionTemplate类本身是SqlSession对象,而其成员属性也有一个SqlSession对象,说明这是装饰者模式,干活的肯定是sqlSessionProxy对象,下面看sqlSessionProxy的初始化:
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
this.sqlSessionProxy = (SqlSession) newProxyInstance(
SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class },
new SqlSessionInterceptor());
}
可以看出,sqlSessionProxy 是通过jdk动态代理生成的代理对象,重点分析InvocationHandler的逻辑,这里是对方法的加强,所以看SqlSessionInterceptor源码:
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession sqlSession = getSqlSession(
SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType,
SqlSessionTemplate.this.exceptionTranslator);
try {
Object result = method.invoke(sqlSession, args);
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
先查看getSqlSession方法源码,该方法调用的是SqlSessionUtils类里的方法:
public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
SqlSession session = sessionHolder(executorType, holder);
if (session != null) {
return session;
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Creating a new SqlSession");
}
session = sessionFactory.openSession(executorType);
registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
return session;
}
分析其源码,先分析这段:
SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
看getResource方法源码:
public static Object getResource(Object key) {
Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
Object value = doGetResource(actualKey);
if (value != null && logger.isTraceEnabled()) {
logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" + Thread.currentThread().getName() + "]");
}
return value;
}
再看doGetResource方法源码:
private static Object doGetResource(Object actualKey) {
Map
可见,最终返回的是resources属性中的一个值,我们看resources的类型:
private static final ThreadLocal
可见,这是一个ThreadLocal类,也就是说,SqlSessionUtils类中的getSqlSession方法,先从ThreadLocal中获取当前线程的SqlSession对象,如果有,则直接返回,如果没有,则运行如下代码,创建sqlSession对象:
session = sessionFactory.openSession(executorType);
因此,对于一个mapper接口而言,一个线程中只存在一个sqlsession对象。
我们继续分析代理对象sqlSessionProxy的代理参数InvokeHandler之SqlSessionInterceptor 源码:
finally {
if (sqlSession != null) {
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
可见,最终,要对sqlsession进行关闭操作。所以,对于sqlsession而言,就是一个线程的生命周期,在一个线程中创建,并在一个线程中关闭。
- getMapper方法研究:
上面我们分析了MapperFactoryBean类中getObject方法中getSqlSession方法的来龙去脉,可知一个线程获取到了一个SqlSession对象,下面我们继续研究getObject方法中getMapperr方法的源码:
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
从代码来看getMapper是讲Mapper接口作为参数传入,然后返回其代理对象到Spring容器中,看其源码:
因为MyBatis和Spring融合了,所以看Spring提供的SqlSessionTemplate中的getMapper方法,一顿跳入,最终点进了这个方法:
public T getMapper(Class type, SqlSession sqlSession) {
final MapperProxyFactory mapperProxyFactory = (MapperProxyFactory) 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 BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
可以看到,getMapper方法首先获得了MapperProxyFactory对象,然后调用MapperProxyFactory的newInstance方法,获得Mapper的代理对象,看MapperProxyFactory源码:
public class MapperProxyFactory {
private final Class mapperInterface;
private final Map methodCache = new ConcurrentHashMap();
public MapperProxyFactory(Class mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class getMapperInterface() {
return mapperInterface;
}
public Map getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy mapperProxy = new MapperProxy(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
可以看出,newInstance方法也是用了jdk的动态代理,生成的代理类,看主要参数InvocationHandler的实现类MapperProxy的invoke方法:
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);
}
最终,调用mapper的方法,进行执行。至于代理类是如何从xml中获取到sql,然后形成语句,发送数据库执行的,这里不做研究。
总结
从上面可以看到,MyBatis的设计思路大致如下:
1.程序员手动或自动(SpringBoot项目)配置SqlSessionFactoryBean类到Spring容器,SqlSessionFactoryBean是一个FactoryBean,通过getObject方法将SqlSessionFactory加入到Spring容器中,同时也将MyBatis的大杂烩Configuration对象加入到Spring容器。所以,SqlSessionFactory是伴随项目运行始终的,所以,二级缓存就放在了SqlSessionFactory中,只要Spring容器不销毁,SqlSessionFactory就一直存在。在分布式场景下,多个项目,就是多个Spring容器,也就是多个SqlSessionFactory,所以,并不能公用二级缓存,在分布式业务场景下,MyBatis的二级分布式缓存就显得很鸡肋。
2.程序员配置MapperScannerConfigurer类和扫描包,MapperScannerConfigurer是一个BeanDefinitionRegistryPostProcessor,可以注册BeanDefinition对象,其扫描到一个mapper后,就会注册一个BeanDefinition对象,注册的BeanDefinition是MapperFactoryBean类对象,mapper接口以mapperinterfaces属性存入MapperFactoryBean中。
3.MapperFactoryBean类也是个FactoryBean对象,其getObject方法将mapper的代理类放入到Spring容器中。
4.MapperFactoryBean类是通过SqlSession对象的代理对象获取到的mapper的代理对象,在一个线程中,一个mapper只会有一个sqlsession对象,也只生成一个mapper的代理对象,当线程方法运行完后,会把sqlsession对象关闭,所以,每个线程都是新的sqlsession对象和新的mapper代理对象。
5.sqlsession对象也用代理对象的目的是为了加强sqlsession对象,对其进行关闭等操作,所以用了代理。二级缓存在sqlsession中,所以,每个线程有自己的二级缓存,不能公用,而在一个线程中,同一个方法查询两次的操作也不多,所以说,MyBatis的二级缓存也很鸡肋。



