使用mybatis的时候会配置sqlSessionFactoryBean,将数据源传入。我对于mybatis的理解是当 处理持久层的请求时,ThreadLocal 内会持有一个数据源的jdbc 链接。这样 该线程的所有数据操作都是用一个链接来操作。完后关闭链接。debug的目的是看下到底是怎么做的。
SqlSessionFactoryBean内有很多对象,debug下来发现最关键是configuration对象。他持有用户配置的datasource。在afterPropertiesSet()方法中打上断点。
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
启动程序 进入断点
这个方法调用过程是spring创建bean的过程。
进入buildsqlSessionFactory(),很长,主要内容是通过配置来初始化configuration。然后通过configuration来创建一个sqlSessionFactory。
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
//删除配置代码
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
//删除配置代码
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
//删除配置代码
return this.sqlSessionFactoryBuilder.build(configuration);
}
最后sqlSessionFactoryBuilder.build new了一个DefaultSqlSessionFactory。顾名思义他的作用就是sqlSession的创建工厂
创建的sqlSession内持有Environment对象,内含我们配置的datasource 和TransactionFactory
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();
}
}
afterPropertiesSet()这个方法结束 就是为sqlSessionFactoryBean 创建了一个sqlSessionFactory。现在看另一个重要的类
MapperFactoryBean 他是用来创建mapper接口的实现类的,它继承自SqlSessionDaoSupport
SqlSessionDaoSupport 的setSqlSessionFactory 方法会根据sqlSessionFactory来创建一个SqlSessionTemplate
该类是SqlSession的实现类,持有SqlSessionFactory对象。和一个动态代理SqlSession的对象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());
}
上面是new一个SqlSessionTemplate 同时new一个SqlSession的动态代理对象SqlSessionProxy
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;
//删除异常处理代码
}
这是一个典型的jdk动态代理。SqlSessionTemplate 并不用来执行数据操作语句而是由通过getSqlsession来创建的sqlsession执行了数据操作后加入了commit。创建的过程就会打开一个链接放入sqlsession.getSqlsession获取的是defaultSqlSession对象他就是被放在threadLocal 的 。
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;
}
然后调用sqlsession。getMapper来得到一个Dao对象。sqlsession的getMapper内部调用Configuration对象的getMapper,它的getMApper有调用mapperRegistry对象的getMapper.
@Override
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
try {
MappedStatement ms = configuration.getMappedStatement(statement);
return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
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 BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
newInstance用来创建一个动态代理将 dao接口的方法代理为sqlSession来执行
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
mapperProxy的invoke为
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
try {
return method.invoke(this, args);
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
final MapperMethod mapperMethod = cachedMapperMethod(method);
return mapperMethod.execute(sqlSession, args);
}
上面是启动过程,再看一个数据操作的过程
一个dao的方法由上分析应该新进入mapperProxy的代理类在他的invoke出打断点
该代理类处理完参数封装为一个方法然后执行sqlSession的响应数据操作方法,然而他也被SqlSessionTemplate 的SqlSessionProxy代理同样进入
这里执行完后 判断是否是事务,不是的话直接commit。执行的整体流程大致如此,并没有如自己所想有ThreadLocal 来管理session。查找sqlsession接口的实现类有一个SqlSessionManager 该类里面有
private ThreadLocal<SqlSession> localSqlSession = new ThreadLocal<SqlSession>();
但是我的程序没有用这个类,而是用的SqlSessionTemplate 来动态代理DefaultSqlSession。
DefaultSqlSession 是实际执行数据操作的,而SqlSessionManager 和SqlSessionTemplate 都是代理。他们都可以保证线程安全。即可以保证同一个线程使用的的DefaultSqlSession 是一个。SqlSessionManager 是mybatis.session 包里面的 不依赖与spring 通过ThreadLocal 来保存DefaultSqlSession 。而SqlSessionTemplate 是mybatis-spring 包里面的。他依赖于spring。spring的TransactionSynchronizationManager类 里面有ThreadLocal 的map,可以存储当前线程的资源。SqlSessionTemplate 就是借助这个 从而不用在写一个ThreadLocal。
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;
}