目录
一. mybatis开放接口
SqlSession 初始化
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
List list = sqlSession.selectList("com.foo.bean.BlogMapper.queryAllBlogInfo");
Mybatis的初始化就是加载配置文件,构造Configuration对象:1. 解析子节点的方法mapperElements(root.evalNode(“mappers”)), 以key=”com.foo.bean.BlogMapper.queryAllBlogInfo” ,value为MappedStatement对象的形式维护到Configuration的一个Map中;2. 解析environments节点,创建事务工厂 TransactionFactory和数据源DataSource
传统的MyBatis提供的API—SqlSession
SqlSession是MyBatis提供的顶层接口,底层实现简单描述如下:
(1、根据传递的参数,完成SQL语句的动态解析,生成BoundSql对象,供StatementHandler使用;
(2、为查询创建缓存,以提高性能;
(3、创建JDBC的Statement连接对象,传递给StatementHandler对象,返回List查询结果。
List/int/void sqlSession.select/selectList/insert/update/delete(statementId, paramObject)
使用Mapper接口—通过动态代理实现对SqlSession接口的封装
SqlSession sqlSession = SessionFactory.getSqlSession(resource);
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
对UserMapper接口的调用通过动态代理最终还是调到SqlSession接口,下面看下如何构造代理对象:
sqlSession.getMapper(UserMapper.class) => mapperRegistry.getMapper(type, sqlSession) => mapperProxyFactory.newInstance(sqlSession)
mapperRegistry保存UserMapper接口到mapperProxyFactory实例的映射,mapperProxyFactory可以创建mapperProxy对象
然后交由MapperMethod将参数传递给sqlSession,调用sqlSession的接口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); } public Object execute(SqlSession sqlSession, Object[] args) { Object result; if (SqlCommandType.INSERT == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.insert(command.getName(), param)); } else if (SqlCommandType.UPDATE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.update(command.getName(), param)); } else if (SqlCommandType.DELETE == command.getType()) { Object param = method.convertArgsToSqlCommandParam(args); result = rowCountResult(sqlSession.delete(command.getName(), param)); } else if (SqlCommandType.SELECT == command.getType()) { if (method.returnsVoid() && method.hasResultHandler()) { executeWithResultHandler(sqlSession, args); result = null; } else if (method.returnsMany()) { result = executeForMany(sqlSession, args); } else if (method.returnsMap()) { result = executeForMap(sqlSession, args); } else { Object param = method.convertArgsToSqlCommandParam(args); result = sqlSession.selectOne(command.getName(), param); } } else { throw new BindingException("Unknown execution method for: " + command.getName()); } if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) { throw new BindingException("Mapper method '" + command.getName() + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ")."); } return result; }
二. MapperScannerConfigurer自动扫描包
MapperFactoryBean返回mapper接口
MapperFactoryBean实现FactoryBean,实际返回的是mapper接口的代理实现,即其实现作为bean被加入到spring容器中
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
MapperFactoryBean调用configuration.addMapper将配置的接口mapperInterface加入到Configuration,MapperFactoryBean是如何找到mapper的配置文件呢?
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Throwable t) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
throw new IllegalArgumentException(t);
} finally {
ErrorContext.instance().reset();
}
}
configuration.addMapper中会调用parse方法,加载mapper文件,mapper文件的地址默认与Mapper接口路径一致
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
parseStatement(method);
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
SqlSessionFactoryBean创建Configuration和SqlSessionFactory,参考
- 首先初始化一个configuration,设置别名,装入插件,解析mybatis.xml文件(若配置了configLocation)
- 然后创建事物工厂SpringManagedTransactionFactory(也可配置自定义的实现),新建一个Environment对象,并将新建的transactionFactory放入其中
- 解析配置的sql mapper配置文件(若有mapperLocations), 解析sql mapper文件中的映射关系生成MappedStatement对象,并执行 configuration.addMappedStatement(statement), 放入到configuration对象中
可以看到整个过程就是初始化configuration然后创建SqlSessionFactory并交给spring容器
实现了BeanDefinitionRegistryPostProcessor接口的MapperScannerConfigurer
可以想象postProcessBeanDefinitionRegistry方法实现就是创建一个个mapper接口类型的BeanDefinition(实际是MapperFactoryBean),然后注册到spring容器中
根据MapperScannerConfigurer节点配置的basePackage,扫描包下的所有接口,包装成MapperFactoryBean的实现类,注入sqlSessionFactory或sqlSessionTemplate对象; 这样,在spring初始化MapperFactoryBean时,就能返回这些mapper接口的动态代理实现
for (BeanDefinitionHolder holder : beanDefinitions) {
GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();
if (logger.isDebugEnabled()) {
logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getPropertyValues().add("mapperInterface", definition.getBeanClassName());
definition.setBeanClass(MapperFactoryBean.class);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (logger.isDebugEnabled()) {
logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
* 通过反射加载路径下的所有类, 这种反射方式较慢,并且没有得到lazyload的好处
* 加载源文件,分析文件属性,这种方式情况复杂,可能需要词法分析
* spring使用到了第三种方式,通过asm对字节码的处理得到响应的属性
三. Spring事物
MapperFactoryBean的sqlSessionTemplate
sqlSessionTemplate是MapperFactoryBean成员sqlsession动态代理实现,最终会调用到SqlSessionInterceptor
首先执行getSqlSession方法,执行步骤如下:
- 第一次执行将进入到sessionFactory.openSession(executorType)
- 然后进入到mybatis的DefaultSqlSessionFactory.openSessionFromDataSource
- 根据之前初始化configuration时的配置得到SpringManagedTransactionFactory,创建SpringManagedTransaction
- 生成一个Executor,configuration.newExecutor(tx, execType, autoCommit),返回new DefaultSqlSession(configuration, executor),即上一步得到的SpringManagedTransaction作为sqlsession的事物;在Executor执行方法中获取connection将通过SpringManagedTransaction得到
- DataSourceUtils.getConnection(this.dataSource)获取connection,过程和getSqlSession方法类似: 在TransactionSynchronizationManager中查找,有则返回,没有则创建一个(三种方式:unpooleddatasource,pooleddatasource,jndidatasource),并绑定到TransactionSynchronizationManager
获取到SqlSession以后,代理SqlSession接口中的方法,调用closeSqlSession
private class SqlSessionInterceptor implements InvocationHandler {
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);
}
}
}
}
spring的TransactionSynchronizationManager
TransactionSynchronizationManager用于辅助实现spring的事物传播机制,使用ThreadLocal持有每个线程的connection\Transaction等.方法执行时判断当前线程是否已开启事物,然后根据配置的事物传播方式开启事物,挂起事物,或者抛出异常,具体可参考TransactionInterceptor的invoke方法